Compare commits

...

167 Commits

Author SHA1 Message Date
yhirose 2e925abacc Release v0.36.0 2026-03-02 21:53:42 -05:00
yhirose bb22553087 Fix code format 2026-03-02 21:49:10 -05:00
yhirose 8bffbe3ff2 Merge commit from fork 2026-03-02 21:46:31 -05:00
yhirose 0e4f104141 Fix #2383 (#2385) 2026-03-02 21:09:31 -05:00
yhirose 74807d24a7 Update pages-data.json with new content and structure for cpp-httplib documentation 2026-03-02 17:22:52 -05:00
yhirose 2e124cde02 Enhance documentation and configuration for static site generator
- Update README for clarity and quick start instructions
- Refine default config.toml with hostname and base path
- Adjust index.md files for consistent heading levels
- Simplify CSS for code block styling and remove unnecessary theme switching
- Refactor SiteConfig to derive full base URL from hostname and base path
- Update MarkdownRenderer to remove light theme handling
2026-03-02 00:48:14 -05:00
yhirose fdb589d97e Update dependency 2026-03-01 23:55:52 -05:00
yhirose 843ad379c7 Add search functionality across documentation pages
- Implemented a search button in the header of each documentation page.
- Added a search modal that allows users to input search queries.
- Integrated a JavaScript search feature that fetches and displays results from a new `pages-data.json` file.
- Each documentation page now includes a search overlay for improved navigation.
- Updated the main JavaScript file to handle search logic, including result highlighting and navigation.
- Created a `pages-data.json` file containing metadata for all documentation pages to facilitate search functionality.
2026-03-01 23:37:51 -05:00
yhirose cda9680cdc Update document site 2026-03-01 23:12:45 -05:00
yhirose e906c31a79 Add favicon and update navigation icons across documentation
- Added a favicon link to all tour pages in the Japanese documentation.
- Updated navigation links to include SVG icons for Home and GitHub.
- Changed language button to include an SVG icon for better visual representation.
- Improved theme toggle button to use SVG icons for light and dark modes.
- Refactored the documentation build commands in the justfile for clarity and consistency.
2026-03-01 23:04:38 -05:00
yhirose 2d669c3636 Refactor main function to display help message when no command is provided 2026-03-01 23:04:38 -05:00
yhirose b2d76658fc Add default templates, styles, and scripts for documentation site 2026-03-01 23:04:38 -05:00
Charles von Kalm 7444646627 Removed unused local variable (#2382) 2026-03-01 18:02:57 -05:00
Matheus Gabriel Werny 82a61a6b60 [CMake] New compoments MbedTLS and wolfSSL (#2360)
* [CMake] New component MbedTLS

New component MbedTLS.

* Fix case

Fix case: HTTPLIB_REQUIRE_OPENSSL=OFF; HTTPLIB_REQUIRE_MBEDTLS=ON

* [CMake] Test target MbedTLS::tfpsacrypto

[CMake] Test target MbedTLS::tfpsacrypto.

* [CMake] Test MbedTLS::mbedx509

[CMake] Test MbedTLS::mbedx509.

* Revert "[CMake] Test MbedTLS::mbedx509"

This reverts commit 1d0b91f59a.

* Revert "[CMake] Test target MbedTLS::tfpsacrypto"

This reverts commit bf099f6264.

* Fix problem caused by the recent performance improvement

* wolfSSL support

wolfSSL support.
Partly solve https://github.com/yhirose/cpp-httplib/issues/2371. Only
meson is missing.

* Solve https://github.com/yhirose/cpp-httplib/issues/2361

Solve https://github.com/yhirose/cpp-httplib/issues/2361.
Apply `WARNING`.

* Fix variable

Fix variable.

* [CMake] Solve incompatibilities with loop

Solve incompatibilities with loop.

* Fix

Fix.

* Remove debug prints

Remove debug prints.

* [CMake] Fix bug

Prevent a bug aus the required and if available libraries are checked
independently from each other. A could be chosen in required but B could
be chosen in if available and everything would pass.

* Remove debug print

Remove debug print.

* Restore change

Restore change.

---------

Co-authored-by: yhirose <yuji.hirose.bug@gmail.com>
2026-03-01 18:00:36 -05:00
yhirose 63ede29db1 Update README 2026-03-01 17:34:03 -05:00
yhirose ae64a5ee90 Fix #2381 2026-02-28 22:07:44 -05:00
yhirose f441fc33fb Update README with documentation link and formatting
Added a line break and a link to the official documentation.
2026-02-28 21:58:58 -05:00
yhirose a9fc935919 Add a link to GitHub 2026-02-28 21:25:01 -05:00
yhirose b6cd71d4ff Update docs/ 2026-02-28 20:58:42 -05:00
yhirose bda599bfb4 Fix base_dir for GitHub PageS 2026-02-28 20:56:46 -05:00
yhirose 797758a742 Documentation Site on GitHub Pages (#2376)
* Add initial documentations

* Update documentation for Basic Client and add WebSocket section

* feat: add a static site generator with multi-language support

- Introduced a new Rust-based static site generator in the `docs-gen` directory.
- Implemented core functionality for building sites from markdown files, including:
  - Configuration loading from `config.toml`.
  - Markdown rendering with frontmatter support.
  - Navigation generation based on page structure.
  - Static file copying and output directory management.
- Added templates for base layout, pages, and portal.
- Created a CSS file for styling and a JavaScript file for interactive features like language selection and theme toggling.
- Updated documentation source with new configuration and example pages in English and Japanese.
- Added a `justfile` target for building the documentation site.

* Add language/theme toggle functionality

- Created a new Japanese tour index page at docs/ja/tour/index.html
- Implemented navigation links for various sections of the cpp-httplib tutorial
- Added a language selector to switch between English and Japanese
- Introduced theme toggle functionality to switch between light and dark modes
- Added mobile sidebar toggle for better navigation on smaller screens
2026-02-28 14:45:40 -05:00
yhirose 85b18a9c64 Release v0.35.0 2026-02-27 22:14:27 -05:00
yhirose 0853ce7753 Fix #2379 2026-02-27 22:08:27 -05:00
yhirose c99d7472b5 Merge commit from fork 2026-02-27 21:36:04 -05:00
yhirose defd907c74 Merge commit from fork 2026-02-27 21:35:32 -05:00
yhirose c6c75e4c69 Remove tag-latest.yml 2026-02-23 00:01:58 -05:00
yhirose c2002f6e06 Make loading system certificates from the Keychain on macOS an opt-out feature (#2377) 2026-02-22 19:18:40 -05:00
yhirose 7c33fd47bf Release v0.34.0 2026-02-22 17:27:02 -05:00
yhirose 21243b3c9e Fix problem caused by the recent performance improvement 2026-02-22 13:02:40 -05:00
yhirose e068da4f6b Fix race condittion with logging 2026-02-22 08:46:30 -05:00
yhirose f6aec98145 Fix SocketStream.wait_writable_INET test 2026-02-22 08:39:08 -05:00
yhirose f29bb15f9d Performance improvement! 2026-02-22 07:53:23 -05:00
yhirose c53d93d145 Add make_file_body 2026-02-22 07:53:23 -05:00
yhirose b4d16a582d Update latest tag when new release 2026-02-21 23:27:16 -05:00
yhirose e61a8bcec7 Release v0.33.1 2026-02-21 08:55:06 -05:00
yhirose 33dbe00cce Fix compiple problem with C++11 compiler 2026-02-21 08:52:50 -05:00
yhirose f1f8ff53d5 Release v0.33.0 2026-02-21 08:43:49 -05:00
yhirose 9b9bda6b6e Fix CI build errors 2026-02-21 08:14:47 -05:00
yhirose 2280f1d191 Improvement for Multipart Form Data 2026-02-20 23:15:01 -05:00
yhirose 43cf1822c6 Resolve #2369 2026-02-20 16:22:01 -05:00
yhirose 17f301877f Add bench command in justfile 2026-02-20 15:45:04 -05:00
yhirose 4f17fbaa03 Missing change for wolfSSL support 2026-02-20 15:44:31 -05:00
yhirose 0d5bf55c73 Add wolfSSL support (#2370)
* Add wolfSSL support

* Update CI

* Fix build error

* Revert "Fix build error"

This reverts commit d48096277fd53777988d23dfdc53d9ce6bbc334c.

* Fix build errors

* Build errors on ubuntu

* Update README

* Refactoring

* Fix wolfSSL issues
2026-02-20 15:42:45 -05:00
yhirose 718d7d92b9 Fix problems in Unit tests 2026-02-16 06:22:18 -05:00
yhirose c41c5fb8a9 Revise README features section and header
Updated section headers and improved feature list formatting.
2026-02-14 23:22:42 -05:00
yhirose ab96f72b96 Update README 2026-02-14 20:06:01 -05:00
yhirose b2430249d2 Update README-websocket.md and justfile 2026-02-14 17:59:15 -05:00
yhirose 464867a9ce WebSocket and Dynamic Thread Pool support (#2368)
* WebSocket support

* Validate selected subprotocol in WebSocket handshake

* Fix problem with a Unit test

* Dynamic Thread Pool support

* Fix race condition in new Dynamic ThreadPool
2026-02-14 17:44:49 -05:00
Kostia Sokolovskyi d4180e923f Fix comparison of integers of different signs warning when compiling with BoringSSL. (#2367) 2026-02-13 17:25:59 -05:00
Adrien Gallouët 8d225afc8c Remove macOS select() fallback (#2365)
* Remove macOS select() fallback

macOS has supported `poll` for a long time now, so there's no need for
the specific `select` code paths.

With this commit, we can successfully build on visionOS.

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>

* Fix coding style

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>

---------

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>
2026-02-13 13:44:06 -05:00
yhirose ed5c5d325b Parallel test on CI (#2364)
* Parallel test on CI

* Fix problem with Windows

* Use cache for vcpkg

* Parallel 'No Exception' test

* Use one job to run all shards
2026-02-13 01:55:30 -05:00
yhirose c1ee85d89e Use iptables to disable network (#2363)
* Use iptables to disable network

* Fix race condition problem

* Enable network after test finishes
2026-02-12 22:46:26 -05:00
yhirose 14e37bd75b Offline test (Resolve #2356) (#2358)
* Offline test

* Disabled network

* Removed MbedTLS
2026-02-12 16:31:23 -05:00
yhirose c0adbb4b20 Release v0.32.0 2026-02-12 15:24:12 -05:00
yhirose f80864ca03 Resolve #2359 2026-02-11 14:25:27 -10:00
Adrien Gallouët 4e75a84b39 Fix compilation on BoringSSL by replacing ASN1_TIME_to_tm (#2354)
* Fix compilation on BoringSSL by replacing ASN1_TIME_to_tm

BoringSSL doesn't expose `ASN1_TIME_to_tm`.
This patch switches to using `ASN1_TIME_diff` to calculate `time_t`.
This is supported by OpenSSL, LibreSSL, and BoringSSL, and also avoids
the platform-specific `timegm` vs `_mkgmtime` logic.

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>

* Format code

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>

* Use detail::scope_exit

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>

---------

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>
2026-02-11 07:55:07 -10:00
Justin 77d945d3b6 Correct sign comparison error with sk_X509_OBJECT_num (#2355)
* Correct sign comparison error with sk_X509_OBJECT_num

In some build configurations, sk_X509_OBJECT_num (from BoringSSL) returns a size_t. Comparing this directly against a signed int loop counter triggers -Werror,-Wsign-compare.

Instead, move the count into a local int variable so the compiler uses implicit conversion to ensure type consistency during the loop comparison.

* Update httplib.h

* Update httplib.h

Missed a s/int/decltype(count)
2026-02-11 07:54:35 -10:00
yhirose f69737a838 Fix problem with PayloadMaxLengthTest.NoContentLengthPayloadLimit 2026-02-10 23:38:57 -10:00
yhirose a188913b02 Merge branch 'master' of github.com:yhirose/cpp-httplib 2026-02-10 23:23:13 -10:00
yhirose 02e4c53685 Fix 'no TLS' problem with RequestWithoutContentLengthOrTransferEncoding 2026-02-10 23:21:43 -10:00
Alexey Sokolov 8c4370247a Add support for mbedtls to meson (#2350)
Related: #2345
2026-02-10 09:51:37 -10:00
yhirose 1f1a799d13 Fix #2351 2026-02-09 16:43:02 -10:00
yhirose a875292153 Move stream and sse implementations from the decl area to the impl area. (#2352) 2026-02-09 16:41:49 -10:00
yhirose f0b7d4161d Update justfile 2026-02-09 15:12:12 -10:00
yhirose 2867b74f13 Release v0.31.0 2026-02-08 16:04:29 -10:00
yhirose 4e14bc8948 Fix memory leak (#2348)
* Fix memory leak

* Fix flaky errors
2026-02-08 15:49:30 -10:00
yhirose 5d717e6d91 Resolve #2347 2026-02-08 09:40:26 -10:00
yhirose 8b4146324f Fix #2116 (#2346)
* Fix #2116

* Fix problem
2026-02-07 19:26:11 -10:00
yhirose 94b5038eb3 Fix build error on Windows 2026-02-06 22:08:28 -10:00
yhirose 9248ce3bfe Fix problem with PayloadMaxLengthZeroMeansNoLimit 2026-02-06 22:02:22 -10:00
yhirose 4639b696ab Fix #2339 (#2344)
* Fix #2339

* Fix CI errors

* Fix Windows build error

* Fix CI errors on Windows

* Fix payload_max_length initialization in BodyReader

* Initialize payload_max_length with CPPHTTPLIB_PAYLOAD_MAX_LENGTH in BodyReader

* Update README and tests to clarify payload_max_length behavior and add no limit case

* Fix server thread lambda capture in ClientVulnerabilityTest
2026-02-06 19:30:33 -10:00
yhirose 5ead179b8e Update README 2026-02-02 11:32:23 -05:00
Miko 1942e0ef01 Add C++ modules support (#2291)
* Add C++ modules support

* Add module examples

* Missing semicolon

* Update GitHub Actions script and create a modules updating script

* Name the unused param

* Use the guarded/direct export of header approach

* Update CMakeLists.txt

Co-authored-by: Andrea Pappacoda <andrea@pappacoda.it>

* Update CMakeLists.txt

Co-authored-by: Andrea Pappacoda <andrea@pappacoda.it>

* Split scripts into split.py and generate_module.py

---------

Co-authored-by: Andrea Pappacoda <andrea@pappacoda.it>
2026-02-02 11:27:09 -05:00
yhirose 6be32a540d Abstract TLS API support (Resolve #2309) (#2342)
Abstract TLS API support (OpenSSL and MbedTLS backends)
2026-02-01 23:48:03 -05:00
yhirose dc6faf5c17 Release v0.30.2 2026-02-01 19:11:44 -05:00
yhirose 71bb17fb0e Fix problems with CPPHTTPLIB_NO_EXCEPTIONS 2026-02-01 08:43:13 -05:00
yhirose 094bf112bb Fix #2340 2026-01-30 22:01:43 -05:00
ctabor-itracs fbec2a3466 case insensitive hostname validation (fix #2333) (#2337)
* Add Tests for case sensitive hostname verfication

* Modify check_host_name to perform case insensitive comparisons during ssl hostname validation

---------

Co-authored-by: Tabor, Chris <chris.tabor@commscope.com>
2026-01-23 14:58:47 -05:00
Sung Po-Han c3fa06112b Fix set_ca_cert_store() to skip system certs like set_ca_cert_path() (#2335)
Both APIs conceptually do the same thing: "use these CA certs for
verification." However, set_ca_cert_store() falls through to the else
branch in load_certs() where system certs are added to the user's
custom store, defeating the purpose of certificate pinning.

This change makes set_ca_cert_store() behave consistently with
set_ca_cert_path() by checking ca_cert_store_ before loading system
certificates.

Added test to verify system certs are not loaded when custom store is set.

Co-authored-by: Your <you@example.com>
2026-01-22 18:58:25 -05:00
Prajwal B Mehendarkar f73e694f0c timegm api absent in AIX (#2336) 2026-01-22 18:57:23 -05:00
TH 191bfb2ea4 Fix build error when zstd < 1.5.6 lacks zstd::libzstd CMake target (#2334)
Fix #2313
2026-01-22 18:56:34 -05:00
yhirose ad5839f0d1 Add retry logic to BenchmarkTest test on Windows 2026-01-20 18:43:56 -05:00
yhirose 02dfb97fd6 Add Expect: 100-continue support 2026-01-18 22:38:25 -05:00
yhirose a38a076571 Resolve #2262 (#2332)
* Resolve #2262

* Enhance request handling on Windows by adding early response check for large request bodies

* Enhance early response handling for large requests with long URIs on Windows
2026-01-18 00:38:43 -05:00
yhirose 0e1b52b23e Fix #2325 (#2331)
* Fix #2325

* clang-format
2026-01-16 18:19:14 -05:00
yhirose c0469eba96 Revert "Fix #2325"
This reverts commit 7dec57d1eb.
2026-01-16 17:28:28 -05:00
yhirose 7dec57d1eb Fix #2325 2026-01-16 16:25:06 -05:00
yhirose b85aa76bd2 Fix #2321, #2322, #2326 2026-01-16 11:29:09 -05:00
yhirose cea018f2cd Fix #2324 2026-01-11 21:23:15 -05:00
yhirose 1111219f17 Fix #2324 2026-01-10 21:05:30 -05:00
yhirose a7e1d14b15 Fix warning on Windows 2026-01-10 19:23:35 -05:00
yhirose 6eff49e1fb Problem with CI test on Windows without OpenSSL (#2323)
* Fix problem with 'windows without SSL`

* Fix payload limit enforcement for requests without Content-Length on Windows

- Enable MSG_PEEK on Windows (non-SSL builds) to detect payloads without Content-Length
- Only use MSG_PEEK when payload_max_length is set to a finite value to avoid blocking
- Use read_content_without_length for actual size checking to support any payload limit
- Set 413 Payload Too Large status before rejecting oversized requests

This fixes three test cases on Windows:
- RequestWithoutContentLengthOrTransferEncoding (no payload limit)
- NoContentLengthPayloadLimit (8-byte limit)
- NoContentLengthExceeds10MB (10MB limit)

* clang-format
2026-01-10 19:23:24 -05:00
yhirose bd95e67c23 Release v0.30.1 2026-01-09 21:35:03 -05:00
yhirose 2e2e47bab1 Merge commit from fork
* Ensure payload_max_length_ is respected for compressed payloads

* Fix Denial of service (DOS) using zip bomb

---------

Co-authored-by: Hritik Vijay <hey@hritik.sh>
2026-01-09 21:09:07 -05:00
seragh 59905c7f0d Prevent redefinition of ssize_t (#2319)
On Windows cpp-httplib defines ssize_t, therefore applications needing
to define ssize_t for their own needs or are using libraries that do
require a means to avoid a possible incompatible redefinition.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-01-05 20:03:18 -05:00
yhirose 8d03ef1615 Fix #2318 on macOS 2026-01-02 22:28:15 -05:00
yhirose 23a1d79a66 Fix #2318 2026-01-02 20:45:01 -05:00
yhirose 781c55f120 Release v0.30.0 2025-12-31 22:19:05 -05:00
yhirose 40f7985e02 Update copyright year 2025-12-31 22:18:11 -05:00
PerseoGI f85f30a637 Apple frameworks: match CoreFoundation and CFNetwork linkage (#2317)
* Apple frameworks: match linkage with CoreFoundation and CFNetwork with actual code

* Fix appleframeworks in Meson code
2025-12-31 12:49:59 -05:00
yhirose 6da7f0c61c Fix port reuse problem 2025-12-31 00:34:15 -05:00
yhirose 2ba0e7a797 Fix build error 2025-12-30 19:46:21 -05:00
yhirose ded82448aa clang-format 2025-12-30 17:34:52 -05:00
yhirose 98048a033a Merge commit from fork 2025-12-30 17:32:42 -05:00
yhirose 7ae794a6bf Fix #2315 2025-12-26 16:17:43 -05:00
yhirose 385adefb11 Use HOST and PORT in test.cc 2025-12-26 00:24:19 -05:00
yhirose b7c2f04318 Fix potential arithmatic overflow problem 2025-12-25 22:19:37 -05:00
yhirose d23cf77cd0 Resolve #2313 2025-12-23 20:10:59 -05:00
yhirose 5304464a53 Release v0.29.0 2025-12-23 00:03:49 -05:00
yhirose db98efee5a Fix problem with Proxy test 2025-12-22 23:10:24 -05:00
yhirose cdf0d33258 Fix #2301 2025-12-22 22:37:56 -05:00
Aaron Gokaslan 25688258ad Add another missing std::move for _base_dirs vector (#2314) 2025-12-22 20:43:46 -05:00
Aaron Gokaslan f0990ca96d Use std::move for request redirection (#2311)
Prevents an additional copy
2025-12-17 12:04:37 -05:00
Aaron Gokaslan 0461cb770c Avoid unncessary copying of request and response objects (#2310) 2025-12-17 12:04:17 -05:00
yhirose 51b704b902 Implement SSEClient (#2308)
* Implement SSEClient

* Fix Windows problem
2025-12-15 00:00:42 -05:00
yhirose 7eb03e81fc Refactoring 2025-12-14 17:41:11 -05:00
yhirose 6a6d4161d1 Removed DigestAuthTest.FromHTTPWatch_Online_HTTPCan 2025-12-14 14:45:48 -05:00
Copilot 63b07ada43 Initial plan (#2307)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-12-14 13:54:29 -05:00
Jean-Francois Simoneau 2de4c59bc2 Add Zstd support through meson (#2293)
* Add Zstd support through meson

* Add libzstd-dev to abitest
2025-12-13 23:55:58 -05:00
Jean-Francois Simoneau b7097f1386 Replace httpbin.org with httpcan.org (#2300)
* Replace httpbin.org with httpcan.org

* Fix DigestAuthTest.FromHTTPWatch_Online test
2025-12-13 23:52:05 -05:00
Aaron Gokaslan 681d388247 Use move semantics for auth key and value (#2306) 2025-12-13 22:53:10 -05:00
Aaron Gokaslan ae94d64f67 Remove another unnecessary string copy (#2305) 2025-12-13 22:52:26 -05:00
Aaron Gokaslan 3401877d3d Change single char string literals to chars (#2304) 2025-12-13 22:52:12 -05:00
Aaron Gokaslan bce08e62f9 Remove unnecessary copies for AcceptEntry (#2303) 2025-12-13 22:49:58 -05:00
Carter Green f4ecb96e54 Fix linker error on macOS (#2299) 2025-12-11 20:12:31 -05:00
Aaron Gokaslan c23764269d Use std::move for boundary in set_boundary method (#2298) 2025-12-09 22:24:33 -05:00
Aaron Gokaslan f441cd2a44 Use std::move for content_provider in adapter (#2297) 2025-12-09 22:23:45 -05:00
Miko c3613c6977 Update the split.py file (#2295) 2025-12-08 22:18:01 -05:00
yhirose 87c2b4e584 Fix #2294 2025-12-08 19:32:35 -05:00
yhirose c795ad1c32 Fix #2259. Add query string normalization to preserve parameter order in requests 2025-12-05 21:39:40 -05:00
yhirose 3e0fa33559 Implement ETag and Last-Modified support for static file responses and If-Range requests (#2286)
* Fix #2242: Implement ETag and Last-Modified support for static file responses

* Add ETag and Last-Modified handling for If-Range requests

* Enhance HTTP date parsing with improved error handling and locale support

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/test.cc

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Refactor ETag handling: separate strong and weak ETag checks for If-Range requests

* Fix type for mtime in FileStat and improve ETag handling comments

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Resolved code review comments

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Refactor ETag handling: use 'auto' for type inference and improve code readability

* Refactor ETag handling: extract check_if_not_modified and check_if_range methods for improved readability and maintainability

* Code cleanup

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/test.cc

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update httplib.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Enhance ETag handling and validation in httplib.h and add comprehensive tests in test.cc

* Refactor ETag comparison logic and add test for If-None-Match with non-existent file

* Fix #2287

* Code cleanup

* Add tests for extreme date values and negative file modification time in ETag handling

* Update HTTP-date parsing comments to reference RFC 9110

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-05 18:58:54 -05:00
yhirose 27b73f050e Fix #2287 2025-12-05 08:50:12 -05:00
yhirose dbd5ca4bf2 Add error handling for stream read timeouts and connection closures 2025-12-04 19:19:18 -05:00
yhirose 143019a38c Fix #2284 2025-12-02 17:24:27 -05:00
yhirose 1d36013fc3 Update README 2025-12-02 17:08:53 -05:00
yhirose 8bba34eebc Add New Streaming API support (#2281) 2025-12-01 21:40:13 -05:00
Aaron Gokaslan 0a9102ff6b Optimize ThreadPool and MatcherBase constructors (#2283)
Add a missing reserve and missing std::move to each ctor respectively. The latter should really be caught by a clang-tidy perf linter.
2025-11-30 21:49:23 -05:00
bigmoonbit c1fa5e1710 chore: fix some typos in comments (#2282)
Signed-off-by: bigmoonbit <bigmoonbit@outlook.com>
2025-11-27 20:34:18 -05:00
yhirose 84796738fc Fix #2248 2025-11-25 22:22:07 -05:00
yhirose adf58bf474 Release v0.28.0 2025-11-25 21:06:51 -05:00
yhirose 337fbb0793 Fix #2279
Enhance request handling: add support for requests without Content-Length or Transfer-Encoding headers
2025-11-25 20:30:43 -05:00
Copilot 9e7861b0b4 Add #undef _res after including resolv.h to prevent macro conflicts (#2280)
* Initial plan

* Add #undef _res after including resolv.h to prevent macro conflicts

Co-authored-by: yhirose <357397+yhirose@users.noreply.github.com>

* Complete task - added #undef _res after resolv.h include

Co-authored-by: yhirose <357397+yhirose@users.noreply.github.com>

* Remove accidentally committed codeql build artifacts

Co-authored-by: yhirose <357397+yhirose@users.noreply.github.com>

* Add inline comment explaining why #undef _res is necessary

Co-authored-by: yhirose <357397+yhirose@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yhirose <357397+yhirose@users.noreply.github.com>
2025-11-25 20:00:10 -05:00
Clarkok Zhang 27ee115a60 Fix android getaddrinfo issue (#2273)
Co-authored-by: Clarkok Zhang <clarkok8@gmail.com>
2025-11-14 16:48:21 -05:00
Clarkok Zhang 59882752aa Add Client::Post with both content provider and receiver (#2268)
Co-authored-by: Clarkok Zhang <clarkok8@gmail.com>
2025-11-14 14:52:06 -05:00
yhirose 61e9f7ce8f Fix #2270 2025-11-14 14:17:04 -05:00
VZ 1acf18876f CMake: Add HTTPLIB_SHARED option, don't define BUILD_SHARED_LIBS (#2266)
To avoid surprises in the projects consuming the library, don't define
BUILD_SHARED_LIBS option ourselves but just use its value, if provided,
to define HTTPLIB_SHARED option which can be also set directly to
specify whether we should build static or shared library.

Closes #2263.
2025-11-10 22:17:43 -05:00
chansikpark 4b2b851dbb Fix HTTP 414 errors hanging until timeout (#2260)
* Fix HTTP 414 errors hanging until timeout

* All errors (status code 400+) close the connection

* 🧹

---------

Co-authored-by: Wor Ker <worker@factory>
2025-11-02 22:23:42 -05:00
yhirose 551f96d4a2 Remove REMOTE_PORT dependency from UnixSocketTest.PeerPid 2025-10-27 20:40:12 -04:00
yhirose eacc1ca98e Release v0.27.0 2025-10-27 19:57:53 -04:00
yhirose ac9ebb0ee3 Merge commit from fork
* Fix "Untrusted HTTP Header Handling (REMOTE*/LOCAL*)"

* Fix "Untrusted HTTP Header Handling (X-Forwarded-For)"

* Fix security problems in docker/main.cc
2025-10-27 19:54:12 -04:00
yhirose 11eed05ce7 Fix #2255 and #2256 2025-10-27 19:51:55 -04:00
yhirose f3bba0646a Fix benchmark test issue on Windows (#2258) 2025-10-27 18:27:55 -04:00
yhirose 2da189f88c Fix EventDispatcher problem (#2257) 2025-10-27 18:10:52 -04:00
yhirose 6e0f211cff Fix problem with .gitignore for examples 2025-10-27 17:54:27 -04:00
yhirose 318a3fe425 Fix problem with installing OpenSSL for Windows (#2254) 2025-10-25 22:35:21 -04:00
yhirose 2d8d524178 Fix #2251 2025-10-25 22:06:42 -04:00
yhirose afa88dbe70 Fix #2250 2025-10-25 21:36:53 -04:00
yhirose 08133b593b Merge branch 'staticlibs-ssl_error_reporting' 2025-10-25 19:32:31 -04:00
yhirose 8aedbf4547 Add a unit test 2025-10-25 19:31:48 -04:00
yhirose cde29362ef Merge branch 'ssl_error_reporting' of github.com:staticlibs/cpp-httplib into staticlibs-ssl_error_reporting 2025-10-25 19:09:09 -04:00
yhirose bae40fcdf2 Resolve #2237 2025-10-25 16:48:45 -04:00
crueter db561f5552 [cmake] FindBrotli: do not add Brotli:: targets if they already exist (#2249)
Not checking for this is terrible practice.
2025-10-16 09:59:15 -04:00
Andrea Pappacoda 35c52c1ab9 build(meson): use C++17 for gtest >= 1.17.0 (#2241) 2025-09-20 15:06:49 -04:00
Alex Kasko 23ff9a5605 Fix error reporting in SSLClient
When the `SSLClient` is used to connect to a plain-HTTP server (which
can happen in clients due to some end-user misconfiguration) it can
return a failure from the `send()` call without setting the `Error`
reference to the corresponding error code. This can cause problems to
callers, that may expect that, when the check like this is passed on
the response:

```c++
if (res.error() == Error::Success)
```

then they can access the response contents with `res.value()`. When
`SSLClient`'s connection fails - the contents `unique_ptr` is not set
and an attemt to access it causes UB.

This change fixes the `SSLClient::create_and_connect_socket` method
making sure that, the `Error` value is set correctly when the
`is_valid()` check fails.
2025-09-18 13:44:39 +01:00
yhirose 41be1e24e3 Code cleanup 2025-09-15 07:59:53 -04:00
Jonas van den Berg 6e52d0a057 Fix UB by use of dangling references in getaddrinfo_with_timeout (#2232)
* Fix use of dangling references

When the resolve thread is detached, local variables were still used, which could lead to a program crash.

* Add test to verify dangling ref fix

* Add missing brace initialization

* Assert that the remaining fields are really zeroed

* Fix use of chrono literals
2025-09-14 20:05:09 -04:00
apocelipes f72b4582e6 Fix: Fix Windows Cross-Compilation (#2234) 2025-09-14 08:05:51 -04:00
121 changed files with 46036 additions and 14320 deletions
+1
View File
@@ -29,6 +29,7 @@ jobs:
git
libbrotli-dev
libssl-dev
libzstd-dev
meson
pkg-config
python3
+100 -13
View File
@@ -75,21 +75,45 @@ jobs:
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_linux == 'true')
strategy:
matrix:
tls_backend: [openssl, mbedtls, wolfssl]
name: ubuntu (${{ matrix.tls_backend }})
steps:
- name: checkout
uses: actions/checkout@v4
- name: install libraries
- name: install common libraries
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y libc6-dev${{ matrix.config.arch_suffix }} libstdc++-13-dev${{ matrix.config.arch_suffix }} \
libssl-dev${{ matrix.config.arch_suffix }} libcurl4-openssl-dev${{ matrix.config.arch_suffix }} \
zlib1g-dev${{ matrix.config.arch_suffix }} libbrotli-dev${{ matrix.config.arch_suffix }} \
libzstd-dev${{ matrix.config.arch_suffix }}
- name: build and run tests
run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}"
sudo apt-get install -y libcurl4-openssl-dev zlib1g-dev libbrotli-dev libzstd-dev
- name: install OpenSSL
if: matrix.tls_backend == 'openssl'
run: sudo apt-get install -y libssl-dev
- name: install Mbed TLS
if: matrix.tls_backend == 'mbedtls'
run: sudo apt-get install -y libmbedtls-dev
- name: install wolfSSL
if: matrix.tls_backend == 'wolfssl'
run: sudo apt-get install -y libwolfssl-dev
- name: build and run tests (OpenSSL)
if: matrix.tls_backend == 'openssl'
run: cd test && make test_split && make test_openssl_parallel
env:
LSAN_OPTIONS: suppressions=lsan_suppressions.txt
- name: build and run tests (Mbed TLS)
if: matrix.tls_backend == 'mbedtls'
run: cd test && make test_split_mbedtls && make test_mbedtls_parallel
- name: build and run tests (wolfSSL)
if: matrix.tls_backend == 'wolfssl'
run: cd test && make test_split_wolfssl && make test_wolfssl_parallel
- name: run fuzz test target
run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}" fuzz_test
if: matrix.tls_backend == 'openssl'
run: cd test && make fuzz_test
- name: build and run WebSocket heartbeat test
if: matrix.tls_backend == 'openssl'
run: cd test && make test_websocket_heartbeat && ./test_websocket_heartbeat
- name: build and run ThreadPool test
run: cd test && make test_thread_pool && ./test_thread_pool
macos:
runs-on: macos-latest
@@ -98,13 +122,38 @@ jobs:
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_macos == 'true')
strategy:
matrix:
tls_backend: [openssl, mbedtls, wolfssl]
name: macos (${{ matrix.tls_backend }})
steps:
- name: checkout
uses: actions/checkout@v4
- name: build and run tests
run: cd test && make
- name: install Mbed TLS
if: matrix.tls_backend == 'mbedtls'
run: brew install mbedtls@3
- name: install wolfSSL
if: matrix.tls_backend == 'wolfssl'
run: brew install wolfssl
- name: build and run tests (OpenSSL)
if: matrix.tls_backend == 'openssl'
run: cd test && make test_split && make test_openssl_parallel
env:
LSAN_OPTIONS: suppressions=lsan_suppressions.txt
- name: build and run tests (Mbed TLS)
if: matrix.tls_backend == 'mbedtls'
run: cd test && make test_split_mbedtls && make test_mbedtls_parallel
- name: build and run tests (wolfSSL)
if: matrix.tls_backend == 'wolfssl'
run: cd test && make test_split_wolfssl && make test_wolfssl_parallel
- name: run fuzz test target
if: matrix.tls_backend == 'openssl'
run: cd test && make fuzz_test
- name: build and run WebSocket heartbeat test
if: matrix.tls_backend == 'openssl'
run: cd test && make test_websocket_heartbeat && ./test_websocket_heartbeat
- name: build and run ThreadPool test
run: cd test && make test_thread_pool && ./test_thread_pool
windows:
runs-on: windows-latest
@@ -144,11 +193,18 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Setup msbuild on windows
uses: microsoft/setup-msbuild@v2
- name: Cache vcpkg packages
id: vcpkg-cache
uses: actions/cache@v4
with:
path: C:/vcpkg/installed
key: vcpkg-installed-windows-gtest-curl-zlib-brotli-zstd
- name: Install vcpkg dependencies
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
run: vcpkg install gtest curl zlib brotli zstd
- name: Install OpenSSL
if: ${{ matrix.config.with_ssl }}
run: choco install openssl --version 3.5.2 # workaround for chocolatey issue with the latest OpenSSL
run: choco install openssl
- name: Configure CMake ${{ matrix.config.name }}
run: >
cmake -B build -S .
@@ -156,6 +212,7 @@ jobs:
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake
-DHTTPLIB_TEST=ON
-DHTTPLIB_COMPILE=${{ matrix.config.compiled && 'ON' || 'OFF' }}
-DHTTPLIB_USE_OPENSSL_IF_AVAILABLE=${{ matrix.config.with_ssl && 'ON' || 'OFF' }}
-DHTTPLIB_REQUIRE_ZLIB=ON
-DHTTPLIB_REQUIRE_BROTLI=ON
-DHTTPLIB_REQUIRE_ZSTD=ON
@@ -164,7 +221,37 @@ jobs:
run: cmake --build build --config Release -- /v:m /clp:ShowCommandLine
- name: Run tests ${{ matrix.config.name }}
if: ${{ matrix.config.run_tests }}
run: ctest --output-on-failure --test-dir build -C Release
shell: pwsh
working-directory: build/test
run: |
$shards = 4
$procs = @()
for ($i = 0; $i -lt $shards; $i++) {
$log = "shard_${i}.log"
$procs += Start-Process -FilePath ./Release/httplib-test.exe `
-ArgumentList "--gtest_color=yes","--gtest_filter=${{ github.event.inputs.gtest_filter || '*' }}-*BenchmarkTest*" `
-NoNewWindow -PassThru -RedirectStandardOutput $log -RedirectStandardError "${log}.err" `
-Environment @{ GTEST_TOTAL_SHARDS="$shards"; GTEST_SHARD_INDEX="$i" }
}
$procs | Wait-Process
$failed = $false
for ($i = 0; $i -lt $shards; $i++) {
$log = "shard_${i}.log"
if (Select-String -Path $log -Pattern "\[ PASSED \]" -Quiet) {
$passed = (Select-String -Path $log -Pattern "\[ PASSED \]").Line
Write-Host "Shard ${i}: $passed"
} else {
Write-Host "=== Shard $i FAILED ==="
Get-Content $log
if (Test-Path "${log}.err") { Get-Content "${log}.err" }
$failed = $true
}
}
if ($failed) { exit 1 }
Write-Host "All shards passed."
- name: Run benchmark tests with retry ${{ matrix.config.name }}
if: ${{ matrix.config.run_tests }}
run: ctest --output-on-failure --test-dir build -C Release -R "BenchmarkTest" --repeat until-pass:5
env:
VCPKG_ROOT: "C:/vcpkg"
+20
View File
@@ -0,0 +1,20 @@
name: No Exceptions Test
on: [push, pull_request]
jobs:
test-no-exceptions:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev zlib1g-dev libcurl4-openssl-dev libbrotli-dev libzstd-dev
- name: Run tests with CPPHTTPLIB_NO_EXCEPTIONS
run: |
cd test && make test_split EXTRA_CXXFLAGS="-fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS" && make test_openssl_parallel EXTRA_CXXFLAGS="-fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS"
+62
View File
@@ -0,0 +1,62 @@
name: test_offline
on:
push:
pull_request:
workflow_dispatch:
inputs:
test_linux:
description: 'Test on Linux'
type: boolean
default: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
env:
GTEST_FILTER: "-*.*_Online"
jobs:
ubuntu:
runs-on: ubuntu-latest
if: >
(github.event_name == 'push') ||
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_linux == 'true')
strategy:
matrix:
tls_backend: [openssl, no-tls]
name: ubuntu (${{ matrix.tls_backend }})
steps:
- name: checkout
uses: actions/checkout@v4
- name: install common libraries
run: |
sudo apt-get update
sudo apt-get install -y libcurl4-openssl-dev zlib1g-dev libbrotli-dev libzstd-dev
- name: install OpenSSL
if: matrix.tls_backend == 'openssl'
run: sudo apt-get install -y libssl-dev
- name: disable network
run: |
sudo iptables -A OUTPUT -o lo -j ACCEPT
sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A OUTPUT -j REJECT
sudo ip6tables -A OUTPUT -o lo -j ACCEPT
sudo ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A OUTPUT -j REJECT
- name: build and run tests (OpenSSL)
if: matrix.tls_backend == 'openssl'
run: cd test && make test_split && make test_openssl_parallel
env:
LSAN_OPTIONS: suppressions=lsan_suppressions.txt
- name: build and run tests (No TLS)
if: matrix.tls_backend == 'no-tls'
run: cd test && make test_no_tls_parallel
- name: restore network
if: always()
run: |
sudo iptables -F OUTPUT
sudo ip6tables -F OUTPUT
+22 -9
View File
@@ -6,15 +6,28 @@ jobs:
test-proxy:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
strategy:
matrix:
tls_backend: [openssl, mbedtls]
name: proxy (${{ matrix.tls_backend }})
steps:
- uses: actions/checkout@v3
- name: Install dependencies
- uses: actions/checkout@v4
- name: Install common dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev zlib1g-dev libcurl4-openssl-dev libbrotli-dev libzstd-dev netcat-openbsd
- name: Run proxy tests
run: |
cd test && make proxy
sudo apt-get install -y build-essential zlib1g-dev libcurl4-openssl-dev libbrotli-dev libzstd-dev netcat-openbsd
- name: Install OpenSSL
if: matrix.tls_backend == 'openssl'
run: sudo apt-get install -y libssl-dev
- name: Install Mbed TLS
if: matrix.tls_backend == 'mbedtls'
run: sudo apt-get install -y libmbedtls-dev
- name: Run proxy tests (OpenSSL)
if: matrix.tls_backend == 'openssl'
run: cd test && make proxy
- name: Run proxy tests (Mbed TLS)
if: matrix.tls_backend == 'mbedtls'
run: cd test && make proxy_mbedtls
+33 -1
View File
@@ -1,31 +1,63 @@
tags
# Ignore executables (no extension) but not source files
example/server
!example/server.*
example/client
!example/client.*
example/hello
!example/hello.*
example/simplecli
!example/simplecli.*
example/simplesvr
!example/simplesvr.*
example/benchmark
!example/benchmark.*
example/redirect
example/sse*
!example/redirect.*
example/ssecli
!example/ssecli.*
example/ssecli-stream
!example/ssecli-stream.*
example/ssesvr
!example/ssesvr.*
example/upload
!example/upload.*
example/one_time_request
!example/one_time_request.*
example/server_and_client
!example/server_and_client.*
example/accept_header
!example/accept_header.*
example/wsecho
!example/wsecho.*
example/*.pem
test/httplib.cc
test/httplib.h
test/test
test/test_mbedtls
test/test_wolfssl
test/test_no_tls
test/server_fuzzer
test/test_proxy
test/test_proxy_mbedtls
test/test_proxy_wolfssl
test/test_split
test/test_split_mbedtls
test/test_split_wolfssl
test/test_split_no_tls
test/test_websocket_heartbeat
test/test_thread_pool
test/test.xcodeproj/xcuser*
test/test.xcodeproj/*/xcuser*
test/*.o
test/*.pem
test/*.srl
test/*.log
test/_build_*
work/
benchmark/server*
docs-gen/target/
*.swp
+139 -24
View File
@@ -1,25 +1,31 @@
#[[
Build options:
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
* Standard BUILD_SHARED_LIBS is supported and sets HTTPLIB_SHARED default value.
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
* HTTPLIB_USE_WOLFSSL_IF_AVAILABLE (default off)
* HTTPLIB_USE_MBEDTLS_IF_AVAILABLE (default off)
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
* HTTPLIB_USE_ZSTD_IF_AVAILABLE (default on)
* HTTPLIB_BUILD_MODULES (default off)
* HTTPLIB_REQUIRE_OPENSSL (default off)
* HTTPLIB_REQUIRE_WOLFSSL (default off)
* HTTPLIB_REQUIRE_MBEDTLS (default off)
* HTTPLIB_REQUIRE_ZLIB (default off)
* HTTPLIB_REQUIRE_BROTLI (default off)
* HTTPLIB_REQUIRE_ZSTD (default off)
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
* HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES (default off)
* HTTPLIB_USE_NON_BLOCKING_GETADDRINFO (default on)
* HTTPLIB_COMPILE (default off)
* HTTPLIB_INSTALL (default on)
* HTTPLIB_SHARED (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
* HTTPLIB_TEST (default off)
* BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed).
* OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed).
-------------------------------------------------------------------------------
After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli zstd) is available.
After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL wolfssl MbedTLS ZLIB Brotli zstd) is available.
This creates a httplib::httplib target (if found and if listed components are supported).
It can be linked like so:
@@ -46,10 +52,12 @@
These variables are available after you run find_package(httplib)
* HTTPLIB_HEADER_PATH - this is the full path to the installed header (e.g. /usr/include/httplib.h).
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
* HTTPLIB_IS_USING_WOLFSSL - a bool for if wolfSSL support is enabled.
* HTTPLIB_IS_USING_MBEDTLS - a bool for if MbedTLS support is enabled.
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
* HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled.
* HTTPLIB_IS_USING_ZSTD - a bool for if ZSTD support is enabled.
* HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled.
* HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES - a bool for if support of loading system certs from the Apple Keychain is enabled.
* HTTPLIB_IS_USING_NON_BLOCKING_GETADDRINFO - a bool for if nonblocking getaddrinfo is enabled.
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
@@ -93,10 +101,14 @@ set(_HTTPLIB_OPENSSL_MIN_VER "3.0.0")
option(HTTPLIB_NO_EXCEPTIONS "Disable the use of C++ exceptions" OFF)
# Allow for a build to require OpenSSL to pass, instead of just being optional
option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF)
option(HTTPLIB_REQUIRE_WOLFSSL "Requires wolfSSL to be found & linked, or fails build." OFF)
option(HTTPLIB_REQUIRE_MBEDTLS "Requires MbedTLS to be found & linked, or fails build." OFF)
option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
# Allow for a build to casually enable OpenSSL/ZLIB support, but silently continue if not found.
# Make these options so their automatic use can be specifically disabled (as needed)
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
option(HTTPLIB_USE_WOLFSSL_IF_AVAILABLE "Uses wolfSSL (if available) to enable HTTPS support." OFF)
option(HTTPLIB_USE_MBEDTLS_IF_AVAILABLE "Uses MbedTLS (if available) to enable HTTPS support." OFF)
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON)
# Lets you compile the program as a regular library instead of header-only
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
@@ -105,20 +117,61 @@ option(HTTPLIB_INSTALL "Enables the installation target" ON)
option(HTTPLIB_TEST "Enables testing and builds tests" OFF)
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF)
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON)
option(HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES "Disable loading system certs from the Apple Keychain on macOS." OFF)
option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON)
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
# Defaults to static library
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
if(BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
# Necessary for Windows if building shared libs
# See https://stackoverflow.com/a/40743080
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# C++20 modules support requires CMake 3.28 or later
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
option(HTTPLIB_BUILD_MODULES "Build httplib modules (requires HTTPLIB_COMPILE to be ON)." OFF)
else()
set(HTTPLIB_BUILD_MODULES OFF CACHE INTERNAL "Build httplib modules disabled (requires CMake 3.28+)" FORCE)
if(DEFINED CACHE{HTTPLIB_BUILD_MODULES} AND HTTPLIB_BUILD_MODULES)
message(WARNING "HTTPLIB_BUILD_MODULES requires CMake 3.28 or later. Current version is ${CMAKE_VERSION}. Modules support has been disabled.")
endif()
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows" AND ${CMAKE_SYSTEM_VERSION} VERSION_LESS "10.0.0")
message(SEND_ERROR "Windows ${CMAKE_SYSTEM_VERSION} or lower is not supported. Please use Windows 10 or later.")
# Incompatibility between TLS libraries
set(TLS_LIBRARIES_USED_TMP 0)
foreach(tls_library OPENSSL WOLFSSL MBEDTLS)
set(TLS_REQUIRED ${HTTPLIB_REQUIRE_${tls_library}})
set(TLS_IF_AVAILABLE ${HTTPLIB_USE_${tls_library}_IF_AVAILABLE})
if(TLS_REQUIRED OR TLS_IF_AVAILABLE)
math(EXPR TLS_LIBRARIES_USED_TMP "${TLS_LIBRARIES_USED_TMP} + 1")
endif()
endforeach()
if(TLS_LIBRARIES_USED_TMP GREATER 1)
message(FATAL_ERROR "TLS libraries are mutually exclusive.")
endif()
# Defaults to static library but respects standard BUILD_SHARED_LIBS if set
include(CMakeDependentOption)
cmake_dependent_option(HTTPLIB_SHARED "Build the library as a shared library instead of static. Has no effect if using header-only."
"${BUILD_SHARED_LIBS}" HTTPLIB_COMPILE OFF
)
if(HTTPLIB_SHARED)
set(HTTPLIB_LIB_TYPE SHARED)
if(WIN32)
# Necessary for Windows if building shared libs
# See https://stackoverflow.com/a/40743080
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
else()
set(HTTPLIB_LIB_TYPE STATIC)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(CMAKE_SYSTEM_VERSION)
if(${CMAKE_SYSTEM_VERSION} VERSION_LESS "10.0.0")
message(WARNING "Windows ${CMAKE_SYSTEM_VERSION} or lower is not supported. Please use Windows 10 or later.")
endif()
else()
set(CMAKE_SYSTEM_VERSION "10.0.19041.0")
message(WARNING "The target is Windows but CMAKE_SYSTEM_VERSION is not set, the default system version is set to Windows 10.")
endif()
endif()
if(CMAKE_SIZEOF_VOID_P LESS 8)
message(WARNING "Pointer size ${CMAKE_SIZEOF_VOID_P} is not supported. Please use a 64-bit compiler.")
@@ -126,7 +179,10 @@ endif()
# Set some variables that are used in-tree and while building based on our options
set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE})
set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN ${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN})
set(HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES TRUE)
if(HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES)
set(HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES FALSE)
endif()
set(HTTPLIB_IS_USING_NON_BLOCKING_GETADDRINFO ${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO})
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
@@ -147,6 +203,22 @@ elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
endif()
endif()
if(HTTPLIB_REQUIRE_WOLFSSL)
find_package(wolfssl REQUIRED)
set(HTTPLIB_IS_USING_WOLFSSL TRUE)
elseif(HTTPLIB_USE_WOLFSSL_IF_AVAILABLE)
find_package(wolfssl QUIET)
set(HTTPLIB_IS_USING_WOLFSSL ${wolfssl_FOUND})
endif()
if(HTTPLIB_REQUIRE_MBEDTLS)
find_package(MbedTLS REQUIRED)
set(HTTPLIB_IS_USING_MBEDTLS TRUE)
elseif(HTTPLIB_USE_MBEDTLS_IF_AVAILABLE)
find_package(MbedTLS QUIET)
set(HTTPLIB_IS_USING_MBEDTLS ${MbedTLS_FOUND})
endif()
if(HTTPLIB_REQUIRE_ZLIB)
find_package(ZLIB REQUIRED)
set(HTTPLIB_IS_USING_ZLIB TRUE)
@@ -169,8 +241,11 @@ elseif(HTTPLIB_USE_BROTLI_IF_AVAILABLE)
set(HTTPLIB_IS_USING_BROTLI ${Brotli_FOUND})
endif()
# NOTE:
# zstd < 1.5.6 does not provide the CMake imported target `zstd::libzstd`.
# Older versions must be consumed via their pkg-config file.
if(HTTPLIB_REQUIRE_ZSTD)
find_package(zstd)
find_package(zstd 1.5.6 CONFIG)
if(NOT zstd_FOUND)
find_package(PkgConfig REQUIRED)
pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)
@@ -178,7 +253,7 @@ if(HTTPLIB_REQUIRE_ZSTD)
endif()
set(HTTPLIB_IS_USING_ZSTD TRUE)
elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE)
find_package(zstd QUIET)
find_package(zstd 1.5.6 CONFIG QUIET)
if(NOT zstd_FOUND)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
@@ -221,15 +296,37 @@ if(HTTPLIB_COMPILE)
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
endif()
# If building modules, also generate the module file
if(HTTPLIB_BUILD_MODULES)
# Put the generate_module script into the build dir
configure_file(generate_module.py "${CMAKE_CURRENT_BINARY_DIR}/generate_module.py"
COPYONLY
)
# Generate the module file
execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/generate_module.py"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ERROR_VARIABLE _httplib_module_error
)
if(_httplib_module_error)
message(FATAL_ERROR "Failed when trying to generate cpp-httplib module with the Python script.\n${_httplib_module_error}")
endif()
endif()
# split.py puts output in "out"
set(_httplib_build_includedir "${CMAKE_CURRENT_BINARY_DIR}/out")
# This will automatically be either static or shared based on the value of BUILD_SHARED_LIBS
add_library(${PROJECT_NAME} "${_httplib_build_includedir}/httplib.cc")
add_library(${PROJECT_NAME} ${HTTPLIB_LIB_TYPE} "${_httplib_build_includedir}/httplib.cc")
target_sources(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
)
# Add C++20 module support if requested
# Include from separate file to prevent parse errors on older CMake versions
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
include(cmake/modules.cmake)
endif()
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
@@ -246,22 +343,28 @@ endif()
# Only useful if building in-tree, versus using it from an installation.
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
# Require C++11
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
# Require C++11, or C++20 if modules are enabled
if(HTTPLIB_BUILD_MODULES)
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_20)
else()
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
endif()
target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
$<BUILD_INTERFACE:${_httplib_build_includedir}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
# Always require threads
target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
# Always require threads
Threads::Threads
# Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered.
$<$<PLATFORM_ID:Windows>:ws2_32>
$<$<PLATFORM_ID:Windows>:crypt32>
# Needed for API from MacOS Security framework
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}>>:-framework CoreFoundation -framework Security>"
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_MACOSX_AUTOMATIC_ROOT_CERTIFICATES}>>:-framework CFNetwork -framework CoreFoundation -framework Security>"
# Needed for non-blocking getaddrinfo on MacOS
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}>>:-framework CFNetwork -framework CoreFoundation>"
# Can't put multiple targets in a single generator expression or it bugs out.
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
@@ -270,6 +373,8 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<BOOL:${HTTPLIB_IS_USING_ZSTD}>:zstd::libzstd>
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::SSL>
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::Crypto>
$<$<BOOL:${HTTPLIB_IS_USING_WOLFSSL}>:wolfssl::wolfssl>
$<$<BOOL:${HTTPLIB_IS_USING_MBEDTLS}>:MbedTLS::mbedtls>
)
# Set the definitions to enable optional features
@@ -279,7 +384,9 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_ZSTD}>:CPPHTTPLIB_ZSTD_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
$<$<BOOL:${HTTPLIB_IS_USING_WOLFSSL}>:CPPHTTPLIB_WOLFSSL_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_MBEDTLS}>:CPPHTTPLIB_MBEDTLS_SUPPORT>
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES}>>:CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES>
$<$<BOOL:${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}>:CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO>
)
@@ -317,7 +424,11 @@ if(HTTPLIB_INSTALL)
# Creates the export httplibTargets.cmake
# This is strictly what holds compilation requirements
# and linkage information (doesn't find deps though).
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
if(HTTPLIB_BUILD_MODULES)
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets FILE_SET CXX_MODULES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/httplib/modules CXX_MODULES_BMI DESTINATION ${CMAKE_INSTALL_LIBDIR}/httplib/modules)
else()
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
endif()
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
@@ -346,6 +457,10 @@ if(HTTPLIB_INSTALL)
include(CPack)
endif()
if(HTTPLIB_BUILD_MODULES AND NOT HTTPLIB_COMPILE)
message(FATAL_ERROR "HTTPLIB_BUILD_MODULES requires HTTPLIB_COMPILE to be ON.")
endif()
if(HTTPLIB_TEST)
include(CTest)
add_subdirectory(test)
+182
View File
@@ -0,0 +1,182 @@
# SSEClient - Server-Sent Events Client
A simple, EventSource-like SSE client for C++11.
## Features
- **Auto-reconnect**: Automatically reconnects on connection loss
- **Last-Event-ID**: Sends last received ID on reconnect for resumption
- **retry field**: Respects server's reconnect interval
- **Event types**: Supports custom event types via `on_event()`
- **Async support**: Run in background thread with `start_async()`
- **C++11 compatible**: No C++14/17/20 features required
## Quick Start
```cpp
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << "Event: " << msg.event << std::endl;
std::cout << "Data: " << msg.data << std::endl;
});
sse.start(); // Blocking, with auto-reconnect
```
## API Reference
### SSEMessage
```cpp
struct SSEMessage {
std::string event; // Event type (default: "message")
std::string data; // Event payload
std::string id; // Event ID
};
```
### SSEClient
#### Constructor
```cpp
// Basic
SSEClient(Client &client, const std::string &path);
// With custom headers
SSEClient(Client &client, const std::string &path, const Headers &headers);
```
#### Event Handlers
```cpp
// Called for all events (or events without a specific handler)
sse.on_message([](const SSEMessage &msg) { });
// Called for specific event types
sse.on_event("update", [](const SSEMessage &msg) { });
sse.on_event("delete", [](const SSEMessage &msg) { });
// Called when connection is established
sse.on_open([]() { });
// Called on connection errors
sse.on_error([](httplib::Error err) { });
```
#### Configuration
```cpp
// Set reconnect interval (default: 3000ms)
sse.set_reconnect_interval(5000);
// Set max reconnect attempts (default: 0 = unlimited)
sse.set_max_reconnect_attempts(10);
```
#### Control
```cpp
// Blocking start with auto-reconnect
sse.start();
// Non-blocking start (runs in background thread)
sse.start_async();
// Stop the client (thread-safe)
sse.stop();
```
#### State
```cpp
bool connected = sse.is_connected();
const std::string &id = sse.last_event_id();
```
## Examples
### Basic Usage
```cpp
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << msg.data << std::endl;
});
sse.start();
```
### With Custom Event Types
```cpp
httplib::sse::SSEClient sse(cli, "/events");
sse.on_event("notification", [](const httplib::sse::SSEMessage &msg) {
std::cout << "Notification: " << msg.data << std::endl;
});
sse.on_event("update", [](const httplib::sse::SSEMessage &msg) {
std::cout << "Update: " << msg.data << std::endl;
});
sse.start();
```
### Async with Stop
```cpp
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << msg.data << std::endl;
});
sse.start_async(); // Returns immediately
// ... do other work ...
sse.stop(); // Stop when done
```
### With Custom Headers (e.g., Authentication)
```cpp
httplib::Headers headers = {
{"Authorization", "Bearer token123"}
};
httplib::sse::SSEClient sse(cli, "/events", headers);
sse.start();
```
### Error Handling
```cpp
sse.on_error([](httplib::Error err) {
std::cerr << "Error: " << httplib::to_string(err) << std::endl;
});
sse.set_reconnect_interval(1000);
sse.set_max_reconnect_attempts(5);
sse.start();
```
## SSE Protocol
The client parses SSE format according to the [W3C specification](https://html.spec.whatwg.org/multipage/server-sent-events.html):
```
event: custom-type
id: 123
data: {"message": "hello"}
data: simple message
: this is a comment (ignored)
```
+317
View File
@@ -0,0 +1,317 @@
# cpp-httplib Streaming API
This document describes the streaming extensions for cpp-httplib, providing an iterator-style API for handling HTTP responses incrementally with **true socket-level streaming**.
> **Important Notes**:
>
> - **No Keep-Alive**: Each `stream::Get()` call uses a dedicated connection that is closed after the response is fully read. For connection reuse, use `Client::Get()`.
> - **Single iteration only**: The `next()` method can only iterate through the body once.
> - **Result is not thread-safe**: While `stream::Get()` can be called from multiple threads simultaneously, the returned `stream::Result` must be used from a single thread only.
## Overview
The streaming API allows you to process HTTP response bodies chunk by chunk using an iterator-style pattern. Data is read directly from the network socket, enabling low-memory processing of large responses. This is particularly useful for:
- **LLM/AI streaming responses** (e.g., ChatGPT, Claude, Ollama)
- **Server-Sent Events (SSE)**
- **Large file downloads** with progress tracking
- **Reverse proxy implementations**
## Quick Start
```cpp
#include "httplib.h"
int main() {
httplib::Client cli("http://localhost:8080");
// Get streaming response
auto result = httplib::stream::Get(cli, "/stream");
if (result) {
// Process response body in chunks
while (result.next()) {
std::cout.write(result.data(), result.size());
}
}
return 0;
}
```
## API Layers
cpp-httplib provides multiple API layers for different use cases:
```text
┌─────────────────────────────────────────────┐
│ SSEClient (planned) │ ← SSE-specific, parsed events
│ - on_message(), on_event() │
│ - Auto-reconnect, Last-Event-ID │
├─────────────────────────────────────────────┤
│ stream::Get() / stream::Result │ ← Iterator-based streaming
│ - while (result.next()) { ... } │
├─────────────────────────────────────────────┤
│ open_stream() / StreamHandle │ ← General-purpose streaming
│ - handle.read(buf, len) │
├─────────────────────────────────────────────┤
│ Client::Get() │ ← Traditional, full buffering
└─────────────────────────────────────────────┘
```
| Use Case | Recommended API |
|----------|----------------|
| SSE with auto-reconnect | SSEClient (planned) or `ssecli-stream.cc` example |
| LLM streaming (JSON Lines) | `stream::Get()` |
| Large file download | `stream::Get()` or `open_stream()` |
| Reverse proxy | `open_stream()` |
| Small responses with Keep-Alive | `Client::Get()` |
## API Reference
### Low-Level API: `StreamHandle`
The `StreamHandle` struct provides direct control over streaming responses. It takes ownership of the socket connection and reads data directly from the network.
> **Note:** When using `open_stream()`, the connection is dedicated to streaming and **Keep-Alive is not supported**. For Keep-Alive connections, use `client.Get()` instead.
```cpp
// Open a stream (takes ownership of socket)
httplib::Client cli("http://localhost:8080");
auto handle = cli.open_stream("GET", "/path");
// Check validity
if (handle.is_valid()) {
// Access response headers immediately
int status = handle.response->status;
auto content_type = handle.response->get_header_value("Content-Type");
// Read body incrementally
char buf[4096];
ssize_t n;
while ((n = handle.read(buf, sizeof(buf))) > 0) {
process(buf, n);
}
}
```
#### StreamHandle Members
| Member | Type | Description |
|--------|------|-------------|
| `response` | `std::unique_ptr<Response>` | HTTP response with headers |
| `error` | `Error` | Error code if request failed |
| `is_valid()` | `bool` | Returns true if response is valid |
| `read(buf, len)` | `ssize_t` | Read up to `len` bytes directly from socket |
| `get_read_error()` | `Error` | Get the last read error |
| `has_read_error()` | `bool` | Check if a read error occurred |
### High-Level API: `stream::Get()` and `stream::Result`
The `httplib.h` header provides a more ergonomic iterator-style API.
```cpp
#include "httplib.h"
httplib::Client cli("http://localhost:8080");
cli.set_follow_location(true);
...
// Simple GET
auto result = httplib::stream::Get(cli, "/path");
// GET with custom headers
httplib::Headers headers = {{"Authorization", "Bearer token"}};
auto result = httplib::stream::Get(cli, "/path", headers);
// Process the response
if (result) {
while (result.next()) {
process(result.data(), result.size());
}
}
// Or read entire body at once
auto result2 = httplib::stream::Get(cli, "/path");
if (result2) {
std::string body = result2.read_all();
}
```
#### stream::Result Members
| Member | Type | Description |
|--------|------|-------------|
| `operator bool()` | `bool` | Returns true if response is valid |
| `is_valid()` | `bool` | Same as `operator bool()` |
| `status()` | `int` | HTTP status code |
| `headers()` | `const Headers&` | Response headers |
| `get_header_value(key, def)` | `std::string` | Get header value (with optional default) |
| `has_header(key)` | `bool` | Check if header exists |
| `next()` | `bool` | Read next chunk, returns false when done |
| `data()` | `const char*` | Pointer to current chunk data |
| `size()` | `size_t` | Size of current chunk |
| `read_all()` | `std::string` | Read entire remaining body into string |
| `error()` | `Error` | Get the connection/request error |
| `read_error()` | `Error` | Get the last read error |
| `has_read_error()` | `bool` | Check if a read error occurred |
## Usage Examples
### Example 1: SSE (Server-Sent Events) Client
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("http://localhost:1234");
auto result = httplib::stream::Get(cli, "/events");
if (!result) { return 1; }
while (result.next()) {
std::cout.write(result.data(), result.size());
std::cout.flush();
}
return 0;
}
```
For a complete SSE client with auto-reconnection and event parsing, see `example/ssecli-stream.cc`.
### Example 2: LLM Streaming Response
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("http://localhost:11434"); // Ollama
auto result = httplib::stream::Get(cli, "/api/generate");
if (result && result.status() == 200) {
while (result.next()) {
std::cout.write(result.data(), result.size());
std::cout.flush();
}
}
// Check for connection errors
if (result.read_error() != httplib::Error::Success) {
std::cerr << "Connection lost\n";
}
return 0;
}
```
### Example 3: Large File Download with Progress
```cpp
#include "httplib.h"
#include <fstream>
#include <iostream>
int main() {
httplib::Client cli("http://example.com");
auto result = httplib::stream::Get(cli, "/large-file.zip");
if (!result || result.status() != 200) {
std::cerr << "Download failed\n";
return 1;
}
std::ofstream file("download.zip", std::ios::binary);
size_t total = 0;
while (result.next()) {
file.write(result.data(), result.size());
total += result.size();
std::cout << "\rDownloaded: " << (total / 1024) << " KB" << std::flush;
}
std::cout << "\nComplete!\n";
return 0;
}
```
### Example 4: Reverse Proxy Streaming
```cpp
#include "httplib.h"
httplib::Server svr;
svr.Get("/proxy/(.*)", [](const httplib::Request& req, httplib::Response& res) {
httplib::Client upstream("http://backend:8080");
auto handle = upstream.open_stream("/" + req.matches[1].str());
if (!handle.is_valid()) {
res.status = 502;
return;
}
res.status = handle.response->status;
res.set_chunked_content_provider(
handle.response->get_header_value("Content-Type"),
[handle = std::move(handle)](size_t, httplib::DataSink& sink) mutable {
char buf[8192];
auto n = handle.read(buf, sizeof(buf));
if (n > 0) {
sink.write(buf, static_cast<size_t>(n));
return true;
}
sink.done();
return true;
}
);
});
svr.listen("0.0.0.0", 3000);
```
## Comparison with Existing APIs
| Feature | `Client::Get()` | `open_stream()` | `stream::Get()` |
|---------|----------------|-----------------|----------------|
| Headers available | After complete | Immediately | Immediately |
| Body reading | All at once | Direct from socket | Iterator-based |
| Memory usage | Full body in RAM | Minimal (controlled) | Minimal (controlled) |
| Keep-Alive support | ✅ Yes | ❌ No | ❌ No |
| Compression | Auto-handled | Auto-handled | Auto-handled |
| Best for | Small responses, Keep-Alive | Low-level streaming | Easy streaming |
## Features
- **True socket-level streaming**: Data is read directly from the network socket
- **Low memory footprint**: Only the current chunk is held in memory
- **Compression support**: Automatic decompression for gzip, brotli, and zstd
- **Chunked transfer**: Full support for chunked transfer encoding
- **SSL/TLS support**: Works with HTTPS connections
## Important Notes
### Keep-Alive Behavior
The streaming API (`stream::Get()` / `open_stream()`) takes ownership of the socket connection for the duration of the stream. This means:
- **Keep-Alive is not supported** for streaming connections
- The socket is closed when `StreamHandle` is destroyed
- For Keep-Alive scenarios, use the standard `client.Get()` API instead
```cpp
// Use for streaming (no Keep-Alive)
auto result = httplib::stream::Get(cli, "/large-stream");
while (result.next()) { /* ... */ }
// Use for Keep-Alive connections
auto res = cli.Get("/api/data"); // Connection can be reused
```
## Related
- [Issue #2269](https://github.com/yhirose/cpp-httplib/issues/2269) - Original feature request
- [example/ssecli-stream.cc](./example/ssecli-stream.cc) - SSE client with auto-reconnection
+391
View File
@@ -0,0 +1,391 @@
# WebSocket - RFC 6455 WebSocket Support
A simple, blocking WebSocket implementation for C++11.
> [!IMPORTANT]
> This is a blocking I/O WebSocket implementation using a thread-per-connection model. If you need high-concurrency WebSocket support with non-blocking/async I/O (e.g., thousands of simultaneous connections), this is not the one that you want.
## Features
- **RFC 6455 compliant**: Full WebSocket protocol support
- **Server and Client**: Both sides included
- **SSL/TLS support**: `wss://` scheme for secure connections
- **Text and Binary**: Both message types supported
- **Automatic heartbeat**: Periodic Ping/Pong keeps connections alive
- **Subprotocol negotiation**: `Sec-WebSocket-Protocol` support for GraphQL, MQTT, etc.
## Quick Start
### Server
```cpp
httplib::Server svr;
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send("echo: " + msg);
}
});
svr.listen("localhost", 8080);
```
### Client
```cpp
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws");
if (ws.connect()) {
ws.send("hello");
std::string msg;
if (ws.read(msg)) {
std::cout << msg << std::endl; // "echo: hello"
}
ws.close();
}
```
## API Reference
### ReadResult
```cpp
enum ReadResult : int {
Fail = 0, // Connection closed or error
Text = 1, // UTF-8 text message
Binary = 2, // Binary message
};
```
Returned by `read()`. Since `Fail` is `0`, the result works naturally in boolean contexts — `while (ws.read(msg))` continues until the connection closes. When you need to distinguish text from binary, check the return value directly.
### CloseStatus
```cpp
enum class CloseStatus : uint16_t {
Normal = 1000,
GoingAway = 1001,
ProtocolError = 1002,
UnsupportedData = 1003,
NoStatus = 1005,
Abnormal = 1006,
InvalidPayload = 1007,
PolicyViolation = 1008,
MessageTooBig = 1009,
MandatoryExtension = 1010,
InternalError = 1011,
};
```
### Server Registration
```cpp
// Basic handler
Server &WebSocket(const std::string &pattern, WebSocketHandler handler);
// With subprotocol negotiation
Server &WebSocket(const std::string &pattern, WebSocketHandler handler,
SubProtocolSelector sub_protocol_selector);
```
**Type aliases:**
```cpp
using WebSocketHandler =
std::function<void(const Request &, ws::WebSocket &)>;
using SubProtocolSelector =
std::function<std::string(const std::vector<std::string> &protocols)>;
```
The `SubProtocolSelector` receives the list of subprotocols proposed by the client (from the `Sec-WebSocket-Protocol` header) and returns the selected one. Return an empty string to decline all proposed subprotocols.
### WebSocket (Server-side)
Passed to the handler registered with `Server::WebSocket()`. The handler runs in a dedicated thread per connection.
```cpp
// Read next message (blocks until received, returns Fail/Text/Binary)
ReadResult read(std::string &msg);
// Send messages
bool send(const std::string &data); // Text
bool send(const char *data, size_t len); // Binary
// Close the connection
void close(CloseStatus status = CloseStatus::Normal,
const std::string &reason = "");
// Access the original HTTP upgrade request
const Request &request() const;
// Check if the connection is still open
bool is_open() const;
```
### WebSocketClient
```cpp
// Constructor - accepts ws:// or wss:// URL
explicit WebSocketClient(const std::string &scheme_host_port_path,
const Headers &headers = {});
// Check if the URL was parsed successfully
bool is_valid() const;
// Connect (performs HTTP upgrade handshake)
bool connect();
// Get the subprotocol selected by the server (empty if none)
const std::string &subprotocol() const;
// Read/Send/Close (same as server-side WebSocket)
ReadResult read(std::string &msg);
bool send(const std::string &data);
bool send(const char *data, size_t len);
void close(CloseStatus status = CloseStatus::Normal,
const std::string &reason = "");
bool is_open() const;
// Timeouts
void set_read_timeout(time_t sec, time_t usec = 0);
void set_write_timeout(time_t sec, time_t usec = 0);
// SSL configuration (wss:// only, requires CPPHTTPLIB_OPENSSL_SUPPORT)
void set_ca_cert_path(const std::string &path);
void set_ca_cert_store(tls::ca_store_t store);
void enable_server_certificate_verification(bool enabled);
```
## Examples
### Echo Server with Connection Logging
```cpp
httplib::Server svr;
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
std::cout << "Connected from " << req.remote_addr << std::endl;
std::string msg;
while (ws.read(msg)) {
ws.send("echo: " + msg);
}
std::cout << "Disconnected" << std::endl;
});
svr.listen("localhost", 8080);
```
### Client: Continuous Read Loop
```cpp
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws");
if (ws.connect()) {
ws.send("hello");
ws.send("world");
std::string msg;
while (ws.read(msg)) { // blocks until a message arrives
std::cout << msg << std::endl; // "echo: hello", "echo: world"
}
// read() returns false when the server closes the connection
}
```
### Text and Binary Messages
Check the `ReadResult` return value to distinguish between text and binary:
```cpp
// Server
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
std::string msg;
httplib::ws::ReadResult ret;
while ((ret = ws.read(msg))) {
if (ret == httplib::ws::Text) {
ws.send("echo: " + msg);
} else {
ws.send(msg.data(), msg.size()); // Binary echo
}
}
});
// Client
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws");
if (ws.connect()) {
// Send binary data
const char binary[] = {0x00, 0x01, 0x02, 0x03};
ws.send(binary, sizeof(binary));
// Receive and check the type
std::string msg;
if (ws.read(msg) == httplib::ws::Binary) {
// Process binary data in msg
}
ws.close();
}
```
### SSL Client
```cpp
httplib::ws::WebSocketClient ws("wss://echo.example.com/ws");
if (ws.connect()) {
ws.send("hello over TLS");
std::string msg;
if (ws.read(msg)) {
std::cout << msg << std::endl;
}
ws.close();
}
```
### Close with Status
```cpp
// Client-side: close with a specific status code and reason
ws.close(httplib::ws::CloseStatus::GoingAway, "shutting down");
// Server-side: close with a policy violation status
ws.close(httplib::ws::CloseStatus::PolicyViolation, "forbidden");
```
### Accessing the Upgrade Request
```cpp
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
// Access headers from the original HTTP upgrade request
auto auth = req.get_header_value("Authorization");
if (auth.empty()) {
ws.close(httplib::ws::CloseStatus::PolicyViolation, "unauthorized");
return;
}
std::string msg;
while (ws.read(msg)) {
ws.send("echo: " + msg);
}
});
```
### Custom Headers and Timeouts
```cpp
httplib::Headers headers = {
{"Authorization", "Bearer token123"}
};
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws", headers);
ws.set_read_timeout(30, 0); // 30 seconds
ws.set_write_timeout(10, 0); // 10 seconds
if (ws.connect()) {
std::string msg;
while (ws.read(msg)) {
std::cout << msg << std::endl;
}
}
```
### Subprotocol Negotiation
The server can negotiate a subprotocol with the client using `Sec-WebSocket-Protocol`. This is required for protocols like GraphQL over WebSocket (`graphql-ws`) and MQTT.
```cpp
// Server: register a handler with a subprotocol selector
svr.WebSocket(
"/ws",
[](const httplib::Request &req, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send("echo: " + msg);
}
},
[](const std::vector<std::string> &protocols) -> std::string {
// The client proposed a list of subprotocols; pick one
for (const auto &p : protocols) {
if (p == "graphql-ws" || p == "graphql-transport-ws") {
return p;
}
}
return ""; // Decline all
});
// Client: propose subprotocols via Sec-WebSocket-Protocol header
httplib::Headers headers = {
{"Sec-WebSocket-Protocol", "graphql-ws, graphql-transport-ws"}
};
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws", headers);
if (ws.connect()) {
// Check which subprotocol the server selected
std::cout << "Subprotocol: " << ws.subprotocol() << std::endl;
// => "graphql-ws"
ws.close();
}
```
### SSL Client with Certificate Configuration
```cpp
httplib::ws::WebSocketClient ws("wss://example.com/ws");
ws.set_ca_cert_path("/path/to/ca-bundle.crt");
ws.enable_server_certificate_verification(true);
if (ws.connect()) {
ws.send("secure message");
ws.close();
}
```
## Configuration
| Macro | Default | Description |
|---------------------------------------------|-------------------|----------------------------------------------------------|
| `CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH` | `16777216` (16MB) | Maximum payload size per message |
| `CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND` | `300` | Read timeout for WebSocket connections (seconds) |
| `CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND` | `5` | Timeout for waiting peer's Close response (seconds) |
| `CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND` | `30` | Automatic Ping interval for heartbeat (seconds) |
## Threading Model
WebSocket connections share the same thread pool as HTTP requests. Each WebSocket connection occupies one thread for its entire lifetime.
The default thread pool uses dynamic scaling: it maintains a base thread count of `CPPHTTPLIB_THREAD_POOL_COUNT` (8 or `std::thread::hardware_concurrency() - 1`, whichever is greater) and can scale up to 4x that count under load (`CPPHTTPLIB_THREAD_POOL_MAX_COUNT`). When all base threads are busy, temporary threads are spawned automatically up to the maximum. These dynamic threads exit after an idle timeout (`CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT`, default 3 seconds).
This dynamic scaling helps accommodate WebSocket connections alongside HTTP requests. However, if you expect many simultaneous WebSocket connections, you should configure the thread pool accordingly:
```cpp
httplib::Server svr;
svr.new_task_queue = [] {
return new httplib::ThreadPool(/*base_threads=*/8, /*max_threads=*/128);
};
```
Choose sizes that account for both your expected HTTP load and the maximum number of simultaneous WebSocket connections.
## Protocol
The implementation follows [RFC 6455](https://tools.ietf.org/html/rfc6455):
- Handshake via HTTP Upgrade with `Sec-WebSocket-Key` / `Sec-WebSocket-Accept`
- Subprotocol negotiation via `Sec-WebSocket-Protocol`
- Frame masking (client-to-server)
- Control frames: Close, Ping, Pong
- Message fragmentation and reassembly
- Close handshake with status codes
## Browser Test
Run the echo server example and open `http://localhost:8080` in a browser:
```bash
cd example && make wsecho && ./wsecho
```
+328 -58
View File
@@ -1,19 +1,26 @@
cpp-httplib
===========
# cpp-httplib
[![](https://github.com/yhirose/cpp-httplib/workflows/test/badge.svg)](https://github.com/yhirose/cpp-httplib/actions)
A C++11 single-file header-only cross platform HTTP/HTTPS library.
A C++11 single-file header-only cross platform HTTP/HTTPS library.<br>
It's extremely easy to set up. Just include the **[httplib.h](https://raw.githubusercontent.com/yhirose/cpp-httplib/refs/heads/master/httplib.h)** file in your code!
It's extremely easy to set up. Just include the **httplib.h** file in your code!
**Learn more in the [official documentation](https://yhirose.github.io/cpp-httplib/)**.
> [!IMPORTANT]
> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
Simple examples
---------------
## Main Features
#### Server (Multi-threaded)
- HTTP Server/Client
- SSL/TLS support (OpenSSL, MbedTLS, wolfSSL)
- [Stream API](README-stream.md)
- [Server-Sent Events](README-sse.md)
- [WebSocket](README-websocket.md)
## Simple examples
### Server
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
@@ -32,7 +39,7 @@ svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
svr.listen("0.0.0.0", 8080);
```
#### Client
### Client
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
@@ -44,24 +51,40 @@ httplib::Client cli("http://yhirose.github.io");
// HTTPS
httplib::Client cli("https://yhirose.github.io");
auto res = cli.Get("/hi");
res->status;
res->body;
if (auto res = cli.Get("/hi")) {
res->status;
res->body;
}
```
SSL Support
-----------
## SSL/TLS Support
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
cpp-httplib supports multiple TLS backends through an abstraction layer:
| Backend | Define | Libraries |
| :------ | :----- | :-------- |
| OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` |
| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` |
| wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` |
> [!NOTE]
> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information.
> OpenSSL 3.0 or later is required. Please see [this page](https://www.openssl.org/policies/releasestrat.html) for more information.
> [!NOTE]
> Mbed TLS 2.x and 3.x are supported. The library automatically detects the version and uses the appropriate API.
> [!NOTE]
> wolfSSL must be built with OpenSSL compatibility layer enabled (`--enable-opensslall`). wolfSSL 5.x is supported.
> [!NOTE]
> **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()` or `load_ca_cert_store(pem, size)`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable with these backends.
> [!TIP]
> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
> For macOS: cpp-httplib automatically loads system certs from the Keychain when a TLS backend is enabled. `CoreFoundation` and `Security` must be linked with `-framework`. To disable this, define `CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES`.
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
// Use either OpenSSL, Mbed TLS, or wolfSSL
#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT
#include "path/to/httplib.h"
// Server
@@ -82,15 +105,15 @@ cli.enable_server_certificate_verification(false);
cli.enable_server_hostname_verification(false);
```
> [!NOTE]
> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
### SSL Error Handling
When SSL operations fail, cpp-httplib provides detailed error information through two separate error fields:
When SSL operations fail, cpp-httplib provides detailed error information through `ssl_error()` and `ssl_backend_error()`:
- `ssl_error()` - Returns the TLS-level error code (e.g., `SSL_ERROR_SSL` for OpenSSL)
- `ssl_backend_error()` - Returns the backend-specific error code (e.g., `ERR_get_error()` for OpenSSL/wolfSSL, return value for Mbed TLS)
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT
#include "path/to/httplib.h"
httplib::Client cli("https://example.com");
@@ -107,18 +130,18 @@ if (!res) {
break;
case httplib::Error::SSLLoadingCerts:
std::cout << "SSL cert loading failed, OpenSSL error: "
<< std::hex << res.ssl_openssl_error() << std::endl;
std::cout << "SSL cert loading failed, backend error: "
<< std::hex << res.ssl_backend_error() << std::endl;
break;
case httplib::Error::SSLServerVerification:
std::cout << "SSL verification failed, X509 error: "
<< res.ssl_openssl_error() << std::endl;
std::cout << "SSL verification failed, verify error: "
<< res.ssl_backend_error() << std::endl;
break;
case httplib::Error::SSLServerHostnameVerification:
std::cout << "SSL hostname verification failed, X509 error: "
<< res.ssl_openssl_error() << std::endl;
std::cout << "SSL hostname verification failed, verify error: "
<< res.ssl_backend_error() << std::endl;
break;
default:
@@ -127,8 +150,75 @@ if (!res) {
}
```
Server
------
### Custom Certificate Verification
You can set a custom verification callback using `tls::VerifyCallback`:
```c++
httplib::Client cli("https://example.com");
cli.set_server_certificate_verifier(
[](const httplib::tls::VerifyContext &ctx) -> bool {
std::cout << "Subject CN: " << ctx.subject_cn() << std::endl;
std::cout << "Issuer: " << ctx.issuer_name() << std::endl;
std::cout << "Depth: " << ctx.depth << std::endl;
std::cout << "Pre-verified: " << ctx.preverify_ok << std::endl;
// Inspect SANs (Subject Alternative Names)
for (const auto &san : ctx.sans()) {
std::cout << "SAN: " << san.value << std::endl;
}
// Return true to accept, false to reject
return ctx.preverify_ok;
});
```
### Peer Certificate Inspection
On the server side, you can inspect the client's peer certificate from a request handler:
```c++
httplib::SSLServer svr("./cert.pem", "./key.pem",
"./client-ca-cert.pem");
svr.Get("/", [](const httplib::Request &req, httplib::Response &res) {
auto cert = req.peer_cert();
if (cert) {
std::cout << "Client CN: " << cert.subject_cn() << std::endl;
std::cout << "Serial: " << cert.serial() << std::endl;
}
auto sni = req.sni();
std::cout << "SNI: " << sni << std::endl;
});
```
### Windows Certificate Verification
On Windows, cpp-httplib automatically performs additional certificate verification using the Windows certificate store via CryptoAPI (`CertGetCertificateChain` / `CertVerifyCertificateChainPolicy`). This works with all TLS backends (OpenSSL, Mbed TLS, and wolfSSL), providing:
- Real-time certificate validation integrated with Windows Update
- Certificate revocation checking
- SSL/TLS policy verification using the system certificate store (ROOT and CA)
This feature is enabled by default and can be controlled at runtime:
```c++
// Disable Windows certificate verification (use only OpenSSL/Mbed TLS/wolfSSL verification)
cli.enable_windows_certificate_verification(false);
```
To disable this feature at compile time, define:
```c++
#define CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
```
> [!NOTE]
> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
## Server
```c++
#include <httplib.h>
@@ -190,7 +280,7 @@ int main(void)
}
```
`Post`, `Put`, `Delete` and `Options` methods are also supported.
`Post`, `Put`, `Patch`, `Delete` and `Options` methods are also supported.
### Bind a socket to multiple interfaces and any available port
@@ -379,6 +469,34 @@ svr.set_pre_request_handler([](const auto& req, auto& res) {
});
```
### Response user data
`res.user_data` is a `std::map<std::string, httplib::any>` that lets pre-routing or pre-request handlers pass arbitrary data to route handlers.
```cpp
struct AuthContext {
std::string user_id;
std::string role;
};
svr.set_pre_routing_handler([](const auto& req, auto& res) {
auto token = req.get_header_value("Authorization");
res.user_data["auth"] = AuthContext{decode_token(token)};
return Server::HandlerResponse::Unhandled;
});
svr.Get("/me", [](const auto& /*req*/, auto& res) {
auto* ctx = httplib::any_cast<AuthContext>(&res.user_data["auth"]);
if (!ctx) {
res.status = StatusCode::Unauthorized_401;
return;
}
res.set_content("Hello " + ctx->user_id, "text/plain");
});
```
`httplib::any` mirrors the C++17 `std::any` API. On C++17 and later it is an alias for `std::any`; on C++11/14 a compatible implementation is provided.
### Form data handling
#### URL-encoded form data ('application/x-www-form-urlencoded')
@@ -419,8 +537,17 @@ svr.Post("/multipart", [&](const Request& req, Response& res) {
std::cout << "Header: " << header.first << " = " << header.second << std::endl;
}
// IMPORTANT: file.filename is an untrusted value from the client.
// Always extract only the basename to prevent path traversal attacks.
auto safe_name = std::filesystem::path(file.filename).filename();
if (safe_name.empty() || safe_name == "." || safe_name == "..") {
res.status = StatusCode::BadRequest_400;
res.set_content("Invalid filename", "text/plain");
return;
}
// Save to disk
std::ofstream ofs(file.filename, std::ios::binary);
std::ofstream ofs(upload_dir / safe_name, std::ios::binary);
ofs << file.content;
}
@@ -633,20 +760,24 @@ Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/e
### Default thread pool support
`ThreadPool` is used as the **default** task queue, with a default thread count of 8 or `std::thread::hardware_concurrency() - 1`, whichever is greater. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
`ThreadPool` is used as the **default** task queue, with dynamic scaling support. By default, it maintains a base thread count of 8 or `std::thread::hardware_concurrency() - 1` (whichever is greater), and can scale up to 4x that count under load. You can change these with `CPPHTTPLIB_THREAD_POOL_COUNT` and `CPPHTTPLIB_THREAD_POOL_MAX_COUNT`.
If you want to set the thread count at runtime, there is no convenient way... But here is how.
When all threads are busy and a new task arrives, a temporary thread is spawned (up to the maximum). When a dynamic thread finishes its task and the queue is empty, or after an idle timeout, it exits automatically. The idle timeout defaults to 3 seconds, configurable via `CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT`.
If you want to set the thread counts at runtime:
```cpp
svr.new_task_queue = [] { return new ThreadPool(12); };
svr.new_task_queue = [] { return new ThreadPool(/*base_threads=*/8, /*max_threads=*/64); };
```
#### Max queued requests
You can also provide an optional parameter to limit the maximum number
of pending requests, i.e. requests `accept()`ed by the listener but
still waiting to be serviced by worker threads.
```cpp
svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };
svr.new_task_queue = [] { return new ThreadPool(/*base_threads=*/12, /*max_threads=*/0, /*max_queued_requests=*/18); };
```
Default limit is 0 (unlimited). Once the limit is reached, the listener
@@ -682,8 +813,7 @@ svr.new_task_queue = [] {
};
```
Client
------
## Client
```c++
#include <httplib.h>
@@ -721,7 +851,7 @@ httplib::SSLClient cli("localhost");
Here is the list of errors from `Result::error()`.
```c++
enum Error {
enum class Error {
Success = 0,
Unknown,
Connection,
@@ -738,6 +868,24 @@ enum Error {
Compression,
ConnectionTimeout,
ProxyConnection,
ConnectionClosed,
Timeout,
ResourceExhaustion,
TooManyFormDataFiles,
ExceedMaxPayloadSize,
ExceedUriMaxLength,
ExceedMaxSocketDescriptorCount,
InvalidRequestLine,
InvalidHTTPMethod,
InvalidHTTPVersion,
InvalidHeaders,
MultipartParsing,
OpenFile,
Listen,
GetSockName,
UnsupportedAddressFamily,
HTTPParsing,
InvalidRangeHeader,
};
```
@@ -794,11 +942,15 @@ httplib::Headers headers = {
};
auto res = cli.Get("/hi", headers);
```
or
```c++
auto res = cli.Get("/hi", {{"Hello", "World!"}});
```
or
```c++
cli.set_default_headers({
{ "Hello", "World!" }
@@ -847,12 +999,38 @@ httplib::UploadFormDataItems items = {
auto res = cli.Post("/multipart", items);
```
To upload files from disk without loading them entirely into memory, use `make_file_provider`. The file is sent with chunked transfer encoding.
```cpp
httplib::FormDataProviderItems providers = {
httplib::make_file_provider("file1", "/path/to/large.bin", "large.bin", "application/octet-stream"),
httplib::make_file_provider("avatar", "/path/to/photo.jpg", "photo.jpg", "image/jpeg"),
};
auto res = cli.Post("/upload", {}, {}, providers);
```
### POST with a file body
To POST a file as a raw binary body with `Content-Length`, use `make_file_body`.
```cpp
auto [size, provider] = httplib::make_file_body("/path/to/data.bin");
auto res = cli.Post("/upload", size, provider, "application/octet-stream");
```
### PUT
```c++
res = cli.Put("/resource/foo", "text", "text/plain");
```
### PATCH
```c++
res = cli.Patch("/resource/foo", "text", "text/plain");
```
### DELETE
```c++
@@ -877,6 +1055,12 @@ cli.set_write_timeout(5, 0); // 5 seconds
cli.set_max_timeout(5000); // 5 seconds
```
### Set maximum payload length for reading a response body
```c++
cli.set_payload_max_length(1024 * 1024 * 512); // 512MB
```
### Receive content with a content receiver
```c++
@@ -987,7 +1171,7 @@ cli.set_proxy_bearer_token_auth("pass");
### Range
```cpp
httplib::Client cli("httpbin.org");
httplib::Client cli("httpcan.org");
auto res = cli.Get("/range/32", {
httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'
@@ -1077,18 +1261,21 @@ httplib::Server svr;
svr.listen("127.0.0.1", 8080);
```
Compression
-----------
## Payload Limit
The maximum payload body size is limited to 100MB by default for both server and client. You can change it with `set_payload_max_length()` or by defining `CPPHTTPLIB_PAYLOAD_MAX_LENGTH` at compile time. Setting it to `0` disables the limit entirely.
## Compression
The server can apply compression to the following MIME type contents:
* all text types except text/event-stream
* image/svg+xml
* application/javascript
* application/json
* application/xml
* application/protobuf
* application/xhtml+xml
- all text types except text/event-stream
- image/svg+xml
- application/javascript
- application/json
- application/xml
- application/protobuf
- application/xhtml+xml
### Zlib Support
@@ -1181,9 +1368,95 @@ std::string decoded_component = httplib::decode_uri_component(encoded_component)
Use `encode_uri()` for full URLs and `encode_uri_component()` for individual query parameters or path segments.
## Stream API
Split httplib.h into .h and .cc
-------------------------------
Process large responses without loading everything into memory.
```c++
httplib::Client cli("localhost", 8080);
cli.set_follow_location(true);
...
auto result = httplib::stream::Get(cli, "/large-file");
if (result) {
while (result.next()) {
process(result.data(), result.size()); // Process each chunk as it arrives
}
}
// Or read the entire body at once
auto result2 = httplib::stream::Get(cli, "/file");
if (result2) {
std::string body = result2.read_all();
}
```
All HTTP methods are supported: `stream::Get`, `Post`, `Put`, `Patch`, `Delete`, `Head`, `Options`.
See [README-stream.md](README-stream.md) for more details.
## SSE Client
```cpp
#include <httplib.h>
int main() {
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << "Event: " << msg.event << std::endl;
std::cout << "Data: " << msg.data << std::endl;
});
sse.start(); // Blocking, with auto-reconnect
return 0;
}
```
See [README-sse.md](README-sse.md) for more details.
## WebSocket
```cpp
// Server
httplib::Server svr;
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
httplib::ws::Message msg;
while (ws.read(msg)) {
if (msg.is_text()) {
ws.send("Echo: " + msg.data);
}
}
});
svr.listen("localhost", 8080);
```
```cpp
// Client
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws");
if (ws.connect()) {
ws.send("Hello, WebSocket!");
std::string msg;
if (ws.read(msg)) {
std::cout << "Received: " << msg << std::endl;
}
ws.close();
}
```
SSL is also supported via `wss://` scheme (e.g. `WebSocketClient("wss://example.com/ws")`). Subprotocol negotiation (`Sec-WebSocket-Protocol`) is supported via `SubProtocolSelector` callback.
> **Note:** WebSocket connections occupy a thread for their entire lifetime. If you plan to handle many simultaneous WebSocket connections, consider using a dynamic thread pool: `svr.new_task_queue = [] { return new ThreadPool(8, 64); };`
See [README-websocket.md](README-websocket.md) for more details.
## Split httplib.h into .h and .cc
```console
$ ./split.py -h
@@ -1201,8 +1474,7 @@ $ ./split.py
Wrote out/httplib.h and out/httplib.cc
```
Dockerfile for Static HTTP Server
---------------------------------
## Dockerfile for Static HTTP Server
Dockerfile for static HTTP server is available. Port number of this HTTP server is 80, and it serves static files from `/html` directory in the container.
@@ -1273,12 +1545,10 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
> [!NOTE]
> Windows 8 or lower, Visual Studio 2015 or lower, and Cygwin and MSYS2 including MinGW are neither supported nor tested.
License
-------
## License
MIT license (© 2025 Yuji Hirose)
MIT license (© 2026 Yuji Hirose)
Special Thanks To
-----------------
## Special Thanks To
[These folks](https://github.com/yhirose/cpp-httplib/graphs/contributors) made great contributions to polish this library to totally another level from a simple toy!
+8 -5
View File
@@ -1,4 +1,7 @@
CXXFLAGS = -std=c++11 -O2 -I..
CXXFLAGS = -O2 -I..
CPPHTTPLIB_CXXFLAGS = -std=c++11
CROW_CXXFLAGS = -std=c++17
CPPHTTPLIB_FLAGS = -DCPPHTTPLIB_THREAD_POOL_COUNT=16
@@ -18,11 +21,11 @@ run : server
@./server
server : cpp-httplib/main.cpp ../httplib.h
@g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp
@g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp
# crow
bench-crow: server-crow
@echo "-------------\n Crow v1.2.0\n-------------\n"
@echo "-------------\n Crow v1.3.1\n-------------\n"
@./server-crow & export PID=$$!; $(BENCH); kill $${PID}
@echo ""
@@ -32,8 +35,8 @@ monitor-crow: server-crow
run-crow : server-crow
@./server-crow
server-crow : crow/main.cpp
@g++ -o $@ $(CXXFLAGS) crow/main.cpp
server-crow : crow/main.cpp crow/crow_all.h
@g++ -o $@ $(CXXFLAGS) $(CROW_CXXFLAGS) crow/main.cpp
# misc
build: server server-crow
+10819 -11339
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
class CustomLogger : public crow::ILogHandler {
public:
void log(std::string, crow::LogLevel) {}
void log(const std::string &, crow::LogLevel) {}
};
int main() {
+7 -2
View File
@@ -86,7 +86,9 @@ foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
# Check if the target was already found by Pkgconf
if(TARGET PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
# ALIAS since we don't want the PkgConfig namespace on the Cmake library (for end-users)
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
if (NOT TARGET Brotli::${_component_name})
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
endif()
# Tells HANDLE_COMPONENTS we found the component
set(Brotli_${_component_name}_FOUND TRUE)
@@ -139,7 +141,10 @@ foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
# Tells HANDLE_COMPONENTS we found the component
set(Brotli_${_component_name}_FOUND TRUE)
add_library("Brotli::${_component_name}" UNKNOWN IMPORTED)
if (NOT TARGET Brotli::${_component_name})
add_library("Brotli::${_component_name}" UNKNOWN IMPORTED)
endif()
# Attach the literal library and include dir to the IMPORTED target for the end-user
set_target_properties("Brotli::${_component_name}" PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Brotli_INCLUDE_DIR}"
+13
View File
@@ -4,6 +4,8 @@
# Setting these here so they're accessible after install.
# Might be useful for some users to check which settings were used.
set(HTTPLIB_IS_USING_OPENSSL @HTTPLIB_IS_USING_OPENSSL@)
set(HTTPLIB_IS_USING_WOLFSSL @HTTPLIB_IS_USING_WOLFSSL@)
set(HTTPLIB_IS_USING_MBEDTLS @HTTPLIB_IS_USING_MBEDTLS@)
set(HTTPLIB_IS_USING_ZLIB @HTTPLIB_IS_USING_ZLIB@)
set(HTTPLIB_IS_COMPILED @HTTPLIB_COMPILE@)
set(HTTPLIB_IS_USING_BROTLI @HTTPLIB_IS_USING_BROTLI@)
@@ -25,11 +27,22 @@ if(@HTTPLIB_IS_USING_OPENSSL@)
endif()
set(httplib_OpenSSL_FOUND ${OpenSSL_FOUND})
endif()
if(@HTTPLIB_IS_USING_ZLIB@)
find_dependency(ZLIB)
set(httplib_ZLIB_FOUND ${ZLIB_FOUND})
endif()
if(@HTTPLIB_IS_USING_WOLFSSL@)
find_dependency(wolfssl)
set(httplib_wolfssl_FOUND ${wolfssl_FOUND})
endif()
if(@HTTPLIB_IS_USING_MBEDTLS@)
find_dependency(MbedTLS)
set(httplib_MbedTLS_FOUND ${MbedTLS_FOUND})
endif()
if(@HTTPLIB_IS_USING_BROTLI@)
# Needed so we can use our own FindBrotli.cmake in this file.
# Note that the FindBrotli.cmake file is installed in the same dir as this file.
+16
View File
@@ -0,0 +1,16 @@
# This file contains C++20 module support requiring CMake 3.28+
# Included conditionally to prevent parse errors on older CMake versions
if(HTTPLIB_BUILD_MODULES)
if(POLICY CMP0155)
cmake_policy(SET CMP0155 NEW)
endif()
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
target_sources(${PROJECT_NAME}
PUBLIC
FILE_SET CXX_MODULES FILES
"${_httplib_build_includedir}/httplib.cppm"
)
endif()
+35 -27
View File
@@ -1,7 +1,7 @@
//
// main.cc
//
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
@@ -48,32 +48,10 @@ std::string get_error_time_format() {
return ss.str();
}
std::string get_client_ip(const Request &req) {
// Check for X-Forwarded-For header first (common in reverse proxy setups)
auto forwarded_for = req.get_header_value("X-Forwarded-For");
if (!forwarded_for.empty()) {
// Get the first IP if there are multiple
auto comma_pos = forwarded_for.find(',');
if (comma_pos != std::string::npos) {
return forwarded_for.substr(0, comma_pos);
}
return forwarded_for;
}
// Check for X-Real-IP header
auto real_ip = req.get_header_value("X-Real-IP");
if (!real_ip.empty()) { return real_ip; }
// Fallback to remote address (though cpp-httplib doesn't provide this
// directly) For demonstration, we'll use a placeholder
return "127.0.0.1";
}
// NGINX Combined log format:
// $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
// "$http_referer" "$http_user_agent"
void nginx_access_logger(const Request &req, const Response &res) {
auto remote_addr = get_client_ip(req);
std::string remote_user =
"-"; // cpp-httplib doesn't have built-in auth user tracking
auto time_local = get_time_format();
@@ -86,7 +64,7 @@ void nginx_access_logger(const Request &req, const Response &res) {
if (http_user_agent.empty()) http_user_agent = "-";
std::cout << std::format("{} - {} [{}] \"{}\" {} {} \"{}\" \"{}\"",
remote_addr, remote_user, time_local, request,
req.remote_addr, remote_user, time_local, request,
status, body_bytes_sent, http_referer,
http_user_agent)
<< std::endl;
@@ -100,7 +78,6 @@ void nginx_error_logger(const Error &err, const Request *req) {
std::string level = "error";
if (req) {
auto client_ip = get_client_ip(*req);
auto request =
std::format("{} {} {}", req->method, req->path, req->version);
auto host = req->get_header_value("Host");
@@ -108,8 +85,8 @@ void nginx_error_logger(const Error &err, const Request *req) {
std::cerr << std::format("{} [{}] {}, client: {}, request: "
"\"{}\", host: \"{}\"",
time_local, level, to_string(err), client_ip,
request, host)
time_local, level, to_string(err),
req->remote_addr, request, host)
<< std::endl;
} else {
// If no request context, just log the error
@@ -131,6 +108,10 @@ void print_usage(const char *program_name) {
std::cout << " Format: mount_point:document_root"
<< std::endl;
std::cout << " (default: /:./html)" << std::endl;
std::cout << " --trusted-proxy <ip> Add trusted proxy IP address"
<< std::endl;
std::cout << " (can be specified multiple times)"
<< std::endl;
std::cout << " --version Show version information"
<< std::endl;
std::cout << " --help Show this help message" << std::endl;
@@ -140,6 +121,9 @@ void print_usage(const char *program_name) {
<< " --host localhost --port 8080 --mount /:./html" << std::endl;
std::cout << " " << program_name
<< " --host 0.0.0.0 --port 3000 --mount /api:./api" << std::endl;
std::cout << " " << program_name
<< " --trusted-proxy 192.168.1.100 --trusted-proxy 10.0.0.1"
<< std::endl;
}
struct ServerConfig {
@@ -147,6 +131,7 @@ struct ServerConfig {
int port = 8080;
std::string mount_point = "/";
std::string document_root = "./html";
std::vector<std::string> trusted_proxies;
};
enum class ParseResult { SUCCESS, HELP_REQUESTED, VERSION_REQUESTED, ERROR };
@@ -205,6 +190,14 @@ ParseResult parse_command_line(int argc, char *argv[], ServerConfig &config) {
} else if (strcmp(argv[i], "--version") == 0) {
std::cout << CPPHTTPLIB_VERSION << std::endl;
return ParseResult::VERSION_REQUESTED;
} else if (strcmp(argv[i], "--trusted-proxy") == 0) {
if (i + 1 >= argc) {
std::cerr << "Error: --trusted-proxy requires an IP address argument"
<< std::endl;
print_usage(argv[0]);
return ParseResult::ERROR;
}
config.trusted_proxies.push_back(argv[++i]);
} else {
std::cerr << "Error: Unknown option '" << argv[i] << "'" << std::endl;
print_usage(argv[0]);
@@ -218,6 +211,11 @@ bool setup_server(Server &svr, const ServerConfig &config) {
svr.set_logger(nginx_access_logger);
svr.set_error_logger(nginx_error_logger);
// Set trusted proxies if specified
if (!config.trusted_proxies.empty()) {
svr.set_trusted_proxies(config.trusted_proxies);
}
auto ret = svr.set_mount_point(config.mount_point, config.document_root);
if (!ret) {
std::cerr
@@ -285,6 +283,16 @@ int main(int argc, char *argv[]) {
<< std::endl;
std::cout << "Mount point: " << config.mount_point << " -> "
<< config.document_root << std::endl;
if (!config.trusted_proxies.empty()) {
std::cout << "Trusted proxies: ";
for (size_t i = 0; i < config.trusted_proxies.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << config.trusted_proxies[i];
}
std::cout << std::endl;
}
std::cout << "Press Ctrl+C to shutdown gracefully..." << std::endl;
auto ret = svr.listen(config.hostname, config.port);
+1992
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "docs-gen"
version = "0.1.0"
edition = "2021"
[dependencies]
pulldown-cmark = "0.12"
tera = "1"
walkdir = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
toml = "0.8"
syntect = "5"
anyhow = "1"
clap = { version = "4", features = ["derive"] }
notify = "7"
tiny_http = "0.12"
tungstenite = "0.24"
open = "5"
tempfile = "3"
socket2 = { version = "0.5", features = ["all"] }
+254
View File
@@ -0,0 +1,254 @@
# docs-gen
A static site generator for multi-language documentation. Markdown content, Tera templates, and syntax highlighting — all in a single binary.
## Quick Start
```bash
# 1. Scaffold a new project
docs-gen init my-docs
# 2. Start the local dev server with live-reload
docs-gen serve my-docs --open
# 3. Build for production
docs-gen build my-docs --out docs
```
---
## Commands
### `init [DIR]`
Creates a new project scaffold in `DIR` (default: `.`).
Generated files:
```
config.toml
pages/
en/index.md
ja/index.md
templates/
base.html
page.html
portal.html
static/
css/main.css
js/main.js
```
Existing files are never overwritten.
---
### `serve [SRC] [--port PORT] [--open]`
Builds the site into a temporary directory and serves it locally. Watches for changes and live-reloads the browser automatically.
| Option | Default | Description |
|--------|---------|-------------|
| `SRC` | `.` | Source directory |
| `--port` | `8080` | HTTP server port |
| `--open` | — | Open browser on startup |
---
### `build [SRC] [--out OUT]`
Generates the static site from source.
| Option | Default | Description |
|--------|---------|-------------|
| `SRC` | `.` | Source directory |
| `--out` | `docs` | Output directory |
---
## Source Directory Structure
Only `config.toml` and `pages/` are required. `templates/` and `static/` are optional — when absent, built-in defaults are used automatically.
```
my-docs/
├── config.toml # Site configuration (required)
├── pages/ # Markdown content (required)
│ ├── en/
│ │ ├── index.md # Portal page (homepage, no sidebar)
│ │ └── guide/
│ │ ├── index.md # Section index
│ │ ├── 01-intro.md
│ │ └── 02-usage.md
│ └── ja/
│ └── ...
├── templates/ # Override built-in HTML templates (optional)
│ ├── base.html
│ ├── page.html
│ └── portal.html
└── static/ # Override built-in CSS/JS/assets (optional)
├── css/main.css
└── js/main.js
```
---
## config.toml
```toml
[site]
title = "My Project"
version = "1.0.0" # Optional. Shown in header.
hostname = "https://example.github.io" # Optional. Combined with base_path for full URLs.
base_path = "/my-project" # URL prefix. Use "" for local-only.
[[nav]]
label = "Guide"
path = "guide/" # Internal section path (resolved per language)
icon_svg = '<svg ...>...</svg>' # Optional inline SVG icon
[[nav]]
label = "GitHub"
url = "https://github.com/your/repo" # External URL
icon_svg = '<svg ...>...</svg>'
[i18n]
langs = ["en", "ja"] # First entry is the default language
[highlight]
dark_theme = "base16-eighties.dark" # Dark mode theme
light_theme = "InspiredGitHub" # Light mode theme (optional)
```
### `[site]`
| Key | Required | Description |
|-----|----------|-------------|
| `title` | yes | Site title displayed in the header |
| `version` | no | Version string displayed in the header |
| `hostname` | no | Base hostname (e.g. `"https://user.github.io"`). Combined with `base_path` to form `site.base_url` in templates. |
| `base_path` | no | URL path prefix. Use `"/repo-name"` for GitHub Pages, `""` for local development. |
### `[[nav]]` — Toolbar Buttons
Defines buttons in the site header. Each entry supports:
| Key | Required | Description |
|-----|----------|-------------|
| `label` | yes | Button label text |
| `path` | no | Internal section path relative to `<lang>/` (e.g. `"guide/"`). Resolved using the current language. |
| `url` | no | Absolute external URL. Takes precedence over `path` if both are set. |
| `icon_svg` | no | Inline SVG markup displayed as an icon |
### `[i18n]`
| Key | Required | Description |
|-----|----------|-------------|
| `langs` | yes | List of language codes. At least one is required. The first entry is used as the default language. |
### `[highlight]`
| Key | Default | Description |
|-----|---------|-------------|
| `dark_theme` | `base16-ocean.dark` | Theme for dark mode |
| `light_theme` | _(none)_ | Theme for light mode. When set, both dark and light code blocks are emitted and toggled via CSS. |
Available themes: `base16-ocean.dark`, `base16-ocean.light`, `base16-eighties.dark`, `base16-mocha.dark`, `InspiredGitHub`, `Solarized (dark)`, `Solarized (light)`.
---
## Writing Pages
### Frontmatter
Every `.md` file must begin with YAML frontmatter:
```yaml
---
title: "Getting Started"
order: 1
---
Page content goes here...
```
| Field | Required | Description |
|-------|----------|-------------|
| `title` | yes | Page title shown in the heading and browser tab |
| `order` | no | Sort order within the section (default: `0`) |
| `status` | no | Set to `"draft"` to display a DRAFT banner |
### URL Routing
Files are mapped to URLs as follows:
| File | URL |
|------|-----|
| `en/index.md` | `<base_path>/en/` |
| `en/guide/index.md` | `<base_path>/en/guide/` |
| `en/guide/01-intro.md` | `<base_path>/en/guide/01-intro/` |
The root `index.html` is generated automatically and redirects to the default language, respecting the user's `localStorage` language preference.
### Sidebar Navigation
Sidebar navigation is generated automatically:
- Each subdirectory under a language becomes a **section**
- The section's `index.md` title is used as the section heading
- Pages within a section are sorted by `order`, then by filename
- `index.md` at the language root uses `portal.html` (no sidebar)
- All other pages use `page.html` (with sidebar)
---
## Customizing Templates and Assets
When `templates/` or `static/` directories exist in the source, files there override the built-in defaults. Use `docs-gen init` to generate the defaults as a starting point.
Three templates are available:
| Template | Used for |
|----------|----------|
| `base.html` | Shared layout: `<head>`, header, footer, scripts |
| `page.html` | Content pages with sidebar |
| `portal.html` | Homepage (`index.md` at language root), no sidebar |
---
## Template Variables
Templates use [Tera](https://keats.github.io/tera/) syntax. Available variables:
### All templates
| Variable | Type | Description |
|----------|------|-------------|
| `page.title` | string | Page title from frontmatter |
| `page.url` | string | Page URL path |
| `page.status` | string? | `"draft"` or null |
| `content` | string | Rendered HTML (use `{{ content \| safe }}`) |
| `lang` | string | Current language code |
| `site.title` | string | Site title |
| `site.version` | string? | Site version |
| `site.base_url` | string | Full base URL (`hostname` + `base_path`) |
| `site.base_path` | string | URL path prefix |
| `site.langs` | list | All language codes |
| `site.nav` | list | Toolbar button entries |
| `site.nav[].label` | string | Button label |
| `site.nav[].url` | string? | External URL (if set) |
| `site.nav[].path` | string? | Internal section path (if set) |
| `site.nav[].icon_svg` | string? | Inline SVG icon (if set) |
### `page.html` only
| Variable | Type | Description |
|----------|------|-------------|
| `nav` | list | Sidebar sections |
| `nav[].title` | string | Section title |
| `nav[].url` | string | Section index URL |
| `nav[].active` | bool | True if this section contains the current page |
| `nav[].children` | list | Pages within this section |
| `nav[].children[].title` | string | Page title |
| `nav[].children[].url` | string | Page URL |
| `nav[].children[].active` | bool | True if this is the current page |
+20
View File
@@ -0,0 +1,20 @@
[site]
title = "My Docs"
hostname = "https://example.github.io"
base_path = "/my-project"
# [[nav]]
# label = "Guide"
# path = "guide/"
#
# [[nav]]
# label = "GitHub"
# url = "https://github.com/your/repo"
# icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>'
[i18n]
langs = ["en"]
[highlight]
dark_theme = "base16-ocean.dark"
light_theme = "InspiredGitHub"
+10
View File
@@ -0,0 +1,10 @@
---
title: Welcome
order: 0
---
## Welcome
This is the home page of your documentation site.
Edit this file at `pages/en/index.md` to get started.
+10
View File
@@ -0,0 +1,10 @@
---
title: ようこそ
order: 0
---
## ようこそ
ドキュメントサイトのトップページです。
`pages/ja/index.md` を編集して始めましょう。
+603
View File
@@ -0,0 +1,603 @@
:root {
--bg: #333;
--bg-secondary: #3c3c3c;
--bg-code: #2a2a2a;
--text: #ccc;
--text-bright: white;
--text-muted: #999;
--text-code: #b0b0b0;
--text-inline-code: plum;
--border: #555;
--border-code: #3a3a3a;
--link: palegoldenrod;
--heading: lightskyblue;
--heading-link: #f0c090;
--header-nav-link: pink;
--emphasis: pink;
--nav-section: #bbb;
--nav-section-active: #ddd;
--content-width: 900px;
--sidebar-width: 280px;
--header-height: 48px;
--line-height: 1.6;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: var(--line-height);
}
a {
color: var(--link);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Header */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--header-height);
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border);
z-index: 100;
}
.header-inner {
height: 100%;
display: flex;
align-items: center;
padding: 0 16px;
gap: 24px;
}
.header-title {
color: var(--text);
font-weight: bold;
font-size: 1.1rem;
white-space: nowrap;
}
.header-title:hover {
text-decoration: none;
color: var(--text-bright);
}
.header-spacer {
flex: 1;
}
.header-nav {
display: flex;
gap: 16px;
}
.header-nav a {
display: flex;
align-items: center;
gap: 4px;
color: var(--header-nav-link);
font-size: 0.9rem;
}
.header-nav a svg {
flex-shrink: 0;
opacity: 0.85;
}
.header-tools {
display: flex;
align-items: center;
gap: 2px;
}
.lang-selector {
position: relative;
}
.lang-btn {
display: flex;
align-items: center;
gap: 5px;
background: none;
border: none;
color: var(--text);
padding: 4px 6px;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
opacity: 0.8;
}
.lang-btn:hover {
opacity: 1;
}
.lang-popup {
display: none;
position: absolute;
right: 0;
top: 100%;
margin-top: 4px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 4px;
list-style: none;
min-width: 60px;
z-index: 200;
}
.lang-popup.open {
display: block;
}
.lang-popup li a {
display: block;
padding: 6px 12px;
color: var(--text);
font-size: 0.85rem;
}
.lang-popup li a:hover {
background: var(--bg);
text-decoration: none;
}
.sidebar-toggle {
display: none;
background: none;
border: none;
color: var(--text);
font-size: 1.2rem;
cursor: pointer;
padding: 4px 8px;
}
/* Draft banner */
.draft-banner {
position: fixed;
top: var(--header-height);
right: 0;
background: #c44;
color: white;
padding: 4px 16px;
font-size: 0.75rem;
font-weight: bold;
letter-spacing: 0.1em;
z-index: 99;
}
/* Layout */
.layout {
margin-top: var(--header-height);
display: grid;
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
min-height: calc(100vh - var(--header-height));
}
.layout.no-sidebar {
grid-template-columns: 1fr;
}
/* Sidebar */
.sidebar {
width: var(--sidebar-width);
flex-shrink: 0;
padding: 24px 16px;
border-right: 1px solid var(--bg-secondary);
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
overflow-y: auto;
}
.nav-section {
margin-bottom: 16px;
}
.nav-section-title {
color: var(--nav-section);
font-weight: bold;
font-size: 1.0rem;
display: block;
margin-bottom: 8px;
}
.nav-section-title.active {
color: var(--nav-section-active);
}
.nav-list {
list-style: none;
padding-left: 8px;
}
.nav-list li {
margin-bottom: 4px;
}
.nav-list li a {
color: var(--text-muted);
font-size: 0.85rem;
}
.nav-list li a:hover {
color: var(--text);
}
.nav-list li a.active {
color: var(--emphasis);
font-weight: bold;
}
/* Content */
.content {
min-width: 0;
max-width: var(--content-width);
padding: 32px 24px;
overflow-wrap: break-word;
}
.content.portal {
max-width: var(--content-width);
padding: 48px 24px;
margin: 0 auto;
}
.content article h1 {
font-size: 1.8rem;
margin-bottom: 24px;
color: var(--heading);
}
.content article h2 {
font-size: 1.4rem;
margin-top: 32px;
margin-bottom: 16px;
color: var(--heading-link);
}
.content article h3 {
font-size: 1.1rem;
margin-top: 24px;
margin-bottom: 12px;
color: var(--text);
}
.content article p {
margin-bottom: 12px;
}
.content article ul,
.content article ol {
margin-bottom: 12px;
padding-left: 24px;
}
.content article li {
margin-bottom: 4px;
}
.content article strong {
color: var(--emphasis);
}
.content article code {
background: var(--bg-code);
color: var(--text-inline-code);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
.content article pre {
background: var(--bg-code);
color: var(--text-code);
padding: 16px;
border-radius: 4px;
overflow-x: auto;
margin-bottom: 16px;
border: 1px solid var(--border-code);
}
.content article pre code {
background: none;
padding: 0;
}
.content article table {
width: 100%;
border-collapse: collapse;
margin-bottom: 16px;
}
.content article th,
.content article td {
border: 1px solid var(--bg-secondary);
padding: 8px 12px;
text-align: left;
}
.content article th {
background: var(--bg-secondary);
}
.content article blockquote {
border-left: 3px solid var(--text-muted);
padding-left: 16px;
margin-bottom: 12px;
color: var(--text-muted);
}
/* Code block theme switching: show dark by default, swap on light mode */
.code-block-wrapper > [data-code-theme="light"] {
display: none;
}
html[data-theme="light"] .code-block-wrapper > [data-code-theme="dark"] {
display: none;
}
html[data-theme="light"] .code-block-wrapper > [data-code-theme="light"] {
display: block;
}
/* Footer */
.footer {
padding: 12px 16px;
text-align: center;
color: var(--text-muted);
font-size: 0.8rem;
border-top: 1px solid var(--bg-secondary);
}
/* Responsive */
@media (max-width: 768px) {
.layout {
grid-template-columns: minmax(0, 1fr);
}
.sidebar {
position: fixed;
left: calc(-1 * var(--sidebar-width));
width: var(--sidebar-width);
top: var(--header-height);
height: calc(100vh - var(--header-height));
background: var(--bg);
z-index: 50;
transition: left 0.2s ease;
border-right: 1px solid var(--border);
}
.sidebar.open {
left: 0;
}
.sidebar-toggle {
display: block;
}
.content {
padding: 24px 16px;
}
}
@media (max-width: 480px) {
:root {
--header-height: 44px;
}
.header-inner {
padding: 0 12px;
gap: 12px;
}
.header-nav a {
font-size: 0.8rem;
}
.content article h1 {
font-size: 1.4rem;
}
.content article h2 {
font-size: 1.2rem;
}
}
/* Light mode */
[data-theme="light"] {
--bg: #f5f5f5;
--bg-secondary: #e8e8e8;
--bg-code: #eee;
--text: #333;
--text-bright: #000;
--text-muted: #666;
--text-code: #333;
--text-inline-code: #8b5ca0;
--border: #ccc;
--border-code: #ddd;
--link: #b8860b;
--heading: #2a6496;
--heading-link: #c06020;
--header-nav-link: #c04060;
--emphasis: #c04060;
--nav-section: #666;
--nav-section-active: #333;
}
/* Theme toggle */
.theme-toggle {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--text);
padding: 5px 6px;
border-radius: 4px;
cursor: pointer;
opacity: 0.8;
}
.theme-toggle:hover {
opacity: 1;
}
/* Search button */
.search-btn {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--text);
padding: 5px 6px;
border-radius: 4px;
cursor: pointer;
opacity: 0.8;
}
.search-btn:hover {
opacity: 1;
}
/* Search overlay & modal */
.search-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 300;
justify-content: center;
align-items: flex-start;
padding-top: 12vh;
}
.search-overlay.open {
display: flex;
}
.search-modal {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
width: 90%;
max-width: 560px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
overflow: hidden;
}
.search-input-wrap {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
}
.search-input-wrap svg {
flex-shrink: 0;
opacity: 0.5;
}
#search-input {
flex: 1;
background: none;
border: none;
outline: none;
color: var(--text-bright);
font-size: 1rem;
font-family: inherit;
}
#search-input::placeholder {
color: var(--text-muted);
}
.search-esc {
background: var(--bg);
color: var(--text-muted);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.7rem;
border: 1px solid var(--border);
}
.search-results {
list-style: none;
max-height: 50vh;
overflow-y: auto;
padding: 0;
margin: 0;
}
.search-results:empty::after {
content: "";
display: none;
}
.search-results li {
padding: 10px 16px;
cursor: pointer;
border-bottom: 1px solid var(--border);
}
.search-results li:last-child {
border-bottom: none;
}
.search-results li:hover,
.search-results li.active {
background: var(--bg);
}
.search-results li .search-result-title {
color: var(--text-bright);
font-weight: 600;
font-size: 0.9rem;
margin-bottom: 2px;
}
.search-results li .search-result-snippet {
color: var(--text-muted);
font-size: 0.8rem;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.search-results li .search-result-snippet mark {
background: rgba(255, 215, 0, 0.3);
color: var(--text-bright);
border-radius: 2px;
padding: 0 1px;
}
[data-theme="light"] .search-results li .search-result-snippet mark {
background: rgba(255, 200, 0, 0.4);
color: var(--text);
}
.search-no-results {
padding: 24px 16px;
text-align: center;
color: var(--text-muted);
font-size: 0.9rem;
}
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+297
View File
@@ -0,0 +1,297 @@
// Language selector
(function () {
var btn = document.querySelector('.lang-btn');
var popup = document.querySelector('.lang-popup');
if (!btn || !popup) return;
btn.addEventListener('click', function (e) {
e.stopPropagation();
popup.classList.toggle('open');
});
document.addEventListener('click', function () {
popup.classList.remove('open');
});
popup.addEventListener('click', function (e) {
var link = e.target.closest('[data-lang]');
if (!link) return;
e.preventDefault();
var lang = link.getAttribute('data-lang');
localStorage.setItem('preferred-lang', lang);
var basePath = document.documentElement.getAttribute('data-base-path') || '';
var path = window.location.pathname;
// Strip base path prefix, replace lang, then re-add base path
var pathWithoutBase = path.slice(basePath.length);
var newPath = basePath + pathWithoutBase.replace(/^\/[a-z]{2}\//, '/' + lang + '/');
window.location.href = newPath;
});
})();
// Theme toggle
(function () {
var btn = document.querySelector('.theme-toggle');
if (!btn) return;
// Feather Icons: sun (light mode) and moon (dark mode)
var sunSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
var moonSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
function getTheme() {
var stored = localStorage.getItem('preferred-theme');
if (stored) return stored;
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
}
function applyTheme(theme) {
if (theme === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
} else {
document.documentElement.removeAttribute('data-theme');
}
btn.innerHTML = theme === 'light' ? sunSVG : moonSVG;
}
applyTheme(getTheme());
btn.addEventListener('click', function () {
var current = getTheme();
var next = current === 'dark' ? 'light' : 'dark';
localStorage.setItem('preferred-theme', next);
applyTheme(next);
});
})();
// Mobile sidebar toggle
(function () {
var toggle = document.querySelector('.sidebar-toggle');
var sidebar = document.querySelector('.sidebar');
if (!toggle || !sidebar) return;
toggle.addEventListener('click', function () {
sidebar.classList.toggle('open');
});
document.addEventListener('click', function (e) {
if (!sidebar.contains(e.target) && e.target !== toggle) {
sidebar.classList.remove('open');
}
});
})();
// Site search (⌘K / Ctrl+K)
(function () {
var overlay = document.getElementById('search-overlay');
var input = document.getElementById('search-input');
var resultsList = document.getElementById('search-results');
if (!overlay || !input || !resultsList) return;
var searchBtn = document.querySelector('.search-btn');
var pagesData = null; // cached pages-data.json
var activeIndex = -1;
function getCurrentLang() {
return document.documentElement.getAttribute('lang') || 'en';
}
function getBasePath() {
return document.documentElement.getAttribute('data-base-path') || '';
}
function openSearch() {
overlay.classList.add('open');
input.value = '';
resultsList.innerHTML = '';
activeIndex = -1;
input.focus();
loadPagesData();
}
function closeSearch() {
overlay.classList.remove('open');
input.value = '';
resultsList.innerHTML = '';
activeIndex = -1;
}
function loadPagesData() {
if (pagesData) return;
var basePath = getBasePath();
fetch(basePath + '/pages-data.json')
.then(function (res) { return res.json(); })
.then(function (data) { pagesData = data; })
.catch(function () { pagesData = []; });
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlightText(text, query) {
if (!query) return text;
var escaped = escapeRegExp(query);
var re = new RegExp('(' + escaped + ')', 'gi');
return text.replace(re, '<mark>$1</mark>');
}
function buildSnippet(body, query) {
if (!query || !body) return '';
var lower = body.toLowerCase();
var idx = lower.indexOf(query.toLowerCase());
var start, end, snippet;
if (idx === -1) {
snippet = body.substring(0, 120);
} else {
start = Math.max(0, idx - 40);
end = Math.min(body.length, idx + query.length + 80);
snippet = (start > 0 ? '...' : '') + body.substring(start, end) + (end < body.length ? '...' : '');
}
return highlightText(snippet, query);
}
function search(query) {
if (!pagesData || !query) {
resultsList.innerHTML = '';
activeIndex = -1;
return;
}
var lang = getCurrentLang();
var q = query.toLowerCase();
// Score and filter
var scored = [];
for (var i = 0; i < pagesData.length; i++) {
var page = pagesData[i];
if (page.lang !== lang) continue;
var score = 0;
var titleLower = page.title.toLowerCase();
var bodyLower = (page.body || '').toLowerCase();
if (titleLower.indexOf(q) !== -1) {
score += 10;
// Bonus for exact title match
if (titleLower === q) score += 5;
}
if (bodyLower.indexOf(q) !== -1) {
score += 3;
}
if (page.section.toLowerCase().indexOf(q) !== -1) {
score += 1;
}
if (score > 0) {
scored.push({ page: page, score: score });
}
}
// Sort by score descending
scored.sort(function (a, b) { return b.score - a.score; });
// Limit results
var results = scored.slice(0, 20);
if (results.length === 0) {
resultsList.innerHTML = '<li class="search-no-results">No results found.</li>';
activeIndex = -1;
return;
}
var html = '';
for (var j = 0; j < results.length; j++) {
var r = results[j];
var snippet = buildSnippet(r.page.body, query);
html += '<li data-url="' + r.page.url + '">'
+ '<div class="search-result-title">' + highlightText(r.page.title, query) + '</div>'
+ (snippet ? '<div class="search-result-snippet">' + snippet + '</div>' : '')
+ '</li>';
}
resultsList.innerHTML = html;
activeIndex = -1;
}
function setActive(index) {
var items = resultsList.querySelectorAll('li[data-url]');
if (items.length === 0) return;
// Remove previous active
for (var i = 0; i < items.length; i++) {
items[i].classList.remove('active');
}
if (index < 0) index = items.length - 1;
if (index >= items.length) index = 0;
activeIndex = index;
items[activeIndex].classList.add('active');
items[activeIndex].scrollIntoView({ block: 'nearest' });
}
function navigateToActive() {
var items = resultsList.querySelectorAll('li[data-url]');
if (activeIndex >= 0 && activeIndex < items.length) {
var url = items[activeIndex].getAttribute('data-url');
if (url) {
closeSearch();
window.location.href = url;
}
}
}
// Event: search button
if (searchBtn) {
searchBtn.addEventListener('click', function (e) {
e.stopPropagation();
openSearch();
});
}
// Use capture phase to intercept keys before browser default behavior
// (e.g. ESC clearing input text in some browsers)
document.addEventListener('keydown', function (e) {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
overlay.classList.contains('open') ? closeSearch() : openSearch();
return;
}
if (!overlay.classList.contains('open')) return;
if (e.key === 'Escape') {
e.preventDefault();
closeSearch();
} else if (e.key === 'ArrowDown') {
e.preventDefault();
setActive(activeIndex + 1);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setActive(activeIndex - 1);
} else if (e.key === 'Enter') {
e.preventDefault();
navigateToActive();
}
}, true); // capture phase
// Event: click overlay background to close
overlay.addEventListener('click', function (e) {
if (e.target === overlay) {
closeSearch();
}
});
// Event: click result item
resultsList.addEventListener('click', function (e) {
var li = e.target.closest('li[data-url]');
if (!li) return;
var url = li.getAttribute('data-url');
if (url) {
closeSearch();
window.location.href = url;
}
});
// Event: input for live search
var debounceTimer = null;
input.addEventListener('input', function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function () {
search(input.value.trim());
}, 150);
});
})();
+88
View File
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="{{ lang }}" data-base-path="{{ site.base_path }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.title }} - {{ site.title }}</title>
<link rel="icon" type="image/svg+xml" href="{{ site.base_path }}/favicon.svg">
<link rel="stylesheet" href="{{ site.base_path }}/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="{{ site.base_path }}/{{ lang }}/" class="header-title">{{ site.title }}{% if site.version %} <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v{{ site.version }}</span>{% endif %}</a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="{{ site.base_path }}/{{ lang }}/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
{% for link in site.nav %}
{% if link.url %}
<a href="{{ link.url }}"{% if link.icon_svg %} aria-label="{{ link.label }}"{% endif %}>
{% if link.icon_svg %}{{ link.icon_svg | safe }}{% endif %}
{{ link.label }}
</a>
{% elif link.path %}
<a href="{{ site.base_path }}/{{ lang }}/{{ link.path }}">
{% if link.icon_svg %}{{ link.icon_svg | safe }}{% endif %}
{{ link.label }}
</a>
{% endif %}
{% endfor %}
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
{{ lang | upper }}
</button>
<ul class="lang-popup">
{% for l in site.langs %}
<li><a href="#" data-lang="{{ l }}">{{ l | upper }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% block sidebar_toggle %}{% endblock %}
</div>
</header>
{% if page.status == "draft" %}
<div class="draft-banner">DRAFT</div>
{% endif %}
<div class="layout {% block layout_class %}{% endblock %}">
{% block body %}{% endblock %}
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="{{ site.base_path }}/js/main.js"></script>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block layout_class %}has-sidebar{% endblock %}
{% block sidebar_toggle %}<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>{% endblock %}
{% block body %}
<aside class="sidebar">
<nav class="sidebar-nav">
{% for section in nav %}
<div class="nav-section">
<a href="{{ section.url }}" class="nav-section-title {% if section.active %}active{% endif %}">{{ section.title }}</a>
{% if section.children %}
<ul class="nav-list">
{% for item in section.children %}
<li><a href="{{ item.url }}" class="{% if item.active %}active{% endif %}">{{ item.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
</nav>
</aside>
<main class="content">
<article>
<h1>{{ page.title }}</h1>
{{ content | safe }}
</article>
</main>
{% endblock %}
+12
View File
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block layout_class %}no-sidebar{% endblock %}
{% block body %}
<main class="content portal">
<article>
<h1>{{ page.title }}</h1>
{{ content | safe }}
</article>
</main>
{% endblock %}
+468
View File
@@ -0,0 +1,468 @@
use crate::config::{NavLink, SiteConfig};
use crate::defaults;
use crate::markdown::{Frontmatter, MarkdownRenderer};
use crate::utils::copy_dir_recursive;
use anyhow::{Context, Result};
use serde::Serialize;
use std::fs;
use std::path::{Path, PathBuf};
use tera::Tera;
use walkdir::WalkDir;
#[derive(Debug, Serialize)]
struct PageContext {
title: String,
url: String,
status: Option<String>,
}
/// Entry for pages-data.json used by client-side search.
#[derive(Debug, Serialize)]
struct PageDataEntry {
title: String,
url: String,
lang: String,
section: String,
/// Plain-text body with HTML tags stripped (truncated to ~500 chars).
body: String,
}
#[derive(Debug, Serialize, Clone)]
struct NavItem {
title: String,
url: String,
children: Vec<NavItem>,
active: bool,
}
#[derive(Debug, Serialize)]
struct SiteContext {
title: String,
version: Option<String>,
base_url: String,
base_path: String,
langs: Vec<String>,
nav: Vec<NavLink>,
}
struct Page {
frontmatter: Frontmatter,
html_content: String,
url: String,
out_path: PathBuf,
rel_path: String,
section: String,
}
pub fn build(src: &Path, out: &Path) -> Result<()> {
let config = SiteConfig::load(src)?;
let renderer = MarkdownRenderer::new(
config.highlight_dark_theme(),
config.highlight_light_theme(),
);
// Build Tera: start with embedded defaults, then override with user templates
let tera = build_tera(src)?;
// Clean output directory
if out.exists() {
fs::remove_dir_all(out).context("Failed to clean output directory")?;
}
fs::create_dir_all(out)?;
// Copy static files: embedded defaults first, then user overrides on top
copy_default_static(out)?;
let static_dir = src.join("static");
if static_dir.exists() {
copy_dir_recursive(&static_dir, out)?;
}
// Collect page data entries for pages-data.json across all languages
let mut page_data_entries: Vec<PageDataEntry> = Vec::new();
// Build each language
for lang in &config.i18n.langs {
let pages_dir = src.join("pages").join(lang);
if !pages_dir.exists() {
eprintln!("Warning: pages directory not found for lang '{}', skipping", lang);
continue;
}
let pages = collect_pages(&pages_dir, lang, out, &renderer, &config.site.base_path)?;
let nav = build_nav(&pages);
for page in &pages {
// Collect search data for pages-data.json
let plain_body = strip_html_tags(&remove_light_theme_blocks(&page.html_content));
let truncated_body: String = plain_body.chars().take(500).collect();
page_data_entries.push(PageDataEntry {
title: page.frontmatter.title.clone(),
url: page.url.clone(),
lang: lang.clone(),
section: page.section.clone(),
body: truncated_body,
});
let template_name = if page.section.is_empty() {
"portal.html"
} else {
"page.html"
};
// Filter nav to only the current section
let section_nav: Vec<&NavItem> = nav
.iter()
.filter(|item| {
let item_section = extract_section(&item.url, &config.site.base_path);
item_section == page.section
})
.collect();
let mut ctx = tera::Context::new();
ctx.insert("page", &PageContext {
title: page.frontmatter.title.clone(),
url: page.url.clone(),
status: page.frontmatter.status.clone(),
});
ctx.insert("content", &page.html_content);
ctx.insert("lang", lang);
ctx.insert("site", &SiteContext {
title: config.site.title.clone(),
version: config.site.version.clone(),
base_url: config.site.base_url(),
base_path: config.site.base_path.clone(),
langs: config.i18n.langs.clone(),
nav: config.nav.clone(),
});
// Set active state and pass nav
let mut nav_with_active: Vec<NavItem> = section_nav
.into_iter()
.cloned()
.map(|mut item| {
set_active(&mut item, &page.url);
item
})
.collect();
// If we're on a section index page, expand its children
if let Some(item) = nav_with_active.first_mut() {
if item.url == page.url {
item.active = true;
}
}
ctx.insert("nav", &nav_with_active);
let html = tera
.render(template_name, &ctx)
.with_context(|| format!("Failed to render template for {}", page.url))?;
if let Some(parent) = page.out_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&page.out_path, html)?;
}
}
// Generate pages-data.json for client-side search
let pages_json = serde_json::to_string(&page_data_entries)
.context("Failed to serialize pages-data.json")?;
fs::write(out.join("pages-data.json"), pages_json)?;
// Generate root redirect
generate_root_redirect(out, &config)?;
println!(
"Site generated: {} languages, output at {}",
config.i18n.langs.len(),
out.display()
);
Ok(())
}
fn collect_pages(
pages_dir: &Path,
lang: &str,
out: &Path,
renderer: &MarkdownRenderer,
base_path: &str,
) -> Result<Vec<Page>> {
let mut pages = Vec::new();
for entry in WalkDir::new(pages_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| {
e.path().extension().map_or(false, |ext| ext == "md")
})
{
let path = entry.path();
let content = fs::read_to_string(path)
.with_context(|| format!("Failed to read {}", path.display()))?;
let (frontmatter, body) = MarkdownRenderer::parse_frontmatter(&content)
.with_context(|| format!("Failed to parse frontmatter in {}", path.display()))?;
let html_content = renderer.render(body);
let rel = path.strip_prefix(pages_dir)?;
let rel_str = rel.to_string_lossy().to_string();
// Compute URL and output path
let (url, out_path) = if rel.file_name().map_or(false, |f| f == "index.md") {
// index.md -> <base_path>/<lang>/dir/
let parent = rel.parent().unwrap_or(Path::new(""));
if parent.as_os_str().is_empty() {
// Root index.md
(
format!("{}/{}/", base_path, lang),
out.join(lang).join("index.html"),
)
} else {
(
format!("{}/{}/{}/", base_path, lang, parent.display()),
out.join(lang).join(parent).join("index.html"),
)
}
} else {
// foo.md -> <base_path>/<lang>/foo/
let stem = rel.with_extension("");
(
format!("{}/{}/{}/", base_path, lang, stem.display()),
out.join(lang).join(&stem).join("index.html"),
)
};
let section = extract_section(&url, base_path);
pages.push(Page {
frontmatter,
html_content,
url,
out_path,
rel_path: rel_str,
section,
});
}
Ok(pages)
}
fn extract_section(url: &str, base_path: &str) -> String {
// Strip base_path prefix before parsing
let stripped = url.strip_prefix(base_path).unwrap_or(url);
// URL format: /<lang>/ or /<lang>/section/...
let parts: Vec<&str> = stripped.trim_matches('/').split('/').collect();
if parts.len() >= 2 {
parts[1].to_string()
} else {
String::new()
}
}
fn build_nav(pages: &[Page]) -> Vec<NavItem> {
// Group pages by section (top-level directory)
let mut sections: std::collections::BTreeMap<String, Vec<&Page>> =
std::collections::BTreeMap::new();
for page in pages {
if page.section.is_empty() {
continue; // Skip root index (portal)
}
sections
.entry(page.section.clone())
.or_default()
.push(page);
}
let mut nav = Vec::new();
for (section, mut section_pages) in sections {
// Sort by order, then by filename
section_pages.sort_by(|a, b| {
a.frontmatter
.order
.cmp(&b.frontmatter.order)
.then_with(|| a.rel_path.cmp(&b.rel_path))
});
// Find the section index page
let index_page = section_pages
.iter()
.find(|p| p.rel_path.ends_with("index.md") && p.section == section);
let section_title = index_page
.map(|p| p.frontmatter.title.clone())
.unwrap_or_else(|| section.clone());
let section_url = index_page
.map(|p| p.url.clone())
.unwrap_or_default();
let children: Vec<NavItem> = section_pages
.iter()
.filter(|p| !p.rel_path.ends_with("index.md") || p.section != section)
.map(|p| NavItem {
title: p.frontmatter.title.clone(),
url: p.url.clone(),
children: Vec::new(),
active: false,
})
.collect();
nav.push(NavItem {
title: section_title,
url: section_url,
children,
active: false,
});
}
// Sort nav sections by order of their index pages
nav
}
fn set_active(item: &mut NavItem, current_url: &str) {
if item.url == current_url {
item.active = true;
}
for child in &mut item.children {
set_active(child, current_url);
if child.active {
item.active = true;
}
}
}
fn generate_root_redirect(out: &Path, config: &SiteConfig) -> Result<()> {
let base_path = &config.site.base_path;
let html = format!(
r#"<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
(function() {{
var lang = localStorage.getItem('preferred-lang') || '{default_lang}';
window.location.replace('{base_path}/' + lang + '/');
}})();
</script>
<meta http-equiv="refresh" content="0;url={base_path}/{default_lang}/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="{base_path}/{default_lang}/">{base_path}/{default_lang}/</a>...</p>
</body>
</html>"#,
default_lang = config.i18n.default_lang(),
base_path = base_path,
);
fs::write(out.join("index.html"), html)?;
Ok(())
}
/// Build Tera with embedded default templates, then override with any files
/// found in `<src>/templates/`.
fn build_tera(src: &Path) -> Result<Tera> {
let mut tera = Tera::default();
// Register embedded defaults
for (name, source) in defaults::default_templates() {
tera.add_raw_template(name, source)
.with_context(|| format!("Failed to add default template '{}'", name))?;
}
// Override with user-provided templates (if any)
let templates_dir = src.join("templates");
if templates_dir.exists() {
for entry in WalkDir::new(&templates_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map_or(false, |ext| ext == "html"))
{
let path = entry.path();
let rel = path.strip_prefix(&templates_dir)?;
let name = rel.to_string_lossy().replace('\\', "/");
let source = fs::read_to_string(path)
.with_context(|| format!("Failed to read template {}", path.display()))?;
tera.add_raw_template(&name, &source)
.with_context(|| format!("Failed to register template '{}'", name))?;
}
}
Ok(tera)
}
/// Write embedded default static files (css/js) to the output directory.
fn copy_default_static(out: &Path) -> Result<()> {
for (rel_path, content) in defaults::default_static_files() {
let target = out.join(rel_path);
if let Some(parent) = target.parent() {
fs::create_dir_all(parent)?;
}
// Only write if not already present (user file takes precedence via
// the subsequent copy_dir_recursive call, but write defaults first)
fs::write(&target, content)?;
}
Ok(())
}
/// Strip HTML tags from a string and collapse whitespace into a single space,
/// producing a plain-text representation suitable for search indexing.
fn strip_html_tags(html: &str) -> String {
let mut result = String::with_capacity(html.len());
let mut in_tag = false;
for ch in html.chars() {
match ch {
'<' => in_tag = true,
'>' => {
in_tag = false;
// Insert a space to avoid words being glued across tags
result.push(' ');
}
_ if !in_tag => result.push(ch),
_ => {}
}
}
// Collapse whitespace
let collapsed: String = result.split_whitespace().collect::<Vec<_>>().join(" ");
collapsed
}
/// Remove `<div data-code-theme="light">...</div>` blocks so that
/// dual-theme code snippets are only indexed once.
fn remove_light_theme_blocks(html: &str) -> String {
const MARKER: &str = "<div data-code-theme=\"light\"";
let mut result = String::with_capacity(html.len());
let mut remaining = html;
while let Some(start) = remaining.find(MARKER) {
result.push_str(&remaining[..start]);
remaining = &remaining[start..];
let mut depth: usize = 0;
let mut i = 0;
while i < remaining.len() {
if remaining[i..].starts_with("<div") {
depth += 1;
i += 4;
} else if remaining[i..].starts_with("</div>") {
depth -= 1;
i += 6;
if depth == 0 {
break;
}
} else {
i += remaining[i..].chars().next().map_or(1, |c| c.len_utf8());
}
}
remaining = &remaining[i..];
}
result.push_str(remaining);
result
}
+97
View File
@@ -0,0 +1,97 @@
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Deserialize)]
pub struct SiteConfig {
pub site: Site,
pub i18n: I18n,
pub highlight: Option<Highlight>,
#[serde(default)]
pub nav: Vec<NavLink>,
}
/// A navigation link entry defined in config.toml under [[nav]].
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct NavLink {
pub label: String,
/// Absolute or external URL (e.g. GitHub link).
pub url: Option<String>,
/// Path relative to /<base_path>/<lang>/ (e.g. "tour/").
pub path: Option<String>,
/// Optional inline SVG string to display as an icon.
pub icon_svg: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct Site {
pub title: String,
pub version: Option<String>,
/// Optional hostname (e.g. "https://example.github.io"). Combined with
/// base_path to form the full base URL.
pub hostname: Option<String>,
#[serde(default)]
pub base_path: String,
}
impl Site {
/// Returns the full base URL derived from hostname + base_path.
/// Falls back to base_path alone if hostname is not set.
pub fn base_url(&self) -> String {
match &self.hostname {
Some(h) => format!("{}{}", h.trim_end_matches('/'), self.base_path),
None => self.base_path.clone(),
}
}
}
#[derive(Debug, Deserialize)]
pub struct I18n {
pub langs: Vec<String>,
}
impl I18n {
/// Returns the default language, which is the first entry in langs.
pub fn default_lang(&self) -> &str {
self.langs.first().map(|s| s.as_str()).unwrap_or("en")
}
}
#[derive(Debug, Deserialize)]
pub struct Highlight {
pub dark_theme: Option<String>,
pub light_theme: Option<String>,
}
impl SiteConfig {
pub fn load(src_dir: &Path) -> Result<Self> {
let path = src_dir.join("config.toml");
let content =
std::fs::read_to_string(&path).with_context(|| format!("Failed to read {}", path.display()))?;
let mut config: SiteConfig =
toml::from_str(&content).with_context(|| format!("Failed to parse {}", path.display()))?;
// Validate required fields
if config.i18n.langs.is_empty() {
anyhow::bail!("[i18n] langs must not be empty. Please specify at least one language.");
}
// Normalize base_path: strip trailing slash (but keep empty for root)
let bp = config.site.base_path.trim_end_matches('/').to_string();
config.site.base_path = bp;
Ok(config)
}
pub fn highlight_dark_theme(&self) -> &str {
self.highlight
.as_ref()
.and_then(|h| h.dark_theme.as_deref())
.unwrap_or("base16-ocean.dark")
}
pub fn highlight_light_theme(&self) -> Option<&str> {
self.highlight
.as_ref()
.and_then(|h| h.light_theme.as_deref())
}
}
+47
View File
@@ -0,0 +1,47 @@
// Default embedded theme files. Users can override any of these by placing
// a file with the same name under their <SRC>/templates/ or <SRC>/static/.
pub const TEMPLATE_BASE: &str = include_str!("../defaults/templates/base.html");
pub const TEMPLATE_PAGE: &str = include_str!("../defaults/templates/page.html");
pub const TEMPLATE_PORTAL: &str = include_str!("../defaults/templates/portal.html");
pub const STATIC_CSS_MAIN: &str = include_str!("../defaults/static/css/main.css");
pub const STATIC_JS_MAIN: &str = include_str!("../defaults/static/js/main.js");
pub const STATIC_FAVICON_SVG: &str = include_str!("../defaults/static/favicon.svg");
// Init command templates
pub const INIT_CONFIG_TOML: &str = include_str!("../defaults/config.toml");
pub const INIT_PAGE_EN_INDEX: &str = include_str!("../defaults/pages/en/index.md");
pub const INIT_PAGE_JA_INDEX: &str = include_str!("../defaults/pages/ja/index.md");
/// Returns all default templates as (name, source) pairs for Tera registration.
pub fn default_templates() -> Vec<(&'static str, &'static str)> {
vec![
("base.html", TEMPLATE_BASE),
("page.html", TEMPLATE_PAGE),
("portal.html", TEMPLATE_PORTAL),
]
}
/// Returns all default static files as (relative_path, content) pairs.
pub fn default_static_files() -> Vec<(&'static str, &'static str)> {
vec![
("css/main.css", STATIC_CSS_MAIN),
("js/main.js", STATIC_JS_MAIN),
("favicon.svg", STATIC_FAVICON_SVG),
]
}
/// Returns all init scaffold files as (relative_path, content) pairs.
pub fn init_files() -> Vec<(&'static str, &'static str)> {
vec![
("config.toml", INIT_CONFIG_TOML),
("templates/base.html", TEMPLATE_BASE),
("templates/page.html", TEMPLATE_PAGE),
("templates/portal.html", TEMPLATE_PORTAL),
("static/css/main.css", STATIC_CSS_MAIN),
("static/js/main.js", STATIC_JS_MAIN),
("pages/en/index.md", INIT_PAGE_EN_INDEX),
("pages/ja/index.md", INIT_PAGE_JA_INDEX),
]
}
+99
View File
@@ -0,0 +1,99 @@
mod builder;
mod config;
mod defaults;
mod markdown;
mod serve;
mod utils;
use anyhow::Result;
use clap::{Parser, Subcommand, CommandFactory};
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Parser)]
#[command(version, about = "A simple static site generator")]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
/// Source directory containing config.toml (used when no subcommand given)
#[arg(default_value = ".")]
src: PathBuf,
/// Output directory (used when no subcommand given)
#[arg(long, default_value = "docs")]
out: PathBuf,
}
#[derive(Subcommand)]
enum Command {
/// Build the documentation site
Build {
/// Source directory containing config.toml
#[arg(default_value = ".")]
src: PathBuf,
/// Output directory
#[arg(long, default_value = "docs")]
out: PathBuf,
},
/// Initialize a new docs project with default scaffold files
Init {
/// Target directory to initialize (default: current directory)
#[arg(default_value = ".")]
src: PathBuf,
},
/// Start a local development server with live-reload
Serve {
/// Source directory containing config.toml
#[arg(default_value = ".")]
src: PathBuf,
/// Port number for the HTTP server
#[arg(long, default_value = "8080")]
port: u16,
/// Open browser automatically
#[arg(long)]
open: bool,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Some(Command::Build { src, out }) => builder::build(&src, &out),
Some(Command::Init { src }) => cmd_init(&src),
Some(Command::Serve { src, port, open }) => serve::serve(&src, port, open),
None => {
Cli::command().print_help()?;
println!();
Ok(())
}
}
}
fn cmd_init(target: &Path) -> Result<()> {
let mut skipped = 0usize;
let mut created = 0usize;
for (rel_path, content) in defaults::init_files() {
let dest = target.join(rel_path);
if dest.exists() {
eprintln!("Skipping (already exists): {}", dest.display());
skipped += 1;
continue;
}
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&dest, content)?;
println!("Created: {}", dest.display());
created += 1;
}
println!("\nInit complete: {} file(s) created, {} skipped.", created, skipped);
Ok(())
}
+145
View File
@@ -0,0 +1,145 @@
use anyhow::{Context, Result};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
use serde::Deserialize;
use syntect::highlighting::ThemeSet;
use syntect::html::highlighted_html_for_string;
use syntect::parsing::SyntaxSet;
#[derive(Debug, Deserialize)]
pub struct Frontmatter {
pub title: String,
#[serde(default)]
pub order: i32,
pub status: Option<String>,
}
pub struct MarkdownRenderer {
syntax_set: SyntaxSet,
theme_set: ThemeSet,
dark_theme: String,
light_theme: Option<String>,
}
impl MarkdownRenderer {
pub fn new(dark_theme: &str, light_theme: Option<&str>) -> Self {
Self {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
dark_theme: dark_theme.to_string(),
light_theme: light_theme.map(|s| s.to_string()),
}
}
pub fn parse_frontmatter(content: &str) -> Result<(Frontmatter, &str)> {
let content = content.trim_start();
if !content.starts_with("---") {
anyhow::bail!("Missing frontmatter delimiter");
}
let after_first = &content[3..];
let end = after_first
.find("\n---")
.context("Missing closing frontmatter delimiter")?;
let yaml = &after_first[..end];
let body = &after_first[end + 4..];
let fm: Frontmatter =
serde_yaml::from_str(yaml).context("Failed to parse frontmatter YAML")?;
Ok((fm, body))
}
pub fn render(&self, markdown: &str) -> String {
let options = Options::ENABLE_TABLES
| Options::ENABLE_STRIKETHROUGH
| Options::ENABLE_TASKLISTS;
let parser = Parser::new_ext(markdown, options);
let mut in_code_block = false;
let mut code_lang = String::new();
let mut code_buf = String::new();
let mut events: Vec<Event> = Vec::new();
for event in parser {
match event {
Event::Start(Tag::CodeBlock(kind)) => {
in_code_block = true;
code_buf.clear();
code_lang = match kind {
CodeBlockKind::Fenced(lang) => lang.to_string(),
CodeBlockKind::Indented => String::new(),
};
}
Event::End(TagEnd::CodeBlock) => {
in_code_block = false;
let html = self.highlight_code(&code_buf, &code_lang);
events.push(Event::Html(html.into()));
}
Event::Text(text) if in_code_block => {
code_buf.push_str(&text);
}
other => events.push(other),
}
}
let mut html_output = String::new();
pulldown_cmark::html::push_html(&mut html_output, events.into_iter());
html_output
}
fn highlight_code(&self, code: &str, lang: &str) -> String {
let syntax = if lang.is_empty() {
self.syntax_set.find_syntax_plain_text()
} else {
self.syntax_set
.find_syntax_by_token(lang)
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
};
let dark_html = self.highlight_with_theme(code, syntax, &self.dark_theme);
match &self.light_theme {
Some(light) => {
let light_html = self.highlight_with_theme(code, syntax, light);
format!(
concat!(
"<div class=\"code-block-wrapper\">",
"<div data-code-theme=\"dark\">{}</div>",
"<div data-code-theme=\"light\">{}</div>",
"</div>",
),
dark_html, light_html
)
}
None => dark_html,
}
}
fn highlight_with_theme(
&self,
code: &str,
syntax: &syntect::parsing::SyntaxReference,
theme_name: &str,
) -> String {
let theme = self
.theme_set
.themes
.get(theme_name)
.unwrap_or_else(|| {
self.theme_set
.themes
.values()
.next()
.expect("No themes available")
});
match highlighted_html_for_string(code, &self.syntax_set, syntax, theme) {
Ok(html) => html,
Err(_) => format!("<pre><code>{}</code></pre>", escape_html(code)),
}
}
}
fn escape_html(s: &str) -> String {
s.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
}
+340
View File
@@ -0,0 +1,340 @@
use crate::builder;
use crate::config::SiteConfig;
use crate::utils::copy_dir_recursive;
use anyhow::{Context, Result};
use notify::{Event, RecursiveMode, Watcher};
use socket2::{Domain, Protocol, Socket, Type};
use std::fs;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::path::Path;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use walkdir::WalkDir;
/// Live-reload WebSocket script injected into every HTML page during serve.
const LIVE_RELOAD_SCRIPT: &str = r#"<script>
(function() {
var ws = new WebSocket('ws://' + location.hostname + ':{{WS_PORT}}');
ws.onmessage = function(e) { if (e.data === 'reload') location.reload(); };
ws.onclose = function() {
setTimeout(function() { location.reload(); }, 2000);
};
})();
</script>"#;
/// Run the serve command: build, start HTTP + WebSocket servers, watch for changes.
pub fn serve(src: &Path, port: u16, open_browser: bool) -> Result<()> {
let config = SiteConfig::load(src)?;
let base_path = config.site.base_path.clone();
let ws_port = port + 1;
// Create temp directory for serving
let tmp_dir = tempfile::tempdir().context("Failed to create temp directory")?;
let serve_root = tmp_dir.path().to_path_buf();
println!("Serving from temp directory: {}", serve_root.display());
// Initial build
build_and_copy(src, &serve_root, &base_path, ws_port)?;
// Track connected WebSocket clients
let clients: Arc<Mutex<Vec<TcpStream>>> = Arc::new(Mutex::new(Vec::new()));
// Create HTTP and WebSocket listeners upfront with SO_REUSEADDR
// so that rapid restarts (after Ctrl+C) don't hit "address in use".
let http_listener = create_reuse_listener(port)
.with_context(|| format!("Failed to bind HTTP server to port {}", port))?;
let ws_listener = create_reuse_listener(ws_port)
.with_context(|| format!("Failed to bind WebSocket server to port {}", ws_port))?;
// Start WebSocket server for live-reload notifications
let ws_clients = clients.clone();
thread::spawn(move || {
if let Err(e) = run_ws_server(ws_listener, ws_clients) {
eprintln!("WebSocket server error: {}", e);
}
});
// Start HTTP server
let http_root = serve_root.clone();
thread::spawn(move || {
if let Err(e) = run_http_server(http_listener, &http_root) {
eprintln!("HTTP server error: {}", e);
}
});
let url = if base_path.is_empty() {
format!("http://localhost:{}/", port)
} else {
format!("http://localhost:{}{}/", port, base_path)
};
println!("\n Local: {}", url);
println!(" Press Ctrl+C to stop.\n");
if open_browser {
let _ = open::that(&url);
}
// File watcher
let (tx, rx) = mpsc::channel();
let mut watcher = notify::recommended_watcher(move |res: Result<Event, notify::Error>| {
if let Ok(event) = res {
if event.kind.is_modify() || event.kind.is_create() || event.kind.is_remove() {
let _ = tx.send(());
}
}
})?;
let src_abs = fs::canonicalize(src)?;
watcher.watch(&src_abs, RecursiveMode::Recursive)?;
println!("Watching for changes in {}...", src_abs.display());
// Debounce: wait for changes, then rebuild
loop {
// Block until a change notification arrives
if rx.recv().is_err() {
break;
}
// Drain any additional events within a short debounce window
thread::sleep(Duration::from_millis(200));
while rx.try_recv().is_ok() {}
println!("Change detected, rebuilding...");
match build_and_copy(src, &serve_root, &base_path, ws_port) {
Ok(()) => {
println!("Rebuild complete. Notifying browser...");
notify_clients(&clients);
}
Err(e) => {
eprintln!("Rebuild failed: {}", e);
}
}
}
Ok(())
}
/// Build site into a temp build dir, then copy to serve_root/<base_path>/
/// with live-reload script injected.
fn build_and_copy(src: &Path, serve_root: &Path, base_path: &str, ws_port: u16) -> Result<()> {
// Build into a temporary output directory
let build_tmp = tempfile::tempdir().context("Failed to create build temp dir")?;
let build_out = build_tmp.path();
builder::build(src, build_out)?;
// Determine the target directory under serve_root
let target = if base_path.is_empty() {
serve_root.to_path_buf()
} else {
let bp = base_path.trim_start_matches('/');
serve_root.join(bp)
};
// Clean target and copy
if target.exists() {
fs::remove_dir_all(&target).ok();
}
copy_dir_recursive(build_out, &target)?;
// Inject live-reload script into all HTML files
inject_live_reload(&target, ws_port)?;
Ok(())
}
/// Inject live-reload WebSocket script into all HTML files under dir.
fn inject_live_reload(dir: &Path, ws_port: u16) -> Result<()> {
let script = LIVE_RELOAD_SCRIPT.replace("{{WS_PORT}}", &ws_port.to_string());
for entry in WalkDir::new(dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| {
e.path()
.extension()
.map_or(false, |ext| ext == "html")
})
{
let path = entry.path();
let content = fs::read_to_string(path)?;
if let Some(pos) = content.rfind("</body>") {
let injected = format!("{}{}{}", &content[..pos], script, &content[pos..]);
fs::write(path, injected)?;
}
}
Ok(())
}
/// Simple HTTP static file server using tiny_http.
fn run_http_server(listener: TcpListener, root: &Path) -> Result<()> {
let server = tiny_http::Server::from_listener(listener, None)
.map_err(|e| anyhow::anyhow!("HTTP server: {}", e))?;
for request in server.incoming_requests() {
let url_path = percent_decode(request.url());
let rel = url_path.trim_start_matches('/');
let file_path = if rel.is_empty() {
root.join("index.html")
} else {
let candidate = root.join(rel);
if candidate.is_dir() {
candidate.join("index.html")
} else {
candidate
}
};
if file_path.exists() && file_path.is_file() {
let content = fs::read(&file_path).unwrap_or_default();
let mime = guess_mime(&file_path);
let response = tiny_http::Response::from_data(content)
.with_header(
tiny_http::Header::from_bytes(&b"Content-Type"[..], mime.as_bytes()).unwrap(),
);
let _ = request.respond(response);
} else {
let response = tiny_http::Response::from_string("404 Not Found")
.with_status_code(404);
let _ = request.respond(response);
}
}
Ok(())
}
/// WebSocket server that accepts connections and stores them for later notification.
fn run_ws_server(listener: TcpListener, clients: Arc<Mutex<Vec<TcpStream>>>) -> Result<()> {
for stream in listener.incoming().flatten() {
let clients = clients.clone();
thread::spawn(move || {
if let Ok(ws) = tungstenite::accept(stream.try_clone().unwrap()) {
// Store the underlying TCP stream for later notification
if let Ok(mut list) = clients.lock() {
list.push(stream);
}
// Keep the WebSocket connection alive - read until closed
let mut ws = ws;
loop {
match ws.read() {
Ok(msg) => {
if msg.is_close() {
break;
}
}
Err(_) => break,
}
}
}
});
}
Ok(())
}
/// Send "reload" to all connected WebSocket clients.
fn notify_clients(clients: &Arc<Mutex<Vec<TcpStream>>>) {
if let Ok(mut list) = clients.lock() {
let mut alive = Vec::new();
for stream in list.drain(..) {
if stream.try_clone().is_ok() {
// Re-wrap as WebSocket and send reload message
// Since we can't easily re-wrap existing TCP streams,
// we'll use a simpler approach: raw WebSocket frame
if send_ws_text_frame(&stream, "reload").is_ok() {
alive.push(stream);
}
}
}
*list = alive;
}
}
/// Send a WebSocket text frame directly on a TCP stream.
fn send_ws_text_frame(mut stream: &TcpStream, msg: &str) -> Result<()> {
let payload = msg.as_bytes();
let len = payload.len();
// WebSocket text frame: opcode 0x81
let mut frame = Vec::new();
frame.push(0x81);
if len < 126 {
frame.push(len as u8);
} else if len < 65536 {
frame.push(126);
frame.push((len >> 8) as u8);
frame.push((len & 0xFF) as u8);
}
frame.extend_from_slice(payload);
stream.write_all(&frame)?;
stream.flush()?;
Ok(())
}
fn guess_mime(path: &Path) -> String {
match path.extension().and_then(|e| e.to_str()) {
Some("html") => "text/html; charset=utf-8".to_string(),
Some("css") => "text/css; charset=utf-8".to_string(),
Some("js") => "application/javascript; charset=utf-8".to_string(),
Some("json") => "application/json; charset=utf-8".to_string(),
Some("svg") => "image/svg+xml".to_string(),
Some("png") => "image/png".to_string(),
Some("jpg") | Some("jpeg") => "image/jpeg".to_string(),
Some("gif") => "image/gif".to_string(),
Some("ico") => "image/x-icon".to_string(),
Some("wasm") => "application/wasm".to_string(),
Some("woff") => "font/woff".to_string(),
Some("woff2") => "font/woff2".to_string(),
Some("ttf") => "font/ttf".to_string(),
_ => "application/octet-stream".to_string(),
}
}
fn percent_decode(input: &str) -> String {
let mut result = String::with_capacity(input.len());
let mut chars = input.bytes();
while let Some(b) = chars.next() {
if b == b'%' {
let hi = chars.next().and_then(|c| hex_val(c));
let lo = chars.next().and_then(|c| hex_val(c));
if let (Some(h), Some(l)) = (hi, lo) {
result.push((h << 4 | l) as char);
}
} else {
result.push(b as char);
}
}
result
}
fn hex_val(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}
/// Create a TCP listener with SO_REUSEADDR (and SO_REUSEPORT on Unix) set,
/// so that rapid restarts after Ctrl+C don't fail with "address in use".
fn create_reuse_listener(port: u16) -> Result<TcpListener> {
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?;
socket.set_reuse_address(true)?;
#[cfg(unix)]
socket.set_reuse_port(true)?;
let addr: std::net::SocketAddr = format!("0.0.0.0:{}", port).parse()?;
socket.bind(&addr.into())?;
socket.listen(128)?;
Ok(socket.into())
}
+22
View File
@@ -0,0 +1,22 @@
use anyhow::Result;
use std::fs;
use std::path::Path;
use walkdir::WalkDir;
pub fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> {
for entry in WalkDir::new(src).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
let rel = path.strip_prefix(src)?;
let target = dst.join(rel);
if path.is_dir() {
fs::create_dir_all(&target)?;
} else {
if let Some(parent) = target.parent() {
fs::create_dir_all(parent)?;
}
fs::copy(path, &target)?;
}
}
Ok(())
}
+22
View File
@@ -0,0 +1,22 @@
[site]
title = "cpp-httplib"
version = "0.36.0"
hostname = "https://yhirose.github.io"
base_path = "/cpp-httplib"
[[nav]]
label = "Tour"
path = "tour/"
icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>'
[[nav]]
label = "GitHub"
url = "https://github.com/yhirose/cpp-httplib"
icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>'
[i18n]
langs = ["en", "ja"]
[highlight]
dark_theme = "base16-eighties.dark"
light_theme = "InspiredGitHub"
+8
View File
@@ -0,0 +1,8 @@
---
title: "Cookbook"
order: 1
---
This section is under construction.
Check back soon for a collection of recipes organized by topic.
+21
View File
@@ -0,0 +1,21 @@
---
title: "cpp-httplib"
order: 0
---
[cpp-httplib](https://github.com/yhirose/cpp-httplib) is an HTTP/HTTPS library for C++. Just copy a single header file, [`httplib.h`](https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h), and you're ready to go.
When you need a quick HTTP server or client in C++, you want something that just works. That's exactly why I built cpp-httplib. You can start writing both servers and clients in just a few lines of code.
The API uses a lambda-based design that feels natural. It runs anywhere you have a C++11 or later compiler. Windows, macOS, Linux — use whatever environment you already have.
HTTPS works too. Just link OpenSSL or mbedTLS, and both server and client gain TLS support. Content-Encoding (gzip, Brotli, etc.), file uploads, and other features you actually need in real-world development are all included. WebSocket is also supported.
Under the hood, it uses blocking I/O with a thread pool. It's not built for handling massive numbers of simultaneous connections. But for API servers, embedded HTTP in tools, mock servers for testing, and many other use cases, it delivers solid performance.
"Solve today's problem, today." That's the kind of simplicity cpp-httplib aims for.
## Documentation
- [A Tour of cpp-httplib](tour/) — A step-by-step tutorial covering the basics. Start here if you're new
- [Cookbook](cookbook/) — A collection of recipes organized by topic. Jump to whatever you need
@@ -0,0 +1,88 @@
---
title: "Getting Started"
order: 1
---
All you need to get started with cpp-httplib is `httplib.h` and a C++ compiler. Let's download the file and get a Hello World server running.
## Getting httplib.h
You can download it directly from GitHub. Always use the latest version.
```sh
curl -LO https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
```
Place the downloaded `httplib.h` in your project directory and you're good to go.
## Setting Up Your Compiler
| OS | Development Environment | Setup |
| -- | ----------------------- | ----- |
| macOS | Apple Clang | Xcode Command Line Tools (`xcode-select --install`) |
| Ubuntu | clang++ or g++ | `apt install clang` or `apt install g++` |
| Windows | MSVC | Visual Studio 2022 or later (install with C++ components) |
## Hello World Server
Save the following code as `server.cpp`.
```cpp
#include "httplib.h"
int main() {
httplib::Server svr;
svr.Get("/", [](const httplib::Request&, httplib::Response& res) {
res.set_content("Hello, World!", "text/plain");
});
svr.listen("0.0.0.0", 8080);
}
```
In just a few lines, you have a server that responds to HTTP requests.
## Compiling and Running
The sample code in this tutorial is written in C++17 for cleaner, more concise code. cpp-httplib itself can compile with C++11 as well.
```sh
# macOS
clang++ -std=c++17 -o server server.cpp
# Linux
# `-pthread`: cpp-httplib uses threads internally
clang++ -std=c++17 -pthread -o server server.cpp
# Windows (Developer Command Prompt)
# `/EHsc`: Enable C++ exception handling
cl /EHsc /std:c++17 server.cpp
```
Once it compiles, run it.
```sh
# macOS / Linux
./server
# Windows
server.exe
```
Open `http://localhost:8080` in your browser. If you see "Hello, World!", you're all set.
You can also verify with `curl`.
```sh
curl http://localhost:8080/
# Hello, World!
```
To stop the server, press `Ctrl+C` in your terminal.
## Next Steps
Now you know the basics of running a server. Next, let's look at the client side. cpp-httplib also comes with HTTP client functionality.
**Next:** [Basic Client](../02-basic-client)
+266
View File
@@ -0,0 +1,266 @@
---
title: "Basic Client"
order: 2
---
cpp-httplib isn't just for servers -- it also comes with a full HTTP client. Let's use `httplib::Client` to send GET and POST requests.
## Preparing a Test Server
To try out the client, you need a server that accepts requests. Save the following code, then compile and run it the same way you did in the previous chapter. We'll cover the server details in the next chapter.
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.Get("/hi", [](const auto &, auto &res) {
res.set_content("Hello!", "text/plain");
});
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
svr.Post("/post", [](const auto &req, auto &res) {
res.set_content(req.body, "text/plain");
});
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1];
res.set_content("File ID: " + std::string(id), "text/plain");
});
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
## GET Request
Once the server is running, open a separate terminal and give it a try. Let's start with the simplest GET request.
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("http://localhost:8080");
auto res = cli.Get("/hi");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body << std::endl; // Hello!
}
}
```
Pass the server address to the `httplib::Client` constructor, then call `Get()` to send a request. You can retrieve the status code and body from the returned `res`.
Here's the equivalent `curl` command.
```sh
curl http://localhost:8080/hi
# Hello!
```
## Checking the Response
A response contains header information in addition to the status code and body.
```cpp
auto res = cli.Get("/hi");
if (res) {
// Status code
std::cout << res->status << std::endl; // 200
// Body
std::cout << res->body << std::endl; // Hello!
// Headers
std::cout << res->get_header_value("Content-Type") << std::endl; // text/plain
}
```
`res->body` is a `std::string`, so if you want to parse a JSON response, you can pass it directly to a JSON library like [nlohmann/json](https://github.com/nlohmann/json).
## Query Parameters
To add query parameters to a GET request, you can either write them directly in the URL or use `httplib::Params`.
```cpp
auto res = cli.Get("/search", httplib::Params{{"q", "cpp-httplib"}});
if (res) {
std::cout << res->body << std::endl; // Query: cpp-httplib
}
```
`httplib::Params` automatically URL-encodes special characters for you.
```sh
curl "http://localhost:8080/search?q=cpp-httplib"
# Query: cpp-httplib
```
## Path Parameters
When values are embedded directly in the URL path, no special client API is needed. Just pass the path to `Get()` as-is.
```cpp
auto res = cli.Get("/users/42");
if (res) {
std::cout << res->body << std::endl; // User ID: 42
}
```
```sh
curl http://localhost:8080/users/42
# User ID: 42
```
The test server also has a `/files/(\d+)` route that uses a regex to accept numeric IDs only.
```cpp
auto res = cli.Get("/files/42");
if (res) {
std::cout << res->body << std::endl; // File ID: 42
}
```
```sh
curl http://localhost:8080/files/42
# File ID: 42
```
Pass a non-numeric ID like `/files/abc` and you'll get a 404. We'll cover how that works in the next chapter.
## Request Headers
To add custom HTTP headers, pass an `httplib::Headers` object. This works with both `Get()` and `Post()`.
```cpp
auto res = cli.Get("/hi", httplib::Headers{
{"Authorization", "Bearer my-token"}
});
```
```sh
curl -H "Authorization: Bearer my-token" http://localhost:8080/hi
```
## POST Request
Let's POST some text data. Pass the body as the second argument to `Post()` and the Content-Type as the third.
```cpp
auto res = cli.Post("/post", "Hello, Server!", "text/plain");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body << std::endl; // Hello, Server!
}
```
The test server's `/post` endpoint echoes the body back, so you get the same string you sent.
```sh
curl -X POST -H "Content-Type: text/plain" -d "Hello, Server!" http://localhost:8080/post
# Hello, Server!
```
## Sending Form Data
You can send key-value pairs just like an HTML form. Use `httplib::Params` for this.
```cpp
auto res = cli.Post("/submit", httplib::Params{
{"name", "Alice"},
{"age", "30"}
});
if (res) {
std::cout << res->body << std::endl;
// age = 30
// name = Alice
}
```
This sends the data in `application/x-www-form-urlencoded` format.
```sh
curl -X POST -d "name=Alice&age=30" http://localhost:8080/submit
```
## POSTing a File
To upload a file, use `httplib::UploadFormDataItems` to send it as multipart form data.
```cpp
auto res = cli.Post("/upload", httplib::UploadFormDataItems{
{"file", "Hello, File!", "hello.txt", "text/plain"}
});
if (res) {
std::cout << res->body << std::endl; // hello.txt (12 bytes)
}
```
Each element in `UploadFormDataItems` has four fields: `{name, content, filename, content_type}`.
```sh
curl -F "file=Hello, File!;filename=hello.txt;type=text/plain" http://localhost:8080/upload
```
## Error Handling
Network communication can fail -- the server might not be reachable. Always check whether `res` is valid.
```cpp
httplib::Client cli("http://localhost:9999"); // Non-existent port
auto res = cli.Get("/hi");
if (!res) {
// Connection error
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
// Error: Connection
return 1;
}
// If we reach here, we have a response
if (res->status != 200) {
std::cout << "HTTP Error: " << res->status << std::endl;
return 1;
}
std::cout << res->body << std::endl;
```
There are two levels of errors.
- **Connection error**: The client couldn't reach the server. `res` evaluates to false, and you can call `res.error()` to find out what went wrong.
- **HTTP error**: The server returned an error status (404, 500, etc.). `res` evaluates to true, but you need to check `res->status`.
## Next Steps
Now you know how to send requests from a client. Next, let's take a closer look at the server side. We'll dig into routing, path parameters, and more.
**Next:** [Basic Server](../03-basic-server)
+280
View File
@@ -0,0 +1,280 @@
---
title: "Basic Server"
order: 3
---
In the previous chapter, you sent requests from a client to a test server. Now let's walk through how that server actually works.
## Starting the Server
Once you've registered your routes, call `svr.listen()` to start the server.
```cpp
svr.listen("0.0.0.0", 8080);
```
The first argument is the host, and the second is the port. `"0.0.0.0"` listens on all network interfaces. Use `"127.0.0.1"` if you want to accept connections from your own machine only.
`listen()` is a blocking call. It won't return until the server stops. The server keeps running until you press `Ctrl+C` in your terminal or call `svr.stop()` from another thread.
## Routing
Routing is the heart of any server. It's how you tell cpp-httplib: when a request comes in for this URL with this HTTP method, run this code.
```cpp
httplib::Server svr;
svr.Get("/hi", [](const httplib::Request &req, httplib::Response &res) {
res.set_content("Hello!", "text/plain");
});
```
`svr.Get()` registers a handler for GET requests. The first argument is the path, the second is the handler function. When a GET request arrives at `/hi`, your lambda runs.
There's a method for each HTTP verb.
```cpp
svr.Get("/path", handler); // GET
svr.Post("/path", handler); // POST
svr.Put("/path", handler); // PUT
svr.Delete("/path", handler); // DELETE
```
The handler signature is `(const httplib::Request &req, httplib::Response &res)`. You can use `auto` to keep it short.
```cpp
svr.Get("/hi", [](const auto &req, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
The handler only runs when the path matches. Requests to unregistered paths automatically return 404.
## The Request Object
The first parameter `req` gives you everything the client sent.
### Body
`req.body` holds the request body as a `std::string`.
```cpp
svr.Post("/post", [](const auto &req, auto &res) {
// Echo the body back to the client
res.set_content(req.body, "text/plain");
});
```
### Headers
Use `req.get_header_value()` to read a request header.
```cpp
svr.Get("/check", [](const auto &req, auto &res) {
auto auth = req.get_header_value("Authorization");
res.set_content("Auth: " + auth, "text/plain");
});
```
### Query Parameters and Form Data
`req.get_param_value()` retrieves a parameter by name. It works for both GET query parameters and POST form data.
```cpp
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
```
A request to `/search?q=cpp-httplib` gives you `"cpp-httplib"` for `q`.
To loop over all parameters, use `req.params`.
```cpp
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
```
### File Uploads
Files uploaded via multipart form data are available through `req.form.get_file()`.
```cpp
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
```
`f.filename` gives you the filename, and `f.content` gives you the file data.
## Path Parameters
Sometimes you want to capture part of the URL as a variable -- for example, the `42` in `/users/42`. Use the `:param` syntax to do that.
```cpp
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
```
A request to `/users/42` gives you `"42"` from `req.path_params.at("id")`. `/users/100` gives you `"100"`.
You can capture multiple segments at once.
```cpp
svr.Get("/users/:user_id/posts/:post_id", [](const auto &req, auto &res) {
auto user_id = req.path_params.at("user_id");
auto post_id = req.path_params.at("post_id");
res.set_content("User: " + user_id + ", Post: " + post_id, "text/plain");
});
```
### Regex Patterns
You can also write a regular expression directly in the path instead of `:param`. Capture group values are available via `req.matches`, which is a `std::smatch`.
```cpp
// Only accept numeric IDs
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1]; // First capture group
res.set_content("File ID: " + std::string(id), "text/plain");
});
```
`/files/42` matches, but `/files/abc` doesn't. This is handy when you want to constrain what values are accepted.
## Building a Response
The second parameter `res` is how you send data back to the client.
### Body and Content-Type
`res.set_content()` sets the body and Content-Type. That's all you need for a 200 response.
```cpp
svr.Get("/hi", [](const auto &req, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
### Status Code
To return a different status code, assign to `res.status`.
```cpp
svr.Get("/not-found", [](const auto &req, auto &res) {
res.status = 404;
res.set_content("Not found", "text/plain");
});
```
### Response Headers
Add response headers with `res.set_header()`.
```cpp
svr.Get("/with-header", [](const auto &req, auto &res) {
res.set_header("X-Custom", "my-value");
res.set_content("Hello!", "text/plain");
});
```
## Walking Through the Test Server
Now let's use what we've learned to read through the test server from the previous chapter.
### GET /hi
```cpp
svr.Get("/hi", [](const auto &, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
The simplest possible handler. We don't need any information from the request, so the `req` parameter is left unnamed. It just returns `"Hello!"`.
### GET /search
```cpp
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
```
`req.get_param_value("q")` pulls out the query parameter `q`. A request to `/search?q=cpp-httplib` returns `"Query: cpp-httplib"`.
### POST /post
```cpp
svr.Post("/post", [](const auto &req, auto &res) {
res.set_content(req.body, "text/plain");
});
```
An echo server. Whatever body the client sends, `req.body` holds it, and we send it straight back.
### POST /submit
```cpp
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
```
Loops over the form data in `req.params` using structured bindings (`auto &[key, val]`) to unpack each key-value pair.
### POST /upload
```cpp
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
```
Receives a file uploaded via multipart form data. `req.form.get_file("file")` fetches the field named `"file"`, and we respond with the filename and size.
### GET /users/:id
```cpp
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
```
`:id` is the path parameter. `req.path_params.at("id")` retrieves its value. `/users/42` gives you `"42"`, `/users/alice` gives you `"alice"`.
### GET /files/(\d+)
```cpp
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1];
res.set_content("File ID: " + std::string(id), "text/plain");
});
```
The regex `(\d+)` matches numeric IDs only. `/files/42` hits this handler, but `/files/abc` returns 404. `req.matches[1]` retrieves the first capture group.
## Next Steps
You now have the full picture of how a server works. Routing, reading requests, building responses -- that's enough to build a real API server.
Next, let's look at serving static files. We'll build a server that delivers HTML and CSS.
**Next:** [Static File Server](../04-static-file-server)
@@ -0,0 +1,134 @@
---
title: "Static File Server"
order: 4
---
cpp-httplib can serve static files too — HTML, CSS, images, you name it. No complicated configuration required. One call to `set_mount_point()` is all it takes.
## The basics of set_mount_point
Let's jump right in. `set_mount_point()` maps a URL path to a local directory.
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.set_mount_point("/", "./html");
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
The first argument is the URL mount point. The second is the local directory path. In this example, requests to `/` are served from the `./html` directory.
Let's try it out. First, create an `html` directory and add an `index.html` file.
```sh
mkdir html
```
```html
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body>
<h1>Hello from cpp-httplib!</h1>
<p>This is a static file.</p>
</body>
</html>
```
Compile and start the server.
```sh
g++ -std=c++17 -o server server.cpp -pthread
./server
```
Open `http://localhost:8080` in your browser. You should see the contents of `html/index.html`. Visiting `http://localhost:8080/index.html` returns the same page.
You can also access it with the client code from the previous chapter, or with `curl`.
```cpp
httplib::Client cli("http://localhost:8080");
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // HTML is displayed
}
```
```sh
curl http://localhost:8080
```
## Multiple mount points
You can call `set_mount_point()` as many times as you like. Each URL path gets its own directory.
```cpp
svr.set_mount_point("/", "./public");
svr.set_mount_point("/assets", "./static/assets");
svr.set_mount_point("/docs", "./documentation");
```
A request to `/assets/style.css` serves `./static/assets/style.css`. A request to `/docs/guide.html` serves `./documentation/guide.html`.
## Combining with handlers
Static file serving and routing handlers — the kind you learned about in the previous chapter — work side by side.
```cpp
httplib::Server svr;
// API endpoint
svr.Get("/api/hello", [](const auto &, auto &res) {
res.set_content(R"({"message":"Hello!"})", "application/json");
});
// Static file serving
svr.set_mount_point("/", "./public");
svr.listen("0.0.0.0", 8080);
```
Handlers take priority. The handler responds to `/api/hello`. For every other path, the server looks for a file in `./public`.
## Adding response headers
Pass headers as the third argument to `set_mount_point()` and they get attached to every static file response. This is great for cache control.
```cpp
svr.set_mount_point("/", "./public", {
{"Cache-Control", "max-age=3600"}
});
```
With this in place, the browser caches served files for one hour.
## A Dockerfile for your static file server
The cpp-httplib repository includes a `Dockerfile` built for static file serving. We also publish a pre-built image on Docker Hub, so you can get up and running with a single command.
```sh
> docker run -p 8080:80 -v ./my-site:/html yhirose4dockerhub/cpp-httplib-server
Serving HTTP on 0.0.0.0:80
Mount point: / -> ./html
Press Ctrl+C to shutdown gracefully...
192.168.65.1 - - [22/Feb/2026:12:00:00 +0000] "GET / HTTP/1.1" 200 256 "-" "Mozilla/5.0 ..."
192.168.65.1 - - [22/Feb/2026:12:00:00 +0000] "GET /style.css HTTP/1.1" 200 1024 "-" "Mozilla/5.0 ..."
192.168.65.1 - - [22/Feb/2026:12:00:01 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
```
Everything in your `./my-site` directory gets served on port 8080. The access log follows the same format as NGINX, so you can see exactly what's happening.
## What's next
You can now serve static files. A web server that delivers HTML, CSS, and JavaScript — built with this little code.
Next, let's encrypt your connections with HTTPS. We'll start by setting up a TLS library.
**Next:** [TLS Setup](../05-tls-setup)
+88
View File
@@ -0,0 +1,88 @@
---
title: "TLS Setup"
order: 5
---
So far we've been using plain HTTP, but in the real world, HTTPS is the norm. To use HTTPS with cpp-httplib, you need a TLS library.
In this tour, we'll use OpenSSL. It's the most widely used option, and you'll find plenty of resources online.
## Installing OpenSSL
Install it for your OS.
| OS | How to install |
| -- | -------------- |
| macOS | [Homebrew](https://brew.sh/) (`brew install openssl`) |
| Ubuntu / Debian | `sudo apt install libssl-dev` |
| Windows | [vcpkg](https://vcpkg.io/) (`vcpkg install openssl`) |
## Compile Options
To enable TLS, define the `CPPHTTPLIB_OPENSSL_SUPPORT` macro when compiling. You'll need a few extra options compared to the previous chapters.
```sh
# macOS (Homebrew)
clang++ -std=c++17 -DCPPHTTPLIB_OPENSSL_SUPPORT \
-I$(brew --prefix openssl)/include \
-L$(brew --prefix openssl)/lib \
-lssl -lcrypto \
-framework CoreFoundation -framework Security \
-o server server.cpp
# Linux
clang++ -std=c++17 -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT \
-lssl -lcrypto \
-o server server.cpp
# Windows (Developer Command Prompt)
cl /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
```
Let's look at what each option does.
- **`-DCPPHTTPLIB_OPENSSL_SUPPORT`** — Defines the macro that enables TLS support
- **`-lssl -lcrypto`** — Links the OpenSSL libraries
- **`-I` / `-L`** (macOS only) — Points to the Homebrew OpenSSL paths
- **`-framework CoreFoundation -framework Security`** (macOS only) — Needed to automatically load system certificates from the Keychain
## Verifying the Setup
Let's make sure everything works. Here's a simple program that passes an HTTPS URL to `httplib::Client`.
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://www.google.com");
auto res = cli.Get("/");
if (res) {
std::cout << "Status: " << res->status << std::endl;
} else {
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
}
}
```
Compile and run it. If you see `Status: 200`, your setup is complete.
## Other TLS Backends
cpp-httplib also supports Mbed TLS and wolfSSL in addition to OpenSSL. You can switch between them just by changing the macro definition and linked libraries.
| Backend | Macro | Libraries to link |
| :--- | :--- | :--- |
| OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` |
| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` |
| wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` |
This tour assumes OpenSSL, but the API is the same regardless of which backend you choose.
## Next Step
You're all set with TLS. Next, let's send a request to an HTTPS site.
**Next:** [HTTPS Client](../06-https-client)
+122
View File
@@ -0,0 +1,122 @@
---
title: "HTTPS Client"
order: 6
---
In the previous chapter, you set up OpenSSL. Now let's put it to use with an HTTPS client. You can use the same `httplib::Client` from Chapter 2. Just pass a URL with the `https://` scheme to the constructor.
## GET Request
Let's try accessing a real HTTPS site.
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://nghttp2.org");
auto res = cli.Get("/");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body.substr(0, 100) << std::endl; // First 100 chars of the HTML
} else {
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
}
}
```
In Chapter 2, you wrote `httplib::Client cli("http://localhost:8080")`. All you need to change is the scheme to `https://`. Every API you learned in Chapter 2 -- `Get()`, `Post()`, and so on -- works exactly the same way.
```sh
curl https://nghttp2.org/
```
## Specifying a Port
The default port for HTTPS is 443. If you need a different port, include it in the URL.
```cpp
httplib::Client cli("https://localhost:8443");
```
## CA Certificate Verification
When connecting over HTTPS, `httplib::Client` verifies the server certificate by default. It only connects to servers whose certificate was issued by a trusted CA (Certificate Authority).
CA certificates are loaded automatically from the Keychain on macOS, the system CA certificate store on Linux, and the Windows certificate store on Windows. In most cases, no extra configuration is needed.
### Specifying a CA Certificate File
On some environments, the system CA certificates may not be found. In that case, use `set_ca_cert_path()` to specify the path directly.
```cpp
httplib::Client cli("https://nghttp2.org");
cli.set_ca_cert_path("/etc/ssl/certs/ca-certificates.crt");
auto res = cli.Get("/");
```
```sh
curl --cacert /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
```
### Disabling Certificate Verification
During development, you might want to connect to a server with a self-signed certificate. You can disable verification for that.
```cpp
httplib::Client cli("https://localhost:8443");
cli.enable_server_certificate_verification(false);
auto res = cli.Get("/");
```
```sh
curl -k https://localhost:8443/
```
Never disable this in production. It opens you up to man-in-the-middle attacks.
## Following Redirects
When accessing HTTPS sites, you'll often encounter redirects. For example, `http://` to `https://`, or a bare domain to `www`.
By default, redirects are not followed. You can check the redirect target in the `Location` header.
```cpp
httplib::Client cli("https://nghttp2.org");
auto res = cli.Get("/httpbin/redirect/3");
if (res) {
std::cout << res->status << std::endl; // 302
std::cout << res->get_header_value("Location") << std::endl;
}
```
```sh
curl https://nghttp2.org/httpbin/redirect/3
```
Call `set_follow_location(true)` to automatically follow redirects and get the final response.
```cpp
httplib::Client cli("https://nghttp2.org");
cli.set_follow_location(true);
auto res = cli.Get("/httpbin/redirect/3");
if (res) {
std::cout << res->status << std::endl; // 200 (the final response)
}
```
```sh
curl -L https://nghttp2.org/httpbin/redirect/3
```
## Next Steps
Now you know how to use the HTTPS client. Next, let's set up your own HTTPS server. We'll start with creating a self-signed certificate.
**Next:** [HTTPS Server](../07-https-server)
+124
View File
@@ -0,0 +1,124 @@
---
title: "HTTPS Server"
order: 7
---
In the previous chapter, you used an HTTPS client. Now let's set up your own HTTPS server. Just swap `httplib::Server` from Chapter 3 with `httplib::SSLServer`.
A TLS server needs a server certificate and a private key, though. Let's get those ready first.
## Creating a Self-Signed Certificate
For development and testing, a self-signed certificate works just fine. You can generate one quickly with an OpenSSL command.
```sh
openssl req -x509 -noenc -keyout key.pem -out cert.pem -subj /CN=localhost
```
This creates two files:
- **`cert.pem`** — Server certificate
- **`key.pem`** — Private key
## A Minimal HTTPS Server
Once you have your certificate, let's write the server.
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::SSLServer svr("cert.pem", "key.pem");
svr.Get("/", [](const auto &, auto &res) {
res.set_content("Hello, HTTPS!", "text/plain");
});
std::cout << "Listening on https://localhost:8443" << std::endl;
svr.listen("0.0.0.0", 8443);
}
```
Just pass the certificate and private key paths to the `httplib::SSLServer` constructor. The routing API is exactly the same as `httplib::Server` from Chapter 3.
Compile and start it up.
## Testing It Out
With the server running, try accessing it with `curl`. Since we're using a self-signed certificate, add the `-k` option to skip certificate verification.
```sh
curl -k https://localhost:8443/
# Hello, HTTPS!
```
If you open `https://localhost:8443` in a browser, you'll see a "This connection is not secure" warning. That's expected with a self-signed certificate. Just proceed past it.
## Connecting from a Client
Let's connect using `httplib::Client` from the previous chapter. There are two ways to connect to a server with a self-signed certificate.
### Option 1: Disable Certificate Verification
This is the quick and easy approach for development.
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://localhost:8443");
cli.enable_server_certificate_verification(false);
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // Hello, HTTPS!
}
}
```
### Option 2: Specify the Self-Signed Certificate as a CA Certificate
This is the safer approach. You tell the client to trust `cert.pem` as a CA certificate.
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://localhost:8443");
cli.set_ca_cert_path("cert.pem");
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // Hello, HTTPS!
}
}
```
This way, only connections to the server with that specific certificate are allowed, preventing impersonation. Use this approach whenever possible, even in test environments.
## Comparing Server and SSLServer
The `httplib::Server` API you learned in Chapter 3 works exactly the same with `httplib::SSLServer`. The only difference is the constructor.
| | `httplib::Server` | `httplib::SSLServer` |
| -- | ------------------ | -------------------- |
| Constructor | No arguments | Certificate and private key paths |
| Protocol | HTTP | HTTPS |
| Port (convention) | 8080 | 8443 |
| Routing | Same | Same |
To switch an HTTP server to HTTPS, just change the constructor.
## Next Steps
Your HTTPS server is up and running. You now have the basics of both HTTP/HTTPS clients and servers covered.
Next, let's look at the WebSocket support that was recently added to cpp-httplib.
**Next:** [WebSocket](../08-websocket)
+139
View File
@@ -0,0 +1,139 @@
---
title: "WebSocket"
order: 8
---
cpp-httplib supports WebSocket as well. Unlike HTTP request/response, WebSocket lets the server and client exchange messages in both directions. It's great for chat apps and real-time notifications.
Let's build an echo server and client right away.
## Echo Server
Here's an echo server that sends back whatever message it receives.
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send(msg); // Send back the received message as-is
}
});
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
You register a WebSocket handler with `svr.WebSocket()`. It works just like `svr.Get()` and `svr.Post()` from Chapter 3.
Inside the handler, `ws.read(msg)` waits for a message. When the connection closes, `read()` returns `false`, so the loop exits. `ws.send(msg)` sends a message back.
## Connecting from a Client
Let's connect to the server using `httplib::ws::WebSocketClient`.
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::ws::WebSocketClient client("ws://localhost:8080/ws");
if (!client.connect()) {
std::cout << "Connection failed" << std::endl;
return 1;
}
// Send a message
client.send("Hello, WebSocket!");
// Receive a response from the server
std::string msg;
if (client.read(msg)) {
std::cout << msg << std::endl; // Hello, WebSocket!
}
client.close();
}
```
Pass a URL in `ws://host:port/path` format to the constructor. Call `connect()` to start the connection, then use `send()` and `read()` to exchange messages.
## Text and Binary
WebSocket has two types of messages: text and binary. You can tell them apart by the return value of `read()`.
```cpp
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
httplib::ws::ReadResult ret;
while ((ret = ws.read(msg))) {
if (ret == httplib::ws::Binary) {
ws.send(msg.data(), msg.size()); // Send as binary
} else {
ws.send(msg); // Send as text
}
}
});
```
- `ws.send(const std::string &)` — sends as a text message
- `ws.send(const char *, size_t)` — sends as a binary message
The client-side API is the same.
## Accessing Request Information
You can read HTTP request information from the handshake through the first argument `req` in the handler. This is handy for checking authentication tokens.
```cpp
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
auto token = req.get_header_value("Authorization");
if (token.empty()) {
ws.close(httplib::ws::CloseStatus::PolicyViolation, "unauthorized");
return;
}
std::string msg;
while (ws.read(msg)) {
ws.send(msg);
}
});
```
## Using WSS
WebSocket over HTTPS (WSS) is also supported. On the server side, just register a WebSocket handler on `httplib::SSLServer`.
```cpp
httplib::SSLServer svr("cert.pem", "key.pem");
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send(msg);
}
});
svr.listen("0.0.0.0", 8443);
```
On the client side, use the `wss://` scheme.
```cpp
httplib::ws::WebSocketClient client("wss://localhost:8443/ws");
```
## Next Steps
Now you know the basics of WebSocket. This wraps up the Tour.
The next page gives you a summary of features we didn't cover in the Tour.
**Next:** [What's Next](../09-whats-next)
+228
View File
@@ -0,0 +1,228 @@
---
title: "What's Next"
order: 9
---
Great job finishing the Tour! You now have a solid grasp of the cpp-httplib basics. But there's a lot more to explore. Here's a quick overview of features we didn't cover in the Tour, organized by category.
## Streaming API
When you're working with LLM streaming responses or downloading large files, you don't want to load the entire response into memory. Use `stream::Get()` to process data chunk by chunk.
```cpp
httplib::Client cli("http://localhost:11434");
auto result = httplib::stream::Get(cli, "/api/generate");
if (result) {
while (result.next()) {
std::cout.write(result.data(), result.size());
}
}
```
You can also pass a `content_receiver` callback to `Get()`. This approach works with Keep-Alive.
```cpp
httplib::Client cli("http://localhost:8080");
cli.Get("/stream", [](const char *data, size_t len) {
std::cout.write(data, len);
return true;
});
```
On the server side, you have `set_content_provider()` and `set_chunked_content_provider()`. Use the former when you know the size, and the latter when you don't.
```cpp
// With known size (sets Content-Length)
svr.Get("/file", [](const auto &, auto &res) {
auto size = get_file_size("large.bin");
res.set_content_provider(size, "application/octet-stream",
[](size_t offset, size_t length, httplib::DataSink &sink) {
// Send 'length' bytes starting from 'offset'
return true;
});
});
// Unknown size (Chunked Transfer Encoding)
svr.Get("/stream", [](const auto &, auto &res) {
res.set_chunked_content_provider("text/plain",
[](size_t offset, httplib::DataSink &sink) {
sink.write("chunk\n", 6);
return true; // Return false to finish
});
});
```
For uploading large files, `make_file_provider()` comes in handy. It streams the file instead of loading it all into memory.
```cpp
httplib::Client cli("http://localhost:8080");
auto res = cli.Post("/upload", {}, {
httplib::make_file_provider("file", "/path/to/large-file.zip")
});
```
## Server-Sent Events (SSE)
We provide an SSE client as well. It supports automatic reconnection and resuming via `Last-Event-ID`.
```cpp
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << msg.event << ": " << msg.data << std::endl;
});
sse.start(); // Blocking, with auto-reconnection
```
You can also set separate handlers for each event type.
```cpp
sse.on_event("update", [](const httplib::sse::SSEMessage &msg) {
// Only handles "update" events
});
```
## Authentication
The client has helpers for Basic auth, Bearer Token auth, and Digest auth.
```cpp
httplib::Client cli("https://api.example.com");
cli.set_basic_auth("user", "password");
cli.set_bearer_token_auth("my-token");
```
## Compression
We support compression and decompression with gzip, Brotli, and Zstandard. Define the corresponding macro when you compile.
| Method | Macro |
| -- | -- |
| gzip | `CPPHTTPLIB_ZLIB_SUPPORT` |
| Brotli | `CPPHTTPLIB_BROTLI_SUPPORT` |
| Zstandard | `CPPHTTPLIB_ZSTD_SUPPORT` |
```cpp
httplib::Client cli("https://example.com");
cli.set_compress(true); // Compress request body
cli.set_decompress(true); // Decompress response body
```
## Proxy
You can connect through an HTTP proxy.
```cpp
httplib::Client cli("https://example.com");
cli.set_proxy("proxy.example.com", 8080);
cli.set_proxy_basic_auth("user", "password");
```
## Timeouts
You can set connection, read, and write timeouts individually.
```cpp
httplib::Client cli("https://example.com");
cli.set_connection_timeout(5, 0); // 5 seconds
cli.set_read_timeout(10, 0); // 10 seconds
cli.set_write_timeout(10, 0); // 10 seconds
```
## Keep-Alive
If you're making multiple requests to the same server, enable Keep-Alive. It reuses the TCP connection, which is much more efficient.
```cpp
httplib::Client cli("https://example.com");
cli.set_keep_alive(true);
```
## Server Middleware
You can hook into request processing before and after handlers run.
```cpp
svr.set_pre_routing_handler([](const auto &req, auto &res) {
// Runs before every request
return httplib::Server::HandlerResponse::Unhandled; // Continue to normal routing
});
svr.set_post_routing_handler([](const auto &req, auto &res) {
// Runs after the response is sent
res.set_header("X-Server", "cpp-httplib");
});
```
Use `req.user_data` to pass data from middleware to handlers. This is useful for sharing things like decoded auth tokens.
```cpp
svr.set_pre_routing_handler([](const auto &req, auto &res) {
req.user_data["auth_user"] = std::string("alice");
return httplib::Server::HandlerResponse::Unhandled;
});
svr.Get("/me", [](const auto &req, auto &res) {
auto user = std::any_cast<std::string>(req.user_data.at("auth_user"));
res.set_content("Hello, " + user, "text/plain");
});
```
You can also customize error and exception handlers.
```cpp
svr.set_error_handler([](const auto &req, auto &res) {
res.set_content("Custom Error Page", "text/html");
});
svr.set_exception_handler([](const auto &req, auto &res, std::exception_ptr ep) {
res.status = 500;
res.set_content("Internal Server Error", "text/plain");
});
```
## Logging
You can set a logger on both the server and the client.
```cpp
svr.set_logger([](const auto &req, const auto &res) {
std::cout << req.method << " " << req.path << " " << res.status << std::endl;
});
```
## Unix Domain Socket
In addition to TCP, we support Unix Domain Sockets. You can use them for inter-process communication on the same machine.
```cpp
// Server
httplib::Server svr;
svr.set_address_family(AF_UNIX);
svr.listen("/tmp/httplib.sock", 0);
```
```cpp
// Client
httplib::Client cli("http://localhost");
cli.set_address_family(AF_UNIX);
cli.set_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
auto res = cli.Get("/");
```
## Learn More
Want to dig deeper? Check out these resources.
- Cookbook — A collection of recipes for common use cases
- [README](https://github.com/yhirose/cpp-httplib/blob/master/README.md) — Full API reference
- [README-sse](https://github.com/yhirose/cpp-httplib/blob/master/README-sse.md) — How to use Server-Sent Events
- [README-stream](https://github.com/yhirose/cpp-httplib/blob/master/README-stream.md) — How to use the Streaming API
- [README-websocket](https://github.com/yhirose/cpp-httplib/blob/master/README-websocket.md) — How to use the WebSocket server
+16
View File
@@ -0,0 +1,16 @@
---
title: "A Tour of cpp-httplib"
order: 1
---
This is a step-by-step tutorial that walks you through the basics of cpp-httplib. Each chapter builds on the previous one, so please read them in order starting from Chapter 1.
1. [Getting Started](01-getting-started) — Get httplib.h and build a Hello World server
2. [Basic Client](02-basic-client) — Send GET/POST requests and use path parameters
3. [Basic Server](03-basic-server) — Routing, path parameters, and building responses
4. [Static File Server](04-static-file-server) — Serve static files
5. [TLS Setup](05-tls-setup) — Set up OpenSSL / mbedTLS
6. [HTTPS Client](06-https-client) — Make requests to HTTPS sites
7. [HTTPS Server](07-https-server) — Build an HTTPS server
8. [WebSocket](08-websocket) — Learn the basics of WebSocket communication
9. [What's Next](09-whats-next) — Explore more features
+8
View File
@@ -0,0 +1,8 @@
---
title: "Cookbook"
order: 1
---
This section is under construction.
Check back soon for a collection of recipes organized by topic.
+21
View File
@@ -0,0 +1,21 @@
---
title: "cpp-httplib"
order: 0
---
[cpp-httplib](https://github.com/yhirose/cpp-httplib)は、C++用のHTTP/HTTPSライブラリです。[`httplib.h`](https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h) というヘッダーファイルを1枚コピーするだけで使えます。
C++でちょっとしたHTTPサーバーやクライアントが必要になったとき、すぐに動くものが欲しいですよね。cpp-httplibはまさにそのために作られました。サーバーもクライアントも、数行のコードで書き始められます。
APIはラムダ式をベースにした直感的な設計で、C++11以降のコンパイラーがあればどこでも動きます。Windows、macOS、Linux — お使いの環境をそのまま使えます。
HTTPSも使えます。OpenSSLやmbedTLSをリンクするだけで、サーバー・クライアントの両方がTLSに対応します。Content-Encodinggzip, brotli等)、ファイルアップロードなど、実際の開発で必要になる機能もひと通り揃っています。WebSocketもサポートしています。
内部的にはブロッキングI/Oとスレッドプールを使っています。大量の同時接続を捌くような用途には向きませんが、APIサーバーやツールの組み込みHTTP、テスト用のモックサーバーなど、多くのユースケースで十分な性能を発揮します。
「今日の課題を、今日中に解決する」— cpp-httplibが目指しているのは、そういうシンプルさです。
## ドキュメント
- [A Tour of cpp-httplib](tour/) — 基本を順を追って学べるチュートリアル。初めての方はここから
- [Cookbook](cookbook/) — 目的別のレシピ集。必要なトピックから読めます
@@ -0,0 +1,88 @@
---
title: "Getting Started"
order: 1
---
cpp-httplibを始めるのに必要なのは、`httplib.h`とC++コンパイラーだけです。ファイルをダウンロードして、Hello Worldサーバーを動かすところまでやってみましょう。
## httplib.h の入手
GitHubから直接ダウンロードできます。常に最新版を使ってください。
```sh
curl -LO https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
```
ダウンロードした `httplib.h` をプロジェクトのディレクトリに置けば、準備完了です。
## コンパイラーの準備
| OS | 開発環境 | セットアップ |
| -- | -------- | ------------ |
| macOS | Apple Clang | Xcode Command Line Tools (`xcode-select --install`) |
| Ubuntu | clang++ または g++ | `apt install clang` または `apt install g++` |
| Windows | MSVC | Visual Studio 2022 以降(C++ コンポーネントを含めてインストール) |
## Hello World サーバー
次のコードを `server.cpp` として保存しましょう。
```cpp
#include "httplib.h"
int main() {
httplib::Server svr;
svr.Get("/", [](const httplib::Request&, httplib::Response& res) {
res.set_content("Hello, World!", "text/plain");
});
svr.listen("0.0.0.0", 8080);
}
```
たった数行で、HTTPリクエストに応答するサーバーが書けます。
## コンパイルと実行
このチュートリアルのサンプルコードは、コードを簡潔に書けるC++17で書いています。cpp-httplib自体はC++11でもコンパイルできます。
```sh
# macOS
clang++ -std=c++17 -o server server.cpp
# Linux
# `-pthread`: cpp-httplibは内部でスレッドを使用
clang++ -std=c++17 -pthread -o server server.cpp
# Windows (Developer Command Prompt)
# `/EHsc`: C++例外処理を有効化
cl /EHsc /std:c++17 server.cpp
```
コンパイルできたら実行します。
```sh
# macOS / Linux
./server
# Windows
server.exe
```
ブラウザで `http://localhost:8080` を開いてください。"Hello, World!" と表示されれば成功です。
`curl` でも確認できます。
```sh
curl http://localhost:8080/
# Hello, World!
```
サーバーを停止するには、ターミナルで `Ctrl+C` を押します。
## 次のステップ
サーバーの基本がわかりましたね。次は、クライアント側を見てみましょう。cpp-httplibはHTTPクライアント機能も備えています。
**次:** [Basic Client](../02-basic-client)
+266
View File
@@ -0,0 +1,266 @@
---
title: "Basic Client"
order: 2
---
cpp-httplibはサーバーだけでなく、HTTPクライアント機能も備えています。`httplib::Client` を使って、GETやPOSTリクエストを送ってみましょう。
## テスト用サーバーの準備
クライアントの動作を確認するために、リクエストを受け付けるサーバーを用意します。次のコードを保存し、前章と同じ手順でコンパイル・実行してください。サーバーの詳しい解説は次章で行います。
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.Get("/hi", [](const auto &, auto &res) {
res.set_content("Hello!", "text/plain");
});
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
svr.Post("/post", [](const auto &req, auto &res) {
res.set_content(req.body, "text/plain");
});
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1];
res.set_content("File ID: " + std::string(id), "text/plain");
});
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
## GETリクエスト
サーバーが起動したら、別のターミナルを開いて試してみましょう。まず、最もシンプルなGETリクエストです。
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("http://localhost:8080");
auto res = cli.Get("/hi");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body << std::endl; // Hello!
}
}
```
`httplib::Client` のコンストラクターにサーバーのアドレスを渡し、`Get()` でリクエストを送ります。戻り値の `res` からステータスコードやボディを取得できます。
対応する `curl` コマンドはこうなります。
```sh
curl http://localhost:8080/hi
# Hello!
```
## レスポンスの確認
レスポンスには、ステータスコードとボディ以外にもヘッダー情報が含まれています。
```cpp
auto res = cli.Get("/hi");
if (res) {
// ステータスコード
std::cout << res->status << std::endl; // 200
// ボディ
std::cout << res->body << std::endl; // Hello!
// ヘッダー
std::cout << res->get_header_value("Content-Type") << std::endl; // text/plain
}
```
`res->body``std::string` なので、JSON レスポンスをパースしたい場合は [nlohmann/json](https://github.com/nlohmann/json) などの JSON ライブラリにそのまま渡せます。
## クエリパラメーター
GETリクエストにクエリパラメーターを付けるには、URLに直接書くか、`httplib::Params` を使います。
```cpp
auto res = cli.Get("/search", httplib::Params{{"q", "cpp-httplib"}});
if (res) {
std::cout << res->body << std::endl; // Query: cpp-httplib
}
```
`httplib::Params` を使うと、特殊文字のURLエンコードを自動で行ってくれます。
```sh
curl "http://localhost:8080/search?q=cpp-httplib"
# Query: cpp-httplib
```
## パスパラメーター
URLのパスに値を直接埋め込む場合も、クライアント側は特別なAPIは不要です。パスをそのまま `Get()` に渡すだけです。
```cpp
auto res = cli.Get("/users/42");
if (res) {
std::cout << res->body << std::endl; // User ID: 42
}
```
```sh
curl http://localhost:8080/users/42
# User ID: 42
```
テスト用サーバーには、正規表現でIDを数字のみに絞った `/files/(\d+)` もあります。
```cpp
auto res = cli.Get("/files/42");
if (res) {
std::cout << res->body << std::endl; // File ID: 42
}
```
```sh
curl http://localhost:8080/files/42
# File ID: 42
```
`/files/abc` のように数字以外を渡すと404が返ります。仕組みは次章で解説します。
## リクエストヘッダー
カスタムHTTPヘッダーを付けるには、`httplib::Headers` を渡します。`Get()``Post()` のどちらでも使えます。
```cpp
auto res = cli.Get("/hi", httplib::Headers{
{"Authorization", "Bearer my-token"}
});
```
```sh
curl -H "Authorization: Bearer my-token" http://localhost:8080/hi
```
## POSTリクエスト
テキストデータをPOSTしてみましょう。`Post()` の第2引数にボディ、第3引数にContent-Typeを指定します。
```cpp
auto res = cli.Post("/post", "Hello, Server!", "text/plain");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body << std::endl; // Hello, Server!
}
```
テスト用サーバーの `/post` はボディをそのまま返すので、送った文字列がそのまま返ってきます。
```sh
curl -X POST -H "Content-Type: text/plain" -d "Hello, Server!" http://localhost:8080/post
# Hello, Server!
```
## フォームデータの送信
HTMLフォームのように、キーと値のペアを送ることもできます。`httplib::Params` を使います。
```cpp
auto res = cli.Post("/submit", httplib::Params{
{"name", "Alice"},
{"age", "30"}
});
if (res) {
std::cout << res->body << std::endl;
// age = 30
// name = Alice
}
```
これは `application/x-www-form-urlencoded` 形式で送信されます。
```sh
curl -X POST -d "name=Alice&age=30" http://localhost:8080/submit
```
## ファイルのPOST
ファイルをアップロードするには、`httplib::UploadFormDataItems` を使ってマルチパートフォームデータとして送信します。
```cpp
auto res = cli.Post("/upload", httplib::UploadFormDataItems{
{"file", "Hello, File!", "hello.txt", "text/plain"}
});
if (res) {
std::cout << res->body << std::endl; // hello.txt (12 bytes)
}
```
`UploadFormDataItems` の各要素は `{name, content, filename, content_type}` の4つのフィールドで構成されます。
```sh
curl -F "file=Hello, File!;filename=hello.txt;type=text/plain" http://localhost:8080/upload
```
## エラーハンドリング
ネットワーク通信では、サーバーに接続できない場合があります。`res` が有効かどうかを必ず確認しましょう。
```cpp
httplib::Client cli("http://localhost:9999"); // 存在しないポート
auto res = cli.Get("/hi");
if (!res) {
// 接続エラー
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
// Error: Connection
return 1;
}
// ここに到達すればレスポンスを受信できている
if (res->status != 200) {
std::cout << "HTTP Error: " << res->status << std::endl;
return 1;
}
std::cout << res->body << std::endl;
```
エラーには2つのレベルがあります。
- **接続エラー**: サーバーに到達できなかった場合。`res` が偽になり、`res.error()` でエラーの種類を取得できます
- **HTTPエラー**: サーバーからエラーステータス(404、500など)が返ってきた場合。`res` は真ですが、`res->status` を確認する必要があります
## 次のステップ
クライアントからリクエストを送る方法がわかりました。次は、サーバー側をもっと詳しく見てみましょう。ルーティングやパスパラメータなど、サーバーの機能を掘り下げます。
**次:** [Basic Server](../03-basic-server)
+280
View File
@@ -0,0 +1,280 @@
---
title: "Basic Server"
order: 3
---
前章ではクライアントからリクエストを送りました。そのとき、テスト用サーバーを用意しましたね。この章では、あのサーバーの仕組みをひとつずつ紐解いていきます。
## サーバーの起動
ルーティングを登録したら、最後に `svr.listen()` を呼んでサーバーを起動します。
```cpp
svr.listen("0.0.0.0", 8080);
```
第1引数はホスト、第2引数はポート番号です。`"0.0.0.0"` を指定すると、すべてのネットワークインターフェースでリクエストを受け付けます。自分のマシンからのアクセスだけに限定したいときは `"127.0.0.1"` を使います。
`listen()` はブロッキング呼び出しです。サーバーが停止するまで、この行から先には進みません。ターミナルで `Ctrl+C` を押すか、別スレッドから `svr.stop()` を呼ぶまでサーバーは動き続けます。
## ルーティング
サーバーの核になるのは「ルーティング」です。どのURLに、どのHTTPメソッドでアクセスされたら、何をするか。それを登録する仕組みです。
```cpp
httplib::Server svr;
svr.Get("/hi", [](const httplib::Request &req, httplib::Response &res) {
res.set_content("Hello!", "text/plain");
});
```
`svr.Get()` は、GETリクエストに対するハンドラーを登録します。第1引数がパス、第2引数がハンドラー関数です。`/hi` にGETリクエストが来たら、このラムダが呼ばれます。
HTTPメソッドごとにメソッドが用意されています。
```cpp
svr.Get("/path", handler); // GET
svr.Post("/path", handler); // POST
svr.Put("/path", handler); // PUT
svr.Delete("/path", handler); // DELETE
```
ハンドラーのシグネチャは `(const httplib::Request &req, httplib::Response &res)` です。`auto` を使って短く書くこともできます。
```cpp
svr.Get("/hi", [](const auto &req, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
パスが一致したときだけハンドラーが呼ばれます。登録されていないパスにアクセスすると、自動的に404が返ります。
## リクエストオブジェクト
ハンドラーの第1引数 `req` から、クライアントが送ってきた情報を読み取れます。
### ボディ
`req.body` でリクエストボディを取得できます。型は `std::string` です。
```cpp
svr.Post("/post", [](const auto &req, auto &res) {
// クライアントが送ったボディをそのまま返す
res.set_content(req.body, "text/plain");
});
```
### ヘッダー
`req.get_header_value()` でリクエストヘッダーの値を取得できます。
```cpp
svr.Get("/check", [](const auto &req, auto &res) {
auto auth = req.get_header_value("Authorization");
res.set_content("Auth: " + auth, "text/plain");
});
```
### クエリパラメーターとフォームデータ
`req.get_param_value()` でパラメーターを取得できます。GETのクエリパラメーターと、POSTのフォームデータの両方に使えます。
```cpp
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
```
`/search?q=cpp-httplib` にアクセスすると、`q` の値は `"cpp-httplib"` になります。
すべてのパラメーターをループで処理したいときは、`req.params` を使います。
```cpp
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
```
### ファイルアップロード
マルチパートフォームでアップロードされたファイルは、`req.form.get_file()` で取得します。
```cpp
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
```
`f.filename` でファイル名、`f.content` でファイルの中身にアクセスできます。
## パスパラメーター
URLの一部を変数として受け取りたいことがあります。たとえば `/users/42``42` を取得したい場合です。`:param` 記法を使うと、URLの一部をキャプチャできます。
```cpp
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
```
`/users/42` にアクセスすると、`req.path_params.at("id")``"42"` を返します。`/users/100` なら `"100"` です。
複数のパスパラメーターも使えます。
```cpp
svr.Get("/users/:user_id/posts/:post_id", [](const auto &req, auto &res) {
auto user_id = req.path_params.at("user_id");
auto post_id = req.path_params.at("post_id");
res.set_content("User: " + user_id + ", Post: " + post_id, "text/plain");
});
```
### 正規表現パターン
`:param` の代わりに正規表現をパスに書くこともできます。キャプチャグループの値は `req.matches` で取得します。型は `std::smatch` です。
```cpp
// 数字のみのIDを受け付ける
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1]; // 最初のキャプチャグループ
res.set_content("File ID: " + std::string(id), "text/plain");
});
```
`/files/42` にはマッチしますが、`/files/abc` にはマッチしません。入力値を絞り込みたいときに便利です。
## レスポンスの組み立て
ハンドラーの第2引数 `res` を使って、クライアントに返すレスポンスを組み立てます。
### ボディとContent-Type
`res.set_content()` でボディとContent-Typeを設定します。これだけでステータスコード200のレスポンスが返ります。
```cpp
svr.Get("/hi", [](const auto &req, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
### ステータスコード
ステータスコードを変えたいときは、`res.status` に代入します。
```cpp
svr.Get("/not-found", [](const auto &req, auto &res) {
res.status = 404;
res.set_content("Not found", "text/plain");
});
```
### レスポンスヘッダー
`res.set_header()` でレスポンスヘッダーを追加できます。
```cpp
svr.Get("/with-header", [](const auto &req, auto &res) {
res.set_header("X-Custom", "my-value");
res.set_content("Hello!", "text/plain");
});
```
## 前章のサーバーを読み解く
ここまでの知識を使って、前章で用意したテスト用サーバーを改めて見てみましょう。
### GET /hi
```cpp
svr.Get("/hi", [](const auto &, auto &res) {
res.set_content("Hello!", "text/plain");
});
```
最もシンプルなハンドラーです。リクエストの情報は使わないので、`req` の変数名を省略しています。`"Hello!"` というテキストをそのまま返します。
### GET /search
```cpp
svr.Get("/search", [](const auto &req, auto &res) {
auto q = req.get_param_value("q");
res.set_content("Query: " + q, "text/plain");
});
```
`req.get_param_value("q")` でクエリパラメーター `q` の値を取り出します。`/search?q=cpp-httplib` なら、レスポンスは `"Query: cpp-httplib"` になります。
### POST /post
```cpp
svr.Post("/post", [](const auto &req, auto &res) {
res.set_content(req.body, "text/plain");
});
```
クライアントが送ったリクエストボディを、そのままレスポンスとして返すエコーサーバーです。`req.body` にボディが丸ごと入っています。
### POST /submit
```cpp
svr.Post("/submit", [](const auto &req, auto &res) {
std::string result;
for (auto &[key, val] : req.params) {
result += key + " = " + val + "\n";
}
res.set_content(result, "text/plain");
});
```
フォームデータとして送られたキーと値のペアを、`req.params` でループ処理しています。構造化束縛 `auto &[key, val]` を使って、各ペアを取り出しています。
### POST /upload
```cpp
svr.Post("/upload", [](const auto &req, auto &res) {
auto f = req.form.get_file("file");
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
res.set_content(content, "text/plain");
});
```
マルチパートフォームで送られたファイルを受け取ります。`req.form.get_file("file")``"file"` という名前のフィールドを取得し、`f.filename``f.content.size()` でファイル名とサイズを返しています。
### GET /users/:id
```cpp
svr.Get("/users/:id", [](const auto &req, auto &res) {
auto id = req.path_params.at("id");
res.set_content("User ID: " + id, "text/plain");
});
```
`:id` の部分がパスパラメーターです。`req.path_params.at("id")` で値を取り出しています。`/users/42` なら `"42"``/users/alice` なら `"alice"` が得られます。
### GET /files/(\d+)
```cpp
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
auto id = req.matches[1];
res.set_content("File ID: " + std::string(id), "text/plain");
});
```
正規表現 `(\d+)` で数字だけのIDにマッチします。`/files/42` にはマッチしますが、`/files/abc` は404になります。`req.matches[1]` で最初のキャプチャグループの値を取得しています。
## 次のステップ
サーバーの基本がわかりましたね。ルーティング、リクエストの読み取り、レスポンスの組み立て。これだけで、十分に実用的なAPIサーバーが作れます。
次は、静的ファイルの配信を見てみましょう。HTMLやCSSを配信するサーバーを作ります。
**次:** [Static File Server](../04-static-file-server)
@@ -0,0 +1,134 @@
---
title: "Static File Server"
order: 4
---
cpp-httplibは、HTMLやCSS、画像ファイルなどの静的ファイルも配信できます。面倒な設定は要りません。`set_mount_point()` を1行呼ぶだけです。
## set_mount_point の基本
さっそくやってみましょう。`set_mount_point()` は、URLのパスとローカルディレクトリを紐づけます。
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.set_mount_point("/", "./html");
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
第1引数がURLのマウントポイント、第2引数がローカルのディレクトリパスです。この例だと、`/` へのリクエストを `./html` ディレクトリから配信します。
試してみましょう。まず `html` ディレクトリを作って、`index.html` を置きます。
```sh
mkdir html
```
```html
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body>
<h1>Hello from cpp-httplib!</h1>
<p>This is a static file.</p>
</body>
</html>
```
コンパイルして起動します。
```sh
g++ -std=c++17 -o server server.cpp -pthread
./server
```
ブラウザで `http://localhost:8080` を開いてみてください。`html/index.html` の内容が表示されるはずです。`http://localhost:8080/index.html` でも同じページが返ります。
もちろん、前章のクライアントコードや `curl` でもアクセスできますよ。
```cpp
httplib::Client cli("http://localhost:8080");
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // HTMLが表示される
}
```
```sh
curl http://localhost:8080
```
## 複数のマウントポイント
`set_mount_point()` は何回でも呼べます。URLのパスごとに、別々のディレクトリを割り当てられます。
```cpp
svr.set_mount_point("/", "./public");
svr.set_mount_point("/assets", "./static/assets");
svr.set_mount_point("/docs", "./documentation");
```
`/assets/style.css` なら `./static/assets/style.css` を、`/docs/guide.html` なら `./documentation/guide.html` を配信します。
## ハンドラーとの組み合わせ
静的ファイルの配信と、前章で学んだルーティングハンドラーは共存できます。
```cpp
httplib::Server svr;
// APIエンドポイント
svr.Get("/api/hello", [](const auto &, auto &res) {
res.set_content(R"({"message":"Hello!"})", "application/json");
});
// 静的ファイル配信
svr.set_mount_point("/", "./public");
svr.listen("0.0.0.0", 8080);
```
ハンドラーが先に評価されます。`/api/hello` にはハンドラーが応答し、それ以外のパスは `./public` ディレクトリからファイルを探します。
## レスポンスヘッダーの追加
`set_mount_point()` の第3引数にヘッダーを渡すと、静的ファイルのレスポンスにカスタムヘッダーを付けられます。キャッシュ制御に便利です。
```cpp
svr.set_mount_point("/", "./public", {
{"Cache-Control", "max-age=3600"}
});
```
こうすると、ブラウザは配信されたファイルを1時間キャッシュします。
## 静的ファイルサーバー用のDockerファイル
cpp-httplibのリポジトリには、静的ファイルサーバー用の `Dockerfile` が含まれています。Docker Hubにビルド済みイメージも公開しているので、1コマンドで起動できます。
```sh
> docker run -p 8080:80 -v ./my-site:/html yhirose4dockerhub/cpp-httplib-server
Serving HTTP on 0.0.0.0:80
Mount point: / -> ./html
Press Ctrl+C to shutdown gracefully...
192.168.65.1 - - [22/Feb/2026:12:00:00 +0000] "GET / HTTP/1.1" 200 256 "-" "Mozilla/5.0 ..."
192.168.65.1 - - [22/Feb/2026:12:00:00 +0000] "GET /style.css HTTP/1.1" 200 1024 "-" "Mozilla/5.0 ..."
192.168.65.1 - - [22/Feb/2026:12:00:01 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
```
`./my-site` ディレクトリの中身が、そのままポート8080で配信されます。NGINXと同じログ形式で、アクセスの様子を確認できますよ。
## 次のステップ
静的ファイルの配信ができるようになりましたね。HTMLやCSS、JavaScriptを配信するWebサーバーが、これだけのコードで作れます。
次は、HTTPSで暗号化通信をしてみましょう。まずはTLSライブラリのセットアップからです。
**次:** [TLS Setup](../05-tls-setup)
+88
View File
@@ -0,0 +1,88 @@
---
title: "TLS Setup"
order: 5
---
ここまではHTTP(平文)でやってきましたが、実際のWebではHTTPS(暗号化通信)が当たり前ですよね。cpp-httplibでHTTPSを使うには、TLSライブラリが必要です。
このTourではOpenSSLを使います。最も広く使われていて、情報も豊富です。
## OpenSSLのインストール
お使いのOSに合わせてインストールしましょう。
| OS | インストール方法 |
| -- | ---------------- |
| macOS | [Homebrew](https://brew.sh/) (`brew install openssl`) |
| Ubuntu / Debian | `sudo apt install libssl-dev` |
| Windows | [vcpkg](https://vcpkg.io/) (`vcpkg install openssl`) |
## コンパイルオプション
TLS機能を有効にするには、`CPPHTTPLIB_OPENSSL_SUPPORT` マクロを定義してコンパイルします。前章までのコンパイルコマンドに、いくつかオプションが増えます。
```sh
# macOS (Homebrew)
clang++ -std=c++17 -DCPPHTTPLIB_OPENSSL_SUPPORT \
-I$(brew --prefix openssl)/include \
-L$(brew --prefix openssl)/lib \
-lssl -lcrypto \
-framework CoreFoundation -framework Security \
-o server server.cpp
# Linux
clang++ -std=c++17 -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT \
-lssl -lcrypto \
-o server server.cpp
# Windows (Developer Command Prompt)
cl /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
```
それぞれのオプションの役割を見てみましょう。
- **`-DCPPHTTPLIB_OPENSSL_SUPPORT`** — TLS機能を有効にするマクロ定義
- **`-lssl -lcrypto`** — OpenSSLのライブラリをリンク
- **`-I` / `-L`**macOSのみ)— Homebrew版OpenSSLのパスを指定
- **`-framework CoreFoundation -framework Security`**macOSのみ)— Keychainからシステム証明書を自動で読み込むために必要です
## 動作確認
ちゃんと動くか確認してみましょう。`httplib::Client` にHTTPSのURLを渡してアクセスするだけのプログラムです。
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://www.google.com");
auto res = cli.Get("/");
if (res) {
std::cout << "Status: " << res->status << std::endl;
} else {
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
}
}
```
コンパイルして実行してみてください。`Status: 200` と表示されれば、セットアップ完了です。
## 他のTLSバックエンド
cpp-httplibはOpenSSL以外にも、Mbed TLSとwolfSSLに対応しています。マクロ定義とリンクするライブラリを変えるだけで切り替えられます。
| バックエンド | マクロ定義 | リンクするライブラリ |
| :--- | :--- | :--- |
| OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` |
| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` |
| wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` |
このTourではOpenSSLを前提に進めますが、APIはどのバックエンドでも共通です。
## 次のステップ
TLSの準備ができましたね。次は、HTTPSサイトにリクエストを送ってみましょう。
**次:** [HTTPS Client](../06-https-client)
+122
View File
@@ -0,0 +1,122 @@
---
title: "HTTPS Client"
order: 6
---
前章でOpenSSLのセットアップが済んだので、さっそくHTTPSクライアントを使ってみましょう。2章で使った `httplib::Client` がそのまま使えます。コンストラクタに `https://` 付きのURLを渡すだけです。
## GETリクエスト
実在するHTTPSサイトにアクセスしてみましょう。
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://nghttp2.org");
auto res = cli.Get("/");
if (res) {
std::cout << res->status << std::endl; // 200
std::cout << res->body.substr(0, 100) << std::endl; // HTMLの先頭部分
} else {
std::cout << "Error: " << httplib::to_string(res.error()) << std::endl;
}
}
```
2章では `httplib::Client cli("http://localhost:8080")` と書きましたよね。スキームを `https://` に変えるだけです。`Get()``Post()` など、2章で学んだAPIはすべてそのまま使えます。
```sh
curl https://nghttp2.org/
```
## ポートの指定
HTTPSのデフォルトポートは443です。別のポートを使いたい場合は、URLにポートを含めます。
```cpp
httplib::Client cli("https://localhost:8443");
```
## CA証明書の検証
`httplib::Client` はHTTPS接続時、デフォルトでサーバー証明書を検証します。信頼できるCA(認証局)が発行した証明書を持つサーバーにしか接続しません。
CA証明書は、macOSならKeychain、LinuxならシステムのCA証明書ストア、WindowsならWindowsの証明書ストアから自動で読み込みます。ほとんどの場合、追加の設定は要りません。
### CA証明書ファイルの指定
環境によってはシステムのCA証明書が見つからないこともあります。そのときは `set_ca_cert_path()` でパスを直接指定してください。
```cpp
httplib::Client cli("https://nghttp2.org");
cli.set_ca_cert_path("/etc/ssl/certs/ca-certificates.crt");
auto res = cli.Get("/");
```
```sh
curl --cacert /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
```
### 証明書検証の無効化
開発中、自己署名証明書のサーバーに接続したいときは、検証を無効にできます。
```cpp
httplib::Client cli("https://localhost:8443");
cli.enable_server_certificate_verification(false);
auto res = cli.Get("/");
```
```sh
curl -k https://localhost:8443/
```
本番では絶対に無効にしないでください。中間者攻撃のリスクがあります。
## リダイレクトの追跡
HTTPSサイトへのアクセスでは、リダイレクトに遭遇することがよくあります。たとえば `http://` から `https://` へ、あるいは `www` なしから `www` ありへ転送されるケースです。
デフォルトではリダイレクトを追跡しません。リダイレクト先は `Location` ヘッダーで確認できます。
```cpp
httplib::Client cli("https://nghttp2.org");
auto res = cli.Get("/httpbin/redirect/3");
if (res) {
std::cout << res->status << std::endl; // 302
std::cout << res->get_header_value("Location") << std::endl;
}
```
```sh
curl https://nghttp2.org/httpbin/redirect/3
```
`set_follow_location(true)` を設定すると、リダイレクトを自動で追跡して、最終的なレスポンスを返してくれます。
```cpp
httplib::Client cli("https://nghttp2.org");
cli.set_follow_location(true);
auto res = cli.Get("/httpbin/redirect/3");
if (res) {
std::cout << res->status << std::endl; // 200(最終的なレスポンス)
}
```
```sh
curl -L https://nghttp2.org/httpbin/redirect/3
```
## 次のステップ
HTTPSクライアントの使い方がわかりましたね。次は自分でHTTPSサーバーを立ててみましょう。自己署名証明書の作り方から始めます。
**次:** [HTTPS Server](../07-https-server)
+124
View File
@@ -0,0 +1,124 @@
---
title: "HTTPS Server"
order: 7
---
前章ではHTTPSクライアントを使いました。今度は自分でHTTPSサーバーを立ててみましょう。3章の `httplib::Server``httplib::SSLServer` に置き換えるだけです。
ただし、TLSサーバーにはサーバー証明書と秘密鍵が必要です。まずはそこから準備しましょう。
## 自己署名証明書の作成
開発やテスト用なら、自己署名証明書(いわゆるオレオレ証明書)で十分です。OpenSSLのコマンドでサクッと作れます。
```sh
openssl req -x509 -noenc -keyout key.pem -out cert.pem -subj /CN=localhost
```
これで2つのファイルができます。
- **`cert.pem`** — サーバー証明書
- **`key.pem`** — 秘密鍵
## 最小のHTTPSサーバー
証明書ができたら、さっそくサーバーを書いてみましょう。
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::SSLServer svr("cert.pem", "key.pem");
svr.Get("/", [](const auto &, auto &res) {
res.set_content("Hello, HTTPS!", "text/plain");
});
std::cout << "Listening on https://localhost:8443" << std::endl;
svr.listen("0.0.0.0", 8443);
}
```
`httplib::SSLServer` のコンストラクタに証明書と秘密鍵のパスを渡すだけです。ルーティングの書き方は3章の `httplib::Server` とまったく同じですよ。
コンパイルして起動しましょう。
## 動作確認
サーバーが起動したら、`curl` でアクセスしてみましょう。自己署名証明書なので、`-k` オプションで証明書検証をスキップします。
```sh
curl -k https://localhost:8443/
# Hello, HTTPS!
```
ブラウザで `https://localhost:8443` を開くと、「この接続は安全ではありません」と警告が出ます。自己署名証明書なので正常です。気にせず進めてください。
## クライアントからの接続
前章の `httplib::Client` で接続してみましょう。自己署名証明書のサーバーに接続するには、2つの方法があります。
### 方法1: 証明書検証を無効にする
開発時の手軽な方法です。
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://localhost:8443");
cli.enable_server_certificate_verification(false);
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // Hello, HTTPS!
}
}
```
### 方法2: 自己署名証明書をCA証明書として指定する
こちらのほうが安全です。`cert.pem` をCA証明書として信頼するよう指定します。
```cpp
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include <iostream>
int main() {
httplib::Client cli("https://localhost:8443");
cli.set_ca_cert_path("cert.pem");
auto res = cli.Get("/");
if (res) {
std::cout << res->body << std::endl; // Hello, HTTPS!
}
}
```
この方法なら、指定した証明書のサーバーにだけ接続を許可して、なりすましを防げます。テスト環境でもなるべくこちらを使いましょう。
## Server と SSLServer の比較
3章で学んだ `httplib::Server` のAPIは、`httplib::SSLServer` でもそのまま使えます。違いはコンストラクタだけです。
| | `httplib::Server` | `httplib::SSLServer` |
| -- | ------------------ | -------------------- |
| コンストラクタ | 引数なし | 証明書と秘密鍵のパス |
| プロトコル | HTTP | HTTPS |
| ポート(慣例) | 8080 | 8443 |
| ルーティング | 共通 | 共通 |
HTTPサーバーをHTTPSに切り替えるには、コンストラクタを変えるだけです。
## 次のステップ
HTTPSサーバーが動きましたね。これでHTTP/HTTPSのクライアントとサーバー、両方の基本がそろいました。
次は、cpp-httplibに新しく加わったWebSocket機能を見てみましょう。
**次:** [WebSocket](../08-websocket)
+139
View File
@@ -0,0 +1,139 @@
---
title: "WebSocket"
order: 8
---
cpp-httplibはWebSocketにも対応しています。HTTPのリクエスト/レスポンスと違い、WebSocketはサーバーとクライアントが双方向にメッセージをやり取りできます。チャットやリアルタイム通知に便利です。
さっそく、エコーサーバーとクライアントを作ってみましょう。
## エコーサーバー
受け取ったメッセージをそのまま返すエコーサーバーです。
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::Server svr;
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send(msg); // 受け取ったメッセージをそのまま返す
}
});
std::cout << "Listening on port 8080..." << std::endl;
svr.listen("0.0.0.0", 8080);
}
```
`svr.WebSocket()` でWebSocketハンドラーを登録します。3章の `svr.Get()``svr.Post()` と同じ感覚ですね。
ハンドラーの中では、`ws.read(msg)` でメッセージを待ちます。接続が閉じられると `read()``false` を返すので、ループを抜けます。`ws.send(msg)` でメッセージを送り返します。
## クライアントからの接続
`httplib::ws::WebSocketClient` を使ってサーバーに接続してみましょう。
```cpp
#include "httplib.h"
#include <iostream>
int main() {
httplib::ws::WebSocketClient client("ws://localhost:8080/ws");
if (!client.connect()) {
std::cout << "Connection failed" << std::endl;
return 1;
}
// メッセージを送信
client.send("Hello, WebSocket!");
// サーバーからの応答を受信
std::string msg;
if (client.read(msg)) {
std::cout << msg << std::endl; // Hello, WebSocket!
}
client.close();
}
```
コンストラクタには `ws://host:port/path` 形式のURLを渡します。`connect()` で接続を開始し、`send()``read()` でメッセージをやり取りします。
## テキストとバイナリ
WebSocketにはテキストとバイナリの2種類のメッセージがあります。`read()` の戻り値で区別できます。
```cpp
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
httplib::ws::ReadResult ret;
while ((ret = ws.read(msg))) {
if (ret == httplib::ws::Binary) {
ws.send(msg.data(), msg.size()); // バイナリとして送信
} else {
ws.send(msg); // テキストとして送信
}
}
});
```
- `ws.send(const std::string &)` — テキストメッセージとして送信
- `ws.send(const char *, size_t)` — バイナリメッセージとして送信
クライアント側も同じAPIです。
## リクエスト情報へのアクセス
ハンドラーの第1引数 `req` から、ハンドシェイク時のHTTPリクエスト情報を読み取れます。認証トークンの確認などに便利です。
```cpp
svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
auto token = req.get_header_value("Authorization");
if (token.empty()) {
ws.close(httplib::ws::CloseStatus::PolicyViolation, "unauthorized");
return;
}
std::string msg;
while (ws.read(msg)) {
ws.send(msg);
}
});
```
## WSSで使う
HTTPS上のWebSocket(WSS)にも対応しています。サーバー側は `httplib::SSLServer` にWebSocketハンドラーを登録するだけです。
```cpp
httplib::SSLServer svr("cert.pem", "key.pem");
svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.read(msg)) {
ws.send(msg);
}
});
svr.listen("0.0.0.0", 8443);
```
クライアント側は `wss://` スキームを使います。
```cpp
httplib::ws::WebSocketClient client("wss://localhost:8443/ws");
```
## 次のステップ
WebSocketの基本がわかりましたね。ここまでで Tourは終わりです。
次のページでは、Tourで取り上げなかった機能をまとめて紹介します。
**次:** [What's Next](../09-whats-next)
+228
View File
@@ -0,0 +1,228 @@
---
title: "What's Next"
order: 9
---
Tourお疲れさまでした! cpp-httplibの基本はひと通り押さえましたね。でも、まだまだ便利な機能があります。Tourで取り上げなかった機能をカテゴリー別に紹介します。
## Streaming API
LLMのストリーミング応答や大きなファイルのダウンロードでは、レスポンス全体をメモリに載せたくないですよね。`stream::Get()` を使えば、データをチャンクごとに処理できます。
```cpp
httplib::Client cli("http://localhost:11434");
auto result = httplib::stream::Get(cli, "/api/generate");
if (result) {
while (result.next()) {
std::cout.write(result.data(), result.size());
}
}
```
`Get()``content_receiver` コールバックを渡す方法もあります。こちらはKeep-Aliveと併用できます。
```cpp
httplib::Client cli("http://localhost:8080");
cli.Get("/stream", [](const char *data, size_t len) {
std::cout.write(data, len);
return true;
});
```
サーバー側には `set_content_provider()``set_chunked_content_provider()` があります。サイズがわかっているなら前者、不明なら後者を使ってください。
```cpp
// サイズ指定あり(Content-Length が設定される)
svr.Get("/file", [](const auto &, auto &res) {
auto size = get_file_size("large.bin");
res.set_content_provider(size, "application/octet-stream",
[](size_t offset, size_t length, httplib::DataSink &sink) {
// offset から length バイト分を送る
return true;
});
});
// サイズ不明(Chunked Transfer Encoding
svr.Get("/stream", [](const auto &, auto &res) {
res.set_chunked_content_provider("text/plain",
[](size_t offset, httplib::DataSink &sink) {
sink.write("chunk\n", 6);
return true; // falseを返すと終了
});
});
```
大きなファイルのアップロードには `make_file_provider()` が便利です。ファイルを全部メモリに読み込まず、ストリーミングで送れます。
```cpp
httplib::Client cli("http://localhost:8080");
auto res = cli.Post("/upload", {}, {
httplib::make_file_provider("file", "/path/to/large-file.zip")
});
```
## Server-Sent Events (SSE)
SSEクライアントも用意しています。自動再接続や `Last-Event-ID` による再開にも対応しています。
```cpp
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << msg.event << ": " << msg.data << std::endl;
});
sse.start(); // ブロッキング、自動再接続あり
```
イベントタイプごとにハンドラーを分けることもできますよ。
```cpp
sse.on_event("update", [](const httplib::sse::SSEMessage &msg) {
// "update" イベントだけ処理
});
```
## 認証
クライアントにはBasic認証、Bearer Token認証、Digest認証のヘルパーを用意しています。
```cpp
httplib::Client cli("https://api.example.com");
cli.set_basic_auth("user", "password");
cli.set_bearer_token_auth("my-token");
```
## 圧縮
gzip、Brotli、Zstandardによる圧縮・展開に対応しています。使いたい方式のマクロを定義してコンパイルしましょう。
| 圧縮方式 | マクロ定義 |
| -- | -- |
| gzip | `CPPHTTPLIB_ZLIB_SUPPORT` |
| Brotli | `CPPHTTPLIB_BROTLI_SUPPORT` |
| Zstandard | `CPPHTTPLIB_ZSTD_SUPPORT` |
```cpp
httplib::Client cli("https://example.com");
cli.set_compress(true); // リクエストボディを圧縮
cli.set_decompress(true); // レスポンスボディを展開
```
## プロキシ
HTTPプロキシ経由で接続できます。
```cpp
httplib::Client cli("https://example.com");
cli.set_proxy("proxy.example.com", 8080);
cli.set_proxy_basic_auth("user", "password");
```
## タイムアウト
接続・読み取り・書き込みのタイムアウトを個別に設定できます。
```cpp
httplib::Client cli("https://example.com");
cli.set_connection_timeout(5, 0); // 5秒
cli.set_read_timeout(10, 0); // 10秒
cli.set_write_timeout(10, 0); // 10秒
```
## Keep-Alive
同じサーバーに何度もリクエストするなら、Keep-Aliveを有効にしましょう。TCP接続を再利用するので効率的です。
```cpp
httplib::Client cli("https://example.com");
cli.set_keep_alive(true);
```
## サーバーのミドルウェア
リクエスト処理の前後にフックを挟めます。
```cpp
svr.set_pre_routing_handler([](const auto &req, auto &res) {
// すべてのリクエストの前に実行される
return httplib::Server::HandlerResponse::Unhandled; // 通常のルーティングに進む
});
svr.set_post_routing_handler([](const auto &req, auto &res) {
// レスポンスが返された後に実行される
res.set_header("X-Server", "cpp-httplib");
});
```
`req.user_data` を使うと、ミドルウェアからハンドラーにデータを渡せます。認証トークンのデコード結果を共有するときに便利です。
```cpp
svr.set_pre_routing_handler([](const auto &req, auto &res) {
req.user_data["auth_user"] = std::string("alice");
return httplib::Server::HandlerResponse::Unhandled;
});
svr.Get("/me", [](const auto &req, auto &res) {
auto user = std::any_cast<std::string>(req.user_data.at("auth_user"));
res.set_content("Hello, " + user, "text/plain");
});
```
エラーや例外のハンドラーもカスタマイズできますよ。
```cpp
svr.set_error_handler([](const auto &req, auto &res) {
res.set_content("Custom Error Page", "text/html");
});
svr.set_exception_handler([](const auto &req, auto &res, std::exception_ptr ep) {
res.status = 500;
res.set_content("Internal Server Error", "text/plain");
});
```
## ロギング
サーバーでもクライアントでもロガーを設定できます。
```cpp
svr.set_logger([](const auto &req, const auto &res) {
std::cout << req.method << " " << req.path << " " << res.status << std::endl;
});
```
## Unix Domain Socket
TCP以外に、Unix Domain Socketでの通信にも対応しています。同じマシン上のプロセス間通信に使えます。
```cpp
// サーバー
httplib::Server svr;
svr.set_address_family(AF_UNIX);
svr.listen("/tmp/httplib.sock", 0);
```
```cpp
// クライアント
httplib::Client cli("http://localhost");
cli.set_address_family(AF_UNIX);
cli.set_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
auto res = cli.Get("/");
```
## さらに詳しく
もっと詳しく知りたいときは、以下を参照してください。
- Cookbook — よくあるユースケースのレシピ集
- [README](https://github.com/yhirose/cpp-httplib/blob/master/README.md) — 全APIのリファレンス
- [README-sse](https://github.com/yhirose/cpp-httplib/blob/master/README-sse.md) — Server-Sent Eventsの使い方
- [README-stream](https://github.com/yhirose/cpp-httplib/blob/master/README-stream.md) — Streaming APIの使い方
- [README-websocket](https://github.com/yhirose/cpp-httplib/blob/master/README-websocket.md) — WebSocketサーバーの使い方
+16
View File
@@ -0,0 +1,16 @@
---
title: "A Tour of cpp-httplib"
order: 1
---
cpp-httplibの基本を、順番に学んでいくチュートリアルです。各章は前の章の内容を踏まえて進む構成なので、1章から順に読んでください。
1. [Getting Started](01-getting-started) — httplib.h の入手とHello Worldサーバー
2. [Basic Client](02-basic-client) — GET/POST・パスパラメーターのリクエスト送信
3. [Basic Server](03-basic-server) — ルーティング、パスパラメーター、レスポンスの組み立て
4. [Static File Server](04-static-file-server) — 静的ファイルの配信
5. [TLS Setup](05-tls-setup) — OpenSSL / mbedTLS のセットアップ
6. [HTTPS Client](06-https-client) — HTTPSサイトへのリクエスト
7. [HTTPS Server](07-https-server) — HTTPSサーバーの構築
8. [WebSocket](08-websocket) — WebSocket通信の基本
9. [What's Next](09-whats-next) — さらなる機能の紹介
+603
View File
@@ -0,0 +1,603 @@
:root {
--bg: #333;
--bg-secondary: #3c3c3c;
--bg-code: #2a2a2a;
--text: #ccc;
--text-bright: white;
--text-muted: #999;
--text-code: #b0b0b0;
--text-inline-code: plum;
--border: #555;
--border-code: #3a3a3a;
--link: palegoldenrod;
--heading: lightskyblue;
--heading-link: #f0c090;
--header-nav-link: pink;
--emphasis: pink;
--nav-section: #bbb;
--nav-section-active: #ddd;
--content-width: 900px;
--sidebar-width: 280px;
--header-height: 48px;
--line-height: 1.6;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: var(--line-height);
}
a {
color: var(--link);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Header */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--header-height);
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border);
z-index: 100;
}
.header-inner {
height: 100%;
display: flex;
align-items: center;
padding: 0 16px;
gap: 24px;
}
.header-title {
color: var(--text);
font-weight: bold;
font-size: 1.1rem;
white-space: nowrap;
}
.header-title:hover {
text-decoration: none;
color: var(--text-bright);
}
.header-spacer {
flex: 1;
}
.header-nav {
display: flex;
gap: 16px;
}
.header-nav a {
display: flex;
align-items: center;
gap: 4px;
color: var(--header-nav-link);
font-size: 0.9rem;
}
.header-nav a svg {
flex-shrink: 0;
opacity: 0.85;
}
.header-tools {
display: flex;
align-items: center;
gap: 2px;
}
.lang-selector {
position: relative;
}
.lang-btn {
display: flex;
align-items: center;
gap: 5px;
background: none;
border: none;
color: var(--text);
padding: 4px 6px;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
opacity: 0.8;
}
.lang-btn:hover {
opacity: 1;
}
.lang-popup {
display: none;
position: absolute;
right: 0;
top: 100%;
margin-top: 4px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 4px;
list-style: none;
min-width: 60px;
z-index: 200;
}
.lang-popup.open {
display: block;
}
.lang-popup li a {
display: block;
padding: 6px 12px;
color: var(--text);
font-size: 0.85rem;
}
.lang-popup li a:hover {
background: var(--bg);
text-decoration: none;
}
.sidebar-toggle {
display: none;
background: none;
border: none;
color: var(--text);
font-size: 1.2rem;
cursor: pointer;
padding: 4px 8px;
}
/* Draft banner */
.draft-banner {
position: fixed;
top: var(--header-height);
right: 0;
background: #c44;
color: white;
padding: 4px 16px;
font-size: 0.75rem;
font-weight: bold;
letter-spacing: 0.1em;
z-index: 99;
}
/* Layout */
.layout {
margin-top: var(--header-height);
display: grid;
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
min-height: calc(100vh - var(--header-height));
}
.layout.no-sidebar {
grid-template-columns: 1fr;
}
/* Sidebar */
.sidebar {
width: var(--sidebar-width);
flex-shrink: 0;
padding: 24px 16px;
border-right: 1px solid var(--bg-secondary);
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
overflow-y: auto;
}
.nav-section {
margin-bottom: 16px;
}
.nav-section-title {
color: var(--nav-section);
font-weight: bold;
font-size: 1.0rem;
display: block;
margin-bottom: 8px;
}
.nav-section-title.active {
color: var(--nav-section-active);
}
.nav-list {
list-style: none;
padding-left: 8px;
}
.nav-list li {
margin-bottom: 4px;
}
.nav-list li a {
color: var(--text-muted);
font-size: 0.85rem;
}
.nav-list li a:hover {
color: var(--text);
}
.nav-list li a.active {
color: var(--emphasis);
font-weight: bold;
}
/* Content */
.content {
min-width: 0;
max-width: var(--content-width);
padding: 32px 24px;
overflow-wrap: break-word;
}
.content.portal {
max-width: var(--content-width);
padding: 48px 24px;
margin: 0 auto;
}
.content article h1 {
font-size: 1.8rem;
margin-bottom: 24px;
color: var(--heading);
}
.content article h2 {
font-size: 1.4rem;
margin-top: 32px;
margin-bottom: 16px;
color: var(--heading-link);
}
.content article h3 {
font-size: 1.1rem;
margin-top: 24px;
margin-bottom: 12px;
color: var(--text);
}
.content article p {
margin-bottom: 12px;
}
.content article ul,
.content article ol {
margin-bottom: 12px;
padding-left: 24px;
}
.content article li {
margin-bottom: 4px;
}
.content article strong {
color: var(--emphasis);
}
.content article code {
background: var(--bg-code);
color: var(--text-inline-code);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
.content article pre {
background: var(--bg-code);
color: var(--text-code);
padding: 16px;
border-radius: 4px;
overflow-x: auto;
margin-bottom: 16px;
border: 1px solid var(--border-code);
}
.content article pre code {
background: none;
padding: 0;
}
.content article table {
width: 100%;
border-collapse: collapse;
margin-bottom: 16px;
}
.content article th,
.content article td {
border: 1px solid var(--bg-secondary);
padding: 8px 12px;
text-align: left;
}
.content article th {
background: var(--bg-secondary);
}
.content article blockquote {
border-left: 3px solid var(--text-muted);
padding-left: 16px;
margin-bottom: 12px;
color: var(--text-muted);
}
/* Code block theme switching: show dark by default, swap on light mode */
.code-block-wrapper > [data-code-theme="light"] {
display: none;
}
html[data-theme="light"] .code-block-wrapper > [data-code-theme="dark"] {
display: none;
}
html[data-theme="light"] .code-block-wrapper > [data-code-theme="light"] {
display: block;
}
/* Footer */
.footer {
padding: 12px 16px;
text-align: center;
color: var(--text-muted);
font-size: 0.8rem;
border-top: 1px solid var(--bg-secondary);
}
/* Responsive */
@media (max-width: 768px) {
.layout {
grid-template-columns: minmax(0, 1fr);
}
.sidebar {
position: fixed;
left: calc(-1 * var(--sidebar-width));
width: var(--sidebar-width);
top: var(--header-height);
height: calc(100vh - var(--header-height));
background: var(--bg);
z-index: 50;
transition: left 0.2s ease;
border-right: 1px solid var(--border);
}
.sidebar.open {
left: 0;
}
.sidebar-toggle {
display: block;
}
.content {
padding: 24px 16px;
}
}
@media (max-width: 480px) {
:root {
--header-height: 44px;
}
.header-inner {
padding: 0 12px;
gap: 12px;
}
.header-nav a {
font-size: 0.8rem;
}
.content article h1 {
font-size: 1.4rem;
}
.content article h2 {
font-size: 1.2rem;
}
}
/* Light mode */
[data-theme="light"] {
--bg: #f5f5f5;
--bg-secondary: #e8e8e8;
--bg-code: #eee;
--text: #333;
--text-bright: #000;
--text-muted: #666;
--text-code: #333;
--text-inline-code: #8b5ca0;
--border: #ccc;
--border-code: #ddd;
--link: #b8860b;
--heading: #2a6496;
--heading-link: #c06020;
--header-nav-link: #c04060;
--emphasis: #c04060;
--nav-section: #666;
--nav-section-active: #333;
}
/* Theme toggle */
.theme-toggle {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--text);
padding: 5px 6px;
border-radius: 4px;
cursor: pointer;
opacity: 0.8;
}
.theme-toggle:hover {
opacity: 1;
}
/* Search button */
.search-btn {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--text);
padding: 5px 6px;
border-radius: 4px;
cursor: pointer;
opacity: 0.8;
}
.search-btn:hover {
opacity: 1;
}
/* Search overlay & modal */
.search-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 300;
justify-content: center;
align-items: flex-start;
padding-top: 12vh;
}
.search-overlay.open {
display: flex;
}
.search-modal {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
width: 90%;
max-width: 560px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
overflow: hidden;
}
.search-input-wrap {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
}
.search-input-wrap svg {
flex-shrink: 0;
opacity: 0.5;
}
#search-input {
flex: 1;
background: none;
border: none;
outline: none;
color: var(--text-bright);
font-size: 1rem;
font-family: inherit;
}
#search-input::placeholder {
color: var(--text-muted);
}
.search-esc {
background: var(--bg);
color: var(--text-muted);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.7rem;
border: 1px solid var(--border);
}
.search-results {
list-style: none;
max-height: 50vh;
overflow-y: auto;
padding: 0;
margin: 0;
}
.search-results:empty::after {
content: "";
display: none;
}
.search-results li {
padding: 10px 16px;
cursor: pointer;
border-bottom: 1px solid var(--border);
}
.search-results li:last-child {
border-bottom: none;
}
.search-results li:hover,
.search-results li.active {
background: var(--bg);
}
.search-results li .search-result-title {
color: var(--text-bright);
font-weight: 600;
font-size: 0.9rem;
margin-bottom: 2px;
}
.search-results li .search-result-snippet {
color: var(--text-muted);
font-size: 0.8rem;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.search-results li .search-result-snippet mark {
background: rgba(255, 215, 0, 0.3);
color: var(--text-bright);
border-radius: 2px;
padding: 0 1px;
}
[data-theme="light"] .search-results li .search-result-snippet mark {
background: rgba(255, 200, 0, 0.4);
color: var(--text);
}
.search-no-results {
padding: 24px 16px;
text-align: center;
color: var(--text-muted);
font-size: 0.9rem;
}
+109
View File
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cookbook - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;cookbook&#x2F;" class="nav-section-title active">Cookbook</a>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Cookbook</h1>
<p>This section is under construction.</p>
<p>Check back soon for a collection of recipes organized by topic.</p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>cpp-httplib - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
</div>
</header>
<div class="layout no-sidebar">
<main class="content portal">
<article>
<h1>cpp-httplib</h1>
<p><a href="https://github.com/yhirose/cpp-httplib">cpp-httplib</a> is an HTTP/HTTPS library for C++. Just copy a single header file, <a href="https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h"><code>httplib.h</code></a>, and you're ready to go.</p>
<p>When you need a quick HTTP server or client in C++, you want something that just works. That's exactly why I built cpp-httplib. You can start writing both servers and clients in just a few lines of code.</p>
<p>The API uses a lambda-based design that feels natural. It runs anywhere you have a C++11 or later compiler. Windows, macOS, Linux — use whatever environment you already have.</p>
<p>HTTPS works too. Just link OpenSSL or mbedTLS, and both server and client gain TLS support. Content-Encoding (gzip, Brotli, etc.), file uploads, and other features you actually need in real-world development are all included. WebSocket is also supported.</p>
<p>Under the hood, it uses blocking I/O with a thread pool. It's not built for handling massive numbers of simultaneous connections. But for API servers, embedded HTTP in tools, mock servers for testing, and many other use cases, it delivers solid performance.</p>
<p>"Solve today's problem, today." That's the kind of simplicity cpp-httplib aims for.</p>
<h2>Documentation</h2>
<ul>
<li><a href="tour/">A Tour of cpp-httplib</a> — A step-by-step tutorial covering the basics. Start here if you're new</li>
<li><a href="cookbook/">Cookbook</a> — A collection of recipes organized by topic. Jump to whatever you need</li>
</ul>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+234
View File
@@ -0,0 +1,234 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Getting Started - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="active">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Getting Started</h1>
<p>All you need to get started with cpp-httplib is <code>httplib.h</code> and a C++ compiler. Let's download the file and get a Hello World server running.</p>
<h2>Getting httplib.h</h2>
<p>You can download it directly from GitHub. Always use the latest version.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -LO</span><span style="color:#d3d0c8;"> https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -LO https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
</span></pre>
</div></div>
<p>Place the downloaded <code>httplib.h</code> in your project directory and you're good to go.</p>
<h2>Setting Up Your Compiler</h2>
<table><thead><tr><th>OS</th><th>Development Environment</th><th>Setup</th></tr></thead><tbody>
<tr><td>macOS</td><td>Apple Clang</td><td>Xcode Command Line Tools (<code>xcode-select --install</code>)</td></tr>
<tr><td>Ubuntu</td><td>clang++ or g++</td><td><code>apt install clang</code> or <code>apt install g++</code></td></tr>
<tr><td>Windows</td><td>MSVC</td><td>Visual Studio 2022 or later (install with C++ components)</td></tr>
</tbody></table>
<h2>Hello World Server</h2>
<p>Save the following code as <code>server.cpp</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request&amp;, httplib::Response&amp; res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, World!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::Response</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;"> res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, World!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>In just a few lines, you have a server that responds to HTTP requests.</p>
<h2>Compiling and Running</h2>
<p>The sample code in this tutorial is written in C++17 for cleaner, more concise code. cpp-httplib itself can compile with C++11 as well.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Linux
</span><span style="color:#747369;"># `-pthread`: cpp-httplib uses threads internally
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -pthread -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows (Developer Command Prompt)
</span><span style="color:#747369;"># `/EHsc`: Enable C++ exception handling
</span><span style="color:#6699cc;">cl</span><span style="color:#d3d0c8;"> /EHsc /std:c++17 server.cpp
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Linux
</span><span style="font-style:italic;color:#969896;"># `-pthread`: cpp-httplib uses threads internally
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -pthread -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows (Developer Command Prompt)
</span><span style="font-style:italic;color:#969896;"># `/EHsc`: Enable C++ exception handling
</span><span style="color:#323232;">cl /EHsc /std:c++17 server.cpp
</span></pre>
</div></div>
<p>Once it compiles, run it.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS / Linux
</span><span style="color:#6699cc;">./server
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows
</span><span style="color:#6699cc;">server.exe
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS / Linux
</span><span style="color:#323232;">./server
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows
</span><span style="color:#323232;">server.exe
</span></pre>
</div></div>
<p>Open <code>http://localhost:8080</code> in your browser. If you see "Hello, World!", you're all set.</p>
<p>You can also verify with <code>curl</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/
</span><span style="color:#747369;"># Hello, World!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/
</span><span style="font-style:italic;color:#969896;"># Hello, World!
</span></pre>
</div></div>
<p>To stop the server, press <code>Ctrl+C</code> in your terminal.</p>
<h2>Next Steps</h2>
<p>Now you know the basics of running a server. Next, let's look at the client side. cpp-httplib also comes with HTTP client functionality.</p>
<p><strong>Next:</strong> <a href="../02-basic-client">Basic Client</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+527
View File
@@ -0,0 +1,527 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basic Client - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="active">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Basic Client</h1>
<p>cpp-httplib isn't just for servers -- it also comes with a full HTTP client. Let's use <code>httplib::Client</code> to send GET and POST requests.</p>
<h2>Preparing a Test Server</h2>
<p>To try out the client, you need a server that accepts requests. Save the following code, then compile and run it the same way you did in the previous chapter. We'll cover the server details in the next chapter.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">];
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">];
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<h2>GET Request</h2>
<p>Once the server is running, open a separate terminal and give it a try. Let's start with the simplest GET request.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>Pass the server address to the <code>httplib::Client</code> constructor, then call <code>Get()</code> to send a request. You can retrieve the status code and body from the returned <code>res</code>.</p>
<p>Here's the equivalent <code>curl</code> command.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/hi
</span><span style="color:#747369;"># Hello!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/hi
</span><span style="font-style:italic;color:#969896;"># Hello!
</span></pre>
</div></div>
<h2>Checking the Response</h2>
<p>A response contains header information in addition to the status code and body.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Status code
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Body
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello!
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Headers
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Content-Type</span><span style="color:#d3d0c8;">&quot;) &lt;&lt; std::endl; </span><span style="color:#747369;">// text/plain
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Status code
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Body
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello!
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Headers
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;get_header_value(</span><span style="color:#183691;">&quot;Content-Type&quot;</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// text/plain
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>res-&gt;body</code> is a <code>std::string</code>, so if you want to parse a JSON response, you can pass it directly to a JSON library like <a href="https://github.com/nlohmann/json">nlohmann/json</a>.</p>
<h2>Query Parameters</h2>
<p>To add query parameters to a GET request, you can either write them directly in the URL or use <code>httplib::Params</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, httplib::Params{{&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">cpp-httplib</span><span style="color:#d3d0c8;">&quot;}});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Query: cpp-httplib
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, httplib::Params{{</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;cpp-httplib&quot;</span><span style="color:#323232;">}});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Query: cpp-httplib
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>httplib::Params</code> automatically URL-encodes special characters for you.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">http://localhost:8080/search?q=cpp-httplib</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#747369;"># Query: cpp-httplib
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl </span><span style="color:#183691;">&quot;http://localhost:8080/search?q=cpp-httplib&quot;
</span><span style="font-style:italic;color:#969896;"># Query: cpp-httplib
</span></pre>
</div></div>
<h2>Path Parameters</h2>
<p>When values are embedded directly in the URL path, no special client API is needed. Just pass the path to <code>Get()</code> as-is.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/42</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// User ID: 42
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/users/42&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// User ID: 42
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/users/42
</span><span style="color:#747369;"># User ID: 42
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/users/42
</span><span style="font-style:italic;color:#969896;"># User ID: 42
</span></pre>
</div></div>
<p>The test server also has a <code>/files/(\d+)</code> route that uses a regex to accept numeric IDs only.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/files/42</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// File ID: 42
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/files/42&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// File ID: 42
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/files/42
</span><span style="color:#747369;"># File ID: 42
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/files/42
</span><span style="font-style:italic;color:#969896;"># File ID: 42
</span></pre>
</div></div>
<p>Pass a non-numeric ID like <code>/files/abc</code> and you'll get a 404. We'll cover how that works in the next chapter.</p>
<h2>Request Headers</h2>
<p>To add custom HTTP headers, pass an <code>httplib::Headers</code> object. This works with both <code>Get()</code> and <code>Post()</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, httplib::Headers{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Bearer my-token</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, httplib::Headers{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Bearer my-token&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -H </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Authorization: Bearer my-token</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/hi
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -H </span><span style="color:#183691;">&quot;Authorization: Bearer my-token&quot;</span><span style="color:#323232;"> http://localhost:8080/hi
</span></pre>
</div></div>
<h2>POST Request</h2>
<p>Let's POST some text data. Pass the body as the second argument to <code>Post()</code> and the Content-Type as the third.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Hello, Server!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, Server!
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Hello, Server!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, Server!
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>The test server's <code>/post</code> endpoint echoes the body back, so you get the same string you sent.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -X</span><span style="color:#d3d0c8;"> POST</span><span style="color:#f2777a;"> -H </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Content-Type: text/plain</span><span style="color:#d3d0c8;">&quot;</span><span style="color:#f2777a;"> -d </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Hello, Server!</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/post
</span><span style="color:#747369;"># Hello, Server!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -X POST -H </span><span style="color:#183691;">&quot;Content-Type: text/plain&quot;</span><span style="color:#323232;"> -d </span><span style="color:#183691;">&quot;Hello, Server!&quot;</span><span style="color:#323232;"> http://localhost:8080/post
</span><span style="font-style:italic;color:#969896;"># Hello, Server!
</span></pre>
</div></div>
<h2>Sending Form Data</h2>
<p>You can send key-value pairs just like an HTML form. Use <code>httplib::Params</code> for this.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, httplib::Params{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">name</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Alice</span><span style="color:#d3d0c8;">&quot;},
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">age</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">30</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// age = 30
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// name = Alice
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, httplib::Params{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;name&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Alice&quot;</span><span style="color:#323232;">},
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;age&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;30&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// age = 30
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// name = Alice
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>This sends the data in <code>application/x-www-form-urlencoded</code> format.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -X</span><span style="color:#d3d0c8;"> POST</span><span style="color:#f2777a;"> -d </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">name=Alice&amp;age=30</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/submit
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -X POST -d </span><span style="color:#183691;">&quot;name=Alice&amp;age=30&quot;</span><span style="color:#323232;"> http://localhost:8080/submit
</span></pre>
</div></div>
<h2>POSTing a File</h2>
<p>To upload a file, use <code>httplib::UploadFormDataItems</code> to send it as multipart form data.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, httplib::UploadFormDataItems{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Hello, File!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">hello.txt</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// hello.txt (12 bytes)
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, httplib::UploadFormDataItems{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Hello, File!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;hello.txt&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// hello.txt (12 bytes)
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>Each element in <code>UploadFormDataItems</code> has four fields: <code>{name, content, filename, content_type}</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -F </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">file=Hello, File!;filename=hello.txt;type=text/plain</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/upload
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -F </span><span style="color:#183691;">&quot;file=Hello, File!;filename=hello.txt;type=text/plain&quot;</span><span style="color:#323232;"> http://localhost:8080/upload
</span></pre>
</div></div>
<h2>Error Handling</h2>
<p>Network communication can fail -- the server might not be reachable. Always check whether <code>res</code> is valid.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:9999</span><span style="color:#d3d0c8;">&quot;); </span><span style="color:#747369;">// Non-existent port
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(!res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Connection error
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Error: Connection
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// If we reach here, we have a response
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">!= </span><span style="color:#f99157;">200</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">HTTP Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:9999&quot;</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// Non-existent port
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">!</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Connection error
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Error: Connection
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// If we reach here, we have a response
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">!= </span><span style="color:#0086b3;">200</span><span style="color:#323232;">) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;HTTP Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span></pre>
</div></div>
<p>There are two levels of errors.</p>
<ul>
<li><strong>Connection error</strong>: The client couldn't reach the server. <code>res</code> evaluates to false, and you can call <code>res.error()</code> to find out what went wrong.</li>
<li><strong>HTTP error</strong>: The server returned an error status (404, 500, etc.). <code>res</code> evaluates to true, but you need to check <code>res-&gt;status</code>.</li>
</ul>
<h2>Next Steps</h2>
<p>Now you know how to send requests from a client. Next, let's take a closer look at the server side. We'll dig into routing, path parameters, and more.</p>
<p><strong>Next:</strong> <a href="../03-basic-server">Basic Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+482
View File
@@ -0,0 +1,482 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basic Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="active">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Basic Server</h1>
<p>In the previous chapter, you sent requests from a client to a test server. Now let's walk through how that server actually works.</p>
<h2>Starting the Server</h2>
<p>Once you've registered your routes, call <code>svr.listen()</code> to start the server.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>The first argument is the host, and the second is the port. <code>"0.0.0.0"</code> listens on all network interfaces. Use <code>"127.0.0.1"</code> if you want to accept connections from your own machine only.</p>
<p><code>listen()</code> is a blocking call. It won't return until the server stops. The server keeps running until you press <code>Ctrl+C</code> in your terminal or call <code>svr.stop()</code> from another thread.</p>
<h2>Routing</h2>
<p>Routing is the heart of any server. It's how you tell cpp-httplib: when a request comes in for this URL with this HTTP method, run this code.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;req, httplib::Response &amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">req, httplib::Response </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>svr.Get()</code> registers a handler for GET requests. The first argument is the path, the second is the handler function. When a GET request arrives at <code>/hi</code>, your lambda runs.</p>
<p>There's a method for each HTTP verb.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// GET
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// POST
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Put</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// PUT
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Delete</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// DELETE
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// GET
</span><span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// POST
</span><span style="color:#323232;">svr.Put(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// PUT
</span><span style="color:#323232;">svr.Delete(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// DELETE
</span></pre>
</div></div>
<p>The handler signature is <code>(const httplib::Request &amp;req, httplib::Response &amp;res)</code>. You can use <code>auto</code> to keep it short.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>The handler only runs when the path matches. Requests to unregistered paths automatically return 404.</p>
<h2>The Request Object</h2>
<p>The first parameter <code>req</code> gives you everything the client sent.</p>
<h3>Body</h3>
<p><code>req.body</code> holds the request body as a <code>std::string</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Echo the body back to the client
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Echo the body back to the client
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>Headers</h3>
<p>Use <code>req.get_header_value()</code> to read a request header.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/check</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> auth = req.</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Auth: </span><span style="color:#d3d0c8;">&quot; + auth, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/check&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> auth </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_header_value(</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Auth: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> auth, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>Query Parameters and Form Data</h3>
<p><code>req.get_param_value()</code> retrieves a parameter by name. It works for both GET query parameters and POST form data.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>A request to <code>/search?q=cpp-httplib</code> gives you <code>"cpp-httplib"</code> for <code>q</code>.</p>
<p>To loop over all parameters, use <code>req.params</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>File Uploads</h3>
<p>Files uploaded via multipart form data are available through <code>req.form.get_file()</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>f.filename</code> gives you the filename, and <code>f.content</code> gives you the file data.</p>
<h2>Path Parameters</h2>
<p>Sometimes you want to capture part of the URL as a variable -- for example, the <code>42</code> in <code>/users/42</code>. Use the <code>:param</code> syntax to do that.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>A request to <code>/users/42</code> gives you <code>"42"</code> from <code>req.path_params.at("id")</code>. <code>/users/100</code> gives you <code>"100"</code>.</p>
<p>You can capture multiple segments at once.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:user_id/posts/:post_id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> user_id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user_id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> post_id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">post_id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User: </span><span style="color:#d3d0c8;">&quot; + user_id + &quot;</span><span style="color:#99cc99;">, Post: </span><span style="color:#d3d0c8;">&quot; + post_id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:user_id/posts/:post_id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> user_id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;user_id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> post_id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;post_id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> user_id </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;, Post: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> post_id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>Regex Patterns</h3>
<p>You can also write a regular expression directly in the path instead of <code>:param</code>. Capture group values are available via <code>req.matches</code>, which is a <code>std::smatch</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// Only accept numeric IDs
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">]; </span><span style="color:#747369;">// First capture group
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// Only accept numeric IDs
</span><span style="color:#323232;">svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">]; </span><span style="font-style:italic;color:#969896;">// First capture group
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>/files/42</code> matches, but <code>/files/abc</code> doesn't. This is handy when you want to constrain what values are accepted.</p>
<h2>Building a Response</h2>
<p>The second parameter <code>res</code> is how you send data back to the client.</p>
<h3>Body and Content-Type</h3>
<p><code>res.set_content()</code> sets the body and Content-Type. That's all you need for a 200 response.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>Status Code</h3>
<p>To return a different status code, assign to <code>res.status</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/not-found</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">= </span><span style="color:#f99157;">404</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Not found</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/not-found&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">404</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Not found&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>Response Headers</h3>
<p>Add response headers with <code>res.set_header()</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/with-header</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_header</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">X-Custom</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">my-value</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/with-header&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_header(</span><span style="color:#183691;">&quot;X-Custom&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;my-value&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Walking Through the Test Server</h2>
<p>Now let's use what we've learned to read through the test server from the previous chapter.</p>
<h3>GET /hi</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>The simplest possible handler. We don't need any information from the request, so the <code>req</code> parameter is left unnamed. It just returns <code>"Hello!"</code>.</p>
<h3>GET /search</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>req.get_param_value("q")</code> pulls out the query parameter <code>q</code>. A request to <code>/search?q=cpp-httplib</code> returns <code>"Query: cpp-httplib"</code>.</p>
<h3>POST /post</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>An echo server. Whatever body the client sends, <code>req.body</code> holds it, and we send it straight back.</p>
<h3>POST /submit</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>Loops over the form data in <code>req.params</code> using structured bindings (<code>auto &amp;[key, val]</code>) to unpack each key-value pair.</p>
<h3>POST /upload</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>Receives a file uploaded via multipart form data. <code>req.form.get_file("file")</code> fetches the field named <code>"file"</code>, and we respond with the filename and size.</p>
<h3>GET /users/:id</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>:id</code> is the path parameter. <code>req.path_params.at("id")</code> retrieves its value. <code>/users/42</code> gives you <code>"42"</code>, <code>/users/alice</code> gives you <code>"alice"</code>.</p>
<h3>GET /files/(\d+)</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">];
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">];
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>The regex <code>(\d+)</code> matches numeric IDs only. <code>/files/42</code> hits this handler, but <code>/files/abc</code> returns 404. <code>req.matches[1]</code> retrieves the first capture group.</p>
<h2>Next Steps</h2>
<p>You now have the full picture of how a server works. Routing, reading requests, building responses -- that's enough to build a real API server.</p>
<p>Next, let's look at serving static files. We'll build a server that delivers HTML and CSS.</p>
<p><strong>Next:</strong> <a href="../04-static-file-server">Static File Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Static File Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="active">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Static File Server</h1>
<p>cpp-httplib can serve static files too — HTML, CSS, images, you name it. No complicated configuration required. One call to <code>set_mount_point()</code> is all it takes.</p>
<h2>The basics of set_mount_point</h2>
<p>Let's jump right in. <code>set_mount_point()</code> maps a URL path to a local directory.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./html</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./html&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>The first argument is the URL mount point. The second is the local directory path. In this example, requests to <code>/</code> are served from the <code>./html</code> directory.</p>
<p>Let's try it out. First, create an <code>html</code> directory and add an <code>index.html</code> file.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">mkdir</span><span style="color:#d3d0c8;"> html
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">mkdir html
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;!</span><span style="color:#f2777a;">DOCTYPE</span><span style="color:#d3d0c8;"> html&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">html</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">head</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">title</span><span style="color:#d3d0c8;">&gt;My Page&lt;/</span><span style="color:#f2777a;">title</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">head</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;"> &lt;</span><span style="color:#f2777a;">h1</span><span style="color:#d3d0c8;">&gt;Hello from cpp-httplib!&lt;/</span><span style="color:#f2777a;">h1</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;"> &lt;</span><span style="color:#f2777a;">p</span><span style="color:#d3d0c8;">&gt;This is a static file.&lt;/</span><span style="color:#f2777a;">p</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">html</span><span style="color:#d3d0c8;">&gt;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">&lt;!</span><span style="color:#63a35c;">DOCTYPE</span><span style="color:#323232;"> html&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">html</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">head</span><span style="color:#323232;">&gt;&lt;</span><span style="color:#63a35c;">title</span><span style="color:#323232;">&gt;My Page&lt;/</span><span style="color:#63a35c;">title</span><span style="color:#323232;">&gt;&lt;/</span><span style="color:#63a35c;">head</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">body</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;"> &lt;</span><span style="color:#63a35c;">h1</span><span style="color:#323232;">&gt;Hello from cpp-httplib!&lt;/</span><span style="color:#63a35c;">h1</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;"> &lt;</span><span style="color:#63a35c;">p</span><span style="color:#323232;">&gt;This is a static file.&lt;/</span><span style="color:#63a35c;">p</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;/</span><span style="color:#63a35c;">body</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;/</span><span style="color:#63a35c;">html</span><span style="color:#323232;">&gt;
</span></pre>
</div></div>
<p>Compile and start the server.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">g++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp</span><span style="color:#f2777a;"> -pthread
</span><span style="color:#6699cc;">./server
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">g++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -o server server.cpp -pthread
</span><span style="color:#323232;">./server
</span></pre>
</div></div>
<p>Open <code>http://localhost:8080</code> in your browser. You should see the contents of <code>html/index.html</code>. Visiting <code>http://localhost:8080/index.html</code> returns the same page.</p>
<p>You can also access it with the client code from the previous chapter, or with <code>curl</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// HTML is displayed
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// HTML is displayed
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080
</span></pre>
</div></div>
<h2>Multiple mount points</h2>
<p>You can call <code>set_mount_point()</code> as many times as you like. Each URL path gets its own directory.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/assets</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./static/assets</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/docs</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./documentation</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/assets&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./static/assets&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/docs&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./documentation&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>A request to <code>/assets/style.css</code> serves <code>./static/assets/style.css</code>. A request to <code>/docs/guide.html</code> serves <code>./documentation/guide.html</code>.</p>
<h2>Combining with handlers</h2>
<p>Static file serving and routing handlers — the kind you learned about in the previous chapter — work side by side.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// API endpoint
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/api/hello</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">{&quot;message&quot;:&quot;Hello!&quot;}</span><span style="color:#d3d0c8;">)&quot;, &quot;</span><span style="color:#99cc99;">application/json</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// Static file serving
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// API endpoint
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/api/hello&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;({&quot;message&quot;:&quot;Hello!&quot;})&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;application/json&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// Static file serving
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>Handlers take priority. The handler responds to <code>/api/hello</code>. For every other path, the server looks for a file in <code>./public</code>.</p>
<h2>Adding response headers</h2>
<p>Pass headers as the third argument to <code>set_mount_point()</code> and they get attached to every static file response. This is great for cache control.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;, {
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">Cache-Control</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">max-age=3600</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">, {
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;Cache-Control&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;max-age=3600&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>With this in place, the browser caches served files for one hour.</p>
<h2>A Dockerfile for your static file server</h2>
<p>The cpp-httplib repository includes a <code>Dockerfile</code> built for static file serving. We also publish a pre-built image on Docker Hub, so you can get up and running with a single command.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&gt; docker run</span><span style="color:#f2777a;"> -p</span><span style="color:#d3d0c8;"> 8080:80</span><span style="color:#f2777a;"> -v</span><span style="color:#d3d0c8;"> ./my-site:/html yhirose4dockerhub/cpp-httplib-server
</span><span style="color:#6699cc;">Serving</span><span style="color:#d3d0c8;"> HTTP on 0.0.0.0:80
</span><span style="color:#6699cc;">Mount</span><span style="color:#d3d0c8;"> point: / -&gt; ./html
</span><span style="color:#6699cc;">Press</span><span style="color:#d3d0c8;"> Ctrl+C to shutdown gracefully...
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:00 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET / HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 200 256 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:00 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET /style.css HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 200 1024 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:01 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET /favicon.ico HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 404 152 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> docker run -p 8080:80 -v ./my-site:/html yhirose4dockerhub/cpp-httplib-server
</span><span style="color:#323232;">Serving HTTP on 0.0.0.0:80
</span><span style="color:#323232;">Mount point: / -</span><span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> ./html
</span><span style="color:#323232;">Press Ctrl+C to shutdown gracefully...
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:00 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET / HTTP/1.1&quot;</span><span style="color:#323232;"> 200 256 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:00 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET /style.css HTTP/1.1&quot;</span><span style="color:#323232;"> 200 1024 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:01 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET /favicon.ico HTTP/1.1&quot;</span><span style="color:#323232;"> 404 152 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span></pre>
</div></div>
<p>Everything in your <code>./my-site</code> directory gets served on port 8080. The access log follows the same format as NGINX, so you can see exactly what's happening.</p>
<h2>What's next</h2>
<p>You can now serve static files. A web server that delivers HTML, CSS, and JavaScript — built with this little code.</p>
<p>Next, let's encrypt your connections with HTTPS. We'll start by setting up a TLS library.</p>
<p><strong>Next:</strong> <a href="../05-tls-setup">TLS Setup</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+229
View File
@@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TLS Setup - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="active">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>TLS Setup</h1>
<p>So far we've been using plain HTTP, but in the real world, HTTPS is the norm. To use HTTPS with cpp-httplib, you need a TLS library.</p>
<p>In this tour, we'll use OpenSSL. It's the most widely used option, and you'll find plenty of resources online.</p>
<h2>Installing OpenSSL</h2>
<p>Install it for your OS.</p>
<table><thead><tr><th>OS</th><th>How to install</th></tr></thead><tbody>
<tr><td>macOS</td><td><a href="https://brew.sh/">Homebrew</a> (<code>brew install openssl</code>)</td></tr>
<tr><td>Ubuntu / Debian</td><td><code>sudo apt install libssl-dev</code></td></tr>
<tr><td>Windows</td><td><a href="https://vcpkg.io/">vcpkg</a> (<code>vcpkg install openssl</code>)</td></tr>
</tbody></table>
<h2>Compile Options</h2>
<p>To enable TLS, define the <code>CPPHTTPLIB_OPENSSL_SUPPORT</code> macro when compiling. You'll need a few extra options compared to the previous chapters.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS (Homebrew)
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -DCPPHTTPLIB_OPENSSL_SUPPORT </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -I</span><span style="color:#d3d0c8;">$</span><span style="color:#f2777a;">(</span><span style="color:#6699cc;">brew</span><span style="color:#f2777a;"> --prefix openssl)</span><span style="color:#d3d0c8;">/include \
</span><span style="color:#f2777a;"> -L</span><span style="color:#d3d0c8;">$</span><span style="color:#f2777a;">(</span><span style="color:#6699cc;">brew</span><span style="color:#f2777a;"> --prefix openssl)</span><span style="color:#d3d0c8;">/lib \
</span><span style="color:#f2777a;"> -lssl -lcrypto </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -framework</span><span style="color:#d3d0c8;"> CoreFoundation</span><span style="color:#f2777a;"> -framework</span><span style="color:#d3d0c8;"> Security \
</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Linux
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -lssl -lcrypto </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows (Developer Command Prompt)
</span><span style="color:#6699cc;">cl</span><span style="color:#d3d0c8;"> /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS (Homebrew)
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -DCPPHTTPLIB_OPENSSL_SUPPORT \
</span><span style="color:#323232;"> -I$(brew --prefix openssl)/include \
</span><span style="color:#323232;"> -L$(brew --prefix openssl)/lib \
</span><span style="color:#323232;"> -lssl -lcrypto \
</span><span style="color:#323232;"> -framework CoreFoundation -framework Security \
</span><span style="color:#323232;"> -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Linux
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT \
</span><span style="color:#323232;"> -lssl -lcrypto \
</span><span style="color:#323232;"> -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows (Developer Command Prompt)
</span><span style="color:#323232;">cl /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
</span></pre>
</div></div>
<p>Let's look at what each option does.</p>
<ul>
<li><strong><code>-DCPPHTTPLIB_OPENSSL_SUPPORT</code></strong> — Defines the macro that enables TLS support</li>
<li><strong><code>-lssl -lcrypto</code></strong> — Links the OpenSSL libraries</li>
<li><strong><code>-I</code> / <code>-L</code></strong> (macOS only) — Points to the Homebrew OpenSSL paths</li>
<li><strong><code>-framework CoreFoundation -framework Security</code></strong> (macOS only) — Needed to automatically load system certificates from the Keychain</li>
</ul>
<h2>Verifying the Setup</h2>
<p>Let's make sure everything works. Here's a simple program that passes an HTTPS URL to <code>httplib::Client</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://www.google.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Status: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://www.google.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Status: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>Compile and run it. If you see <code>Status: 200</code>, your setup is complete.</p>
<h2>Other TLS Backends</h2>
<p>cpp-httplib also supports Mbed TLS and wolfSSL in addition to OpenSSL. You can switch between them just by changing the macro definition and linked libraries.</p>
<table><thead><tr><th style="text-align: left">Backend</th><th style="text-align: left">Macro</th><th style="text-align: left">Libraries to link</th></tr></thead><tbody>
<tr><td style="text-align: left">OpenSSL</td><td style="text-align: left"><code>CPPHTTPLIB_OPENSSL_SUPPORT</code></td><td style="text-align: left"><code>libssl</code>, <code>libcrypto</code></td></tr>
<tr><td style="text-align: left">Mbed TLS</td><td style="text-align: left"><code>CPPHTTPLIB_MBEDTLS_SUPPORT</code></td><td style="text-align: left"><code>libmbedtls</code>, <code>libmbedx509</code>, <code>libmbedcrypto</code></td></tr>
<tr><td style="text-align: left">wolfSSL</td><td style="text-align: left"><code>CPPHTTPLIB_WOLFSSL_SUPPORT</code></td><td style="text-align: left"><code>libwolfssl</code></td></tr>
</tbody></table>
<p>This tour assumes OpenSSL, but the API is the same regardless of which backend you choose.</p>
<h2>Next Step</h2>
<p>You're all set with TLS. Next, let's send a request to an HTTPS site.</p>
<p><strong>Next:</strong> <a href="../06-https-client">HTTPS Client</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+287
View File
@@ -0,0 +1,287 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTPS Client - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="active">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>HTTPS Client</h1>
<p>In the previous chapter, you set up OpenSSL. Now let's put it to use with an HTTPS client. You can use the same <code>httplib::Client</code> from Chapter 2. Just pass a URL with the <code>https://</code> scheme to the constructor.</p>
<h2>GET Request</h2>
<p>Let's try accessing a real HTTPS site.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">substr</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">) &lt;&lt; std::endl; </span><span style="color:#747369;">// First 100 chars of the HTML
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body.substr(</span><span style="color:#0086b3;">0</span><span style="color:#323232;">, </span><span style="color:#0086b3;">100</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// First 100 chars of the HTML
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>In Chapter 2, you wrote <code>httplib::Client cli("http://localhost:8080")</code>. All you need to change is the scheme to <code>https://</code>. Every API you learned in Chapter 2 -- <code>Get()</code>, <code>Post()</code>, and so on -- works exactly the same way.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> https://nghttp2.org/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl https://nghttp2.org/
</span></pre>
</div></div>
<h2>Specifying a Port</h2>
<p>The default port for HTTPS is 443. If you need a different port, include it in the URL.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>CA Certificate Verification</h2>
<p>When connecting over HTTPS, <code>httplib::Client</code> verifies the server certificate by default. It only connects to servers whose certificate was issued by a trusted CA (Certificate Authority).</p>
<p>CA certificates are loaded automatically from the Keychain on macOS, the system CA certificate store on Linux, and the Windows certificate store on Windows. In most cases, no extra configuration is needed.</p>
<h3>Specifying a CA Certificate File</h3>
<p>On some environments, the system CA certificates may not be found. In that case, use <code>set_ca_cert_path()</code> to specify the path directly.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_ca_cert_path</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/etc/ssl/certs/ca-certificates.crt</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_ca_cert_path(</span><span style="color:#183691;">&quot;/etc/ssl/certs/ca-certificates.crt&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> --cacert</span><span style="color:#d3d0c8;"> /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl --cacert /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
</span></pre>
</div></div>
<h3>Disabling Certificate Verification</h3>
<p>During development, you might want to connect to a server with a self-signed certificate. You can disable verification for that.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">enable_server_certificate_verification</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.enable_server_certificate_verification(</span><span style="color:#0086b3;">false</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -k</span><span style="color:#d3d0c8;"> https://localhost:8443/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -k https://localhost:8443/
</span></pre>
</div></div>
<p>Never disable this in production. It opens you up to man-in-the-middle attacks.</p>
<h2>Following Redirects</h2>
<p>When accessing HTTPS sites, you'll often encounter redirects. For example, <code>http://</code> to <code>https://</code>, or a bare domain to <code>www</code>.</p>
<p>By default, redirects are not followed. You can check the redirect target in the <code>Location</code> header.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/httpbin/redirect/3</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 302
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Location</span><span style="color:#d3d0c8;">&quot;) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/httpbin/redirect/3&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 302
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;get_header_value(</span><span style="color:#183691;">&quot;Location&quot;</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div></div>
<p>Call <code>set_follow_location(true)</code> to automatically follow redirects and get the final response.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_follow_location</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/httpbin/redirect/3</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200 (the final response)
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_follow_location(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/httpbin/redirect/3&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200 (the final response)
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -L</span><span style="color:#d3d0c8;"> https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -L https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div></div>
<h2>Next Steps</h2>
<p>Now you know how to use the HTTPS client. Next, let's set up your own HTTPS server. We'll start with creating a self-signed certificate.</p>
<p><strong>Next:</strong> <a href="../07-https-server">HTTPS Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+276
View File
@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTPS Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="active">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>HTTPS Server</h1>
<p>In the previous chapter, you used an HTTPS client. Now let's set up your own HTTPS server. Just swap <code>httplib::Server</code> from Chapter 3 with <code>httplib::SSLServer</code>.</p>
<p>A TLS server needs a server certificate and a private key, though. Let's get those ready first.</p>
<h2>Creating a Self-Signed Certificate</h2>
<p>For development and testing, a self-signed certificate works just fine. You can generate one quickly with an OpenSSL command.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">openssl</span><span style="color:#d3d0c8;"> req</span><span style="color:#f2777a;"> -x509 -noenc -keyout</span><span style="color:#d3d0c8;"> key.pem</span><span style="color:#f2777a;"> -out</span><span style="color:#d3d0c8;"> cert.pem</span><span style="color:#f2777a;"> -subj</span><span style="color:#d3d0c8;"> /CN=localhost
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">openssl req -x509 -noenc -keyout key.pem -out cert.pem -subj /CN=localhost
</span></pre>
</div></div>
<p>This creates two files:</p>
<ul>
<li><strong><code>cert.pem</code></strong> — Server certificate</li>
<li><strong><code>key.pem</code></strong> — Private key</li>
</ul>
<h2>A Minimal HTTPS Server</h2>
<p>Once you have your certificate, let's write the server.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::SSLServer </span><span style="color:#6699cc;">svr</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">key.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, HTTPS!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on https://localhost:8443</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8443</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::SSLServer svr(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;key.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, HTTPS!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on https://localhost:8443&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8443</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>Just pass the certificate and private key paths to the <code>httplib::SSLServer</code> constructor. The routing API is exactly the same as <code>httplib::Server</code> from Chapter 3.</p>
<p>Compile and start it up.</p>
<h2>Testing It Out</h2>
<p>With the server running, try accessing it with <code>curl</code>. Since we're using a self-signed certificate, add the <code>-k</code> option to skip certificate verification.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -k</span><span style="color:#d3d0c8;"> https://localhost:8443/
</span><span style="color:#747369;"># Hello, HTTPS!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -k https://localhost:8443/
</span><span style="font-style:italic;color:#969896;"># Hello, HTTPS!
</span></pre>
</div></div>
<p>If you open <code>https://localhost:8443</code> in a browser, you'll see a "This connection is not secure" warning. That's expected with a self-signed certificate. Just proceed past it.</p>
<h2>Connecting from a Client</h2>
<p>Let's connect using <code>httplib::Client</code> from the previous chapter. There are two ways to connect to a server with a self-signed certificate.</p>
<h3>Option 1: Disable Certificate Verification</h3>
<p>This is the quick and easy approach for development.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> cli.</span><span style="color:#6699cc;">enable_server_certificate_verification</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, HTTPS!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> cli.enable_server_certificate_verification(</span><span style="color:#0086b3;">false</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, HTTPS!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<h3>Option 2: Specify the Self-Signed Certificate as a CA Certificate</h3>
<p>This is the safer approach. You tell the client to trust <code>cert.pem</code> as a CA certificate.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> cli.</span><span style="color:#6699cc;">set_ca_cert_path</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, HTTPS!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> cli.set_ca_cert_path(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, HTTPS!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>This way, only connections to the server with that specific certificate are allowed, preventing impersonation. Use this approach whenever possible, even in test environments.</p>
<h2>Comparing Server and SSLServer</h2>
<p>The <code>httplib::Server</code> API you learned in Chapter 3 works exactly the same with <code>httplib::SSLServer</code>. The only difference is the constructor.</p>
<table><thead><tr><th></th><th><code>httplib::Server</code></th><th><code>httplib::SSLServer</code></th></tr></thead><tbody>
<tr><td>Constructor</td><td>No arguments</td><td>Certificate and private key paths</td></tr>
<tr><td>Protocol</td><td>HTTP</td><td>HTTPS</td></tr>
<tr><td>Port (convention)</td><td>8080</td><td>8443</td></tr>
<tr><td>Routing</td><td>Same</td><td>Same</td></tr>
</tbody></table>
<p>To switch an HTTP server to HTTPS, just change the constructor.</p>
<h2>Next Steps</h2>
<p>Your HTTPS server is up and running. You now have the basics of both HTTP/HTTPS clients and servers covered.</p>
<p>Next, let's look at the WebSocket support that was recently added to cpp-httplib.</p>
<p><strong>Next:</strong> <a href="../08-websocket">WebSocket</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+328
View File
@@ -0,0 +1,328 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WebSocket - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="active">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>WebSocket</h1>
<p>cpp-httplib supports WebSocket as well. Unlike HTTP request/response, WebSocket lets the server and client exchange messages in both directions. It's great for chat apps and real-time notifications.</p>
<p>Let's build an echo server and client right away.</p>
<h2>Echo Server</h2>
<p>Here's an echo server that sends back whatever message it receives.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg); </span><span style="color:#747369;">// Send back the received message as-is
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg); </span><span style="font-style:italic;color:#969896;">// Send back the received message as-is
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>You register a WebSocket handler with <code>svr.WebSocket()</code>. It works just like <code>svr.Get()</code> and <code>svr.Post()</code> from Chapter 3.</p>
<p>Inside the handler, <code>ws.read(msg)</code> waits for a message. When the connection closes, <code>read()</code> returns <code>false</code>, so the loop exits. <code>ws.send(msg)</code> sends a message back.</p>
<h2>Connecting from a Client</h2>
<p>Let's connect to the server using <code>httplib::ws::WebSocketClient</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::ws::WebSocketClient </span><span style="color:#6699cc;">client</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">ws://localhost:8080/ws</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(!client.</span><span style="color:#6699cc;">connect</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Connection failed</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Send a message
</span><span style="color:#d3d0c8;"> client.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, WebSocket!</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Receive a response from the server
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(client.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; msg &lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, WebSocket!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> client.</span><span style="color:#6699cc;">close</span><span style="color:#d3d0c8;">();
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::ws::WebSocketClient client(</span><span style="color:#183691;">&quot;ws://localhost:8080/ws&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">!</span><span style="color:#323232;">client.connect()) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Connection failed&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Send a message
</span><span style="color:#323232;"> client.send(</span><span style="color:#183691;">&quot;Hello, WebSocket!&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Receive a response from the server
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(client.read(msg)) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, WebSocket!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> client.close();
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>Pass a URL in <code>ws://host:port/path</code> format to the constructor. Call <code>connect()</code> to start the connection, then use <code>send()</code> and <code>read()</code> to exchange messages.</p>
<h2>Text and Binary</h2>
<p>WebSocket has two types of messages: text and binary. You can tell them apart by the return value of <code>read()</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> httplib::ws::ReadResult ret;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">((ret = ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg))) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(ret == httplib::ws::Binary) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg.</span><span style="color:#6699cc;">data</span><span style="color:#d3d0c8;">(), msg.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()); </span><span style="color:#747369;">// Send as binary
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg); </span><span style="color:#747369;">// Send as text
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> httplib::ws::ReadResult ret;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">((ret </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> ws.read(msg))) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(ret </span><span style="font-weight:bold;color:#a71d5d;">==</span><span style="color:#323232;"> httplib::ws::Binary) {
</span><span style="color:#323232;"> ws.send(msg.data(), msg.size()); </span><span style="font-style:italic;color:#969896;">// Send as binary
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> ws.send(msg); </span><span style="font-style:italic;color:#969896;">// Send as text
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<ul>
<li><code>ws.send(const std::string &amp;)</code> — sends as a text message</li>
<li><code>ws.send(const char *, size_t)</code> — sends as a binary message</li>
</ul>
<p>The client-side API is the same.</p>
<h2>Accessing Request Information</h2>
<p>You can read HTTP request information from the handshake through the first argument <code>req</code> in the handler. This is handy for checking authentication tokens.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;req, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> token = req.</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(token.</span><span style="color:#6699cc;">empty</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">close</span><span style="color:#d3d0c8;">(httplib::ws::CloseStatus::PolicyViolation, &quot;</span><span style="color:#99cc99;">unauthorized</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg);
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">req, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> token </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_header_value(</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(token.empty()) {
</span><span style="color:#323232;"> ws.close(httplib::ws::CloseStatus::PolicyViolation, </span><span style="color:#183691;">&quot;unauthorized&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg);
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Using WSS</h2>
<p>WebSocket over HTTPS (WSS) is also supported. On the server side, just register a WebSocket handler on <code>httplib::SSLServer</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::SSLServer </span><span style="color:#6699cc;">svr</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">key.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg);
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8443</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::SSLServer </span><span style="font-weight:bold;color:#795da3;">svr</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;key.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg);
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8443</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>On the client side, use the <code>wss://</code> scheme.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::ws::WebSocketClient </span><span style="color:#6699cc;">client</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">wss://localhost:8443/ws</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::ws::WebSocketClient </span><span style="font-weight:bold;color:#795da3;">client</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;wss://localhost:8443/ws&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>Next Steps</h2>
<p>Now you know the basics of WebSocket. This wraps up the Tour.</p>
<p>The next page gives you a summary of features we didn't cover in the Tour.</p>
<p><strong>Next:</strong> <a href="../09-whats-next">What's Next</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+460
View File
@@ -0,0 +1,460 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>What&#x27;s Next - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="active">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>What&#x27;s Next</h1>
<p>Great job finishing the Tour! You now have a solid grasp of the cpp-httplib basics. But there's a lot more to explore. Here's a quick overview of features we didn't cover in the Tour, organized by category.</p>
<h2>Streaming API</h2>
<p>When you're working with LLM streaming responses or downloading large files, you don't want to load the entire response into memory. Use <code>stream::Get()</code> to process data chunk by chunk.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:11434</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> result = </span><span style="color:#6699cc;">httplib::stream::Get</span><span style="color:#d3d0c8;">(cli, &quot;</span><span style="color:#99cc99;">/api/generate</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(result) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(result.</span><span style="color:#6699cc;">next</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> std::cout.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(result.</span><span style="color:#6699cc;">data</span><span style="color:#d3d0c8;">(), result.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">());
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:11434&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">httplib::stream::Get(cli, </span><span style="color:#183691;">&quot;/api/generate&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(result) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(result.next()) {
</span><span style="color:#323232;"> std::cout.write(result.data(), result.size());
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>You can also pass a <code>content_receiver</code> callback to <code>Get()</code>. This approach works with Keep-Alive.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/stream</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const char </span><span style="color:#d3d0c8;">*data, size_t len) {
</span><span style="color:#d3d0c8;"> std::cout.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(data, len);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">cli.Get(</span><span style="color:#183691;">&quot;/stream&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const char *</span><span style="color:#323232;">data, </span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> len) {
</span><span style="color:#323232;"> std::cout.write(data, len);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>On the server side, you have <code>set_content_provider()</code> and <code>set_chunked_content_provider()</code>. Use the former when you know the size, and the latter when you don't.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// With known size (sets Content-Length)
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/file</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> size = </span><span style="color:#6699cc;">get_file_size</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">large.bin</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content_provider</span><span style="color:#d3d0c8;">(size, &quot;</span><span style="color:#99cc99;">application/octet-stream</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;"> [](size_t offset, size_t length, httplib::DataSink &amp;sink) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Send &#39;length&#39; bytes starting from &#39;offset&#39;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// Unknown size (Chunked Transfer Encoding)
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/stream</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_chunked_content_provider</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;"> [](size_t offset, httplib::DataSink &amp;sink) {
</span><span style="color:#d3d0c8;"> sink.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">chunk</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">6</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">; </span><span style="color:#747369;">// Return false to finish
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// With known size (sets Content-Length)
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/file&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> size </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">get_file_size(</span><span style="color:#183691;">&quot;large.bin&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content_provider(size, </span><span style="color:#183691;">&quot;application/octet-stream&quot;</span><span style="color:#323232;">,
</span><span style="color:#323232;"> [](</span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> offset, </span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> length, httplib::DataSink </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">sink) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Send &#39;length&#39; bytes starting from &#39;offset&#39;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// Unknown size (Chunked Transfer Encoding)
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/stream&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_chunked_content_provider(</span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">,
</span><span style="color:#323232;"> [](</span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> offset, httplib::DataSink </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">sink) {
</span><span style="color:#323232;"> sink.write(</span><span style="color:#183691;">&quot;chunk</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">6</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">; </span><span style="font-style:italic;color:#969896;">// Return false to finish
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>For uploading large files, <code>make_file_provider()</code> comes in handy. It streams the file instead of loading it all into memory.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, {}, {
</span><span style="color:#d3d0c8;"> </span><span style="color:#6699cc;">httplib::make_file_provider</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">/path/to/large-file.zip</span><span style="color:#d3d0c8;">&quot;)
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, {}, {
</span><span style="color:#323232;"> httplib::make_file_provider(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;/path/to/large-file.zip&quot;</span><span style="color:#323232;">)
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Server-Sent Events (SSE)</h2>
<p>We provide an SSE client as well. It supports automatic reconnection and resuming via <code>Last-Event-ID</code>.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">httplib::sse::SSEClient </span><span style="color:#6699cc;">sse</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">cli</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">/events</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">on_message</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::sse::SSEMessage &amp;msg) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; msg.</span><span style="color:#f2777a;">event </span><span style="color:#d3d0c8;">&lt;&lt; &quot;</span><span style="color:#99cc99;">: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; msg.</span><span style="color:#f2777a;">data </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">start</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// Blocking, with auto-reconnection
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">httplib::sse::SSEClient </span><span style="font-weight:bold;color:#795da3;">sse</span><span style="color:#323232;">(cli, </span><span style="color:#183691;">&quot;/events&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">sse.on_message([](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::sse::SSEMessage </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">msg) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg.event </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg.data </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">sse.start(); </span><span style="font-style:italic;color:#969896;">// Blocking, with auto-reconnection
</span></pre>
</div></div>
<p>You can also set separate handlers for each event type.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">on_event</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">update</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::sse::SSEMessage &amp;msg) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Only handles &quot;update&quot; events
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">sse.on_event(</span><span style="color:#183691;">&quot;update&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::sse::SSEMessage </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">msg) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Only handles &quot;update&quot; events
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Authentication</h2>
<p>The client has helpers for Basic auth, Bearer Token auth, and Digest auth.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://api.example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_basic_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_bearer_token_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">my-token</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://api.example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_basic_auth(</span><span style="color:#183691;">&quot;user&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;password&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_bearer_token_auth(</span><span style="color:#183691;">&quot;my-token&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>Compression</h2>
<p>We support compression and decompression with gzip, Brotli, and Zstandard. Define the corresponding macro when you compile.</p>
<table><thead><tr><th>Method</th><th>Macro</th></tr></thead><tbody>
<tr><td>gzip</td><td><code>CPPHTTPLIB_ZLIB_SUPPORT</code></td></tr>
<tr><td>Brotli</td><td><code>CPPHTTPLIB_BROTLI_SUPPORT</code></td></tr>
<tr><td>Zstandard</td><td><code>CPPHTTPLIB_ZSTD_SUPPORT</code></td></tr>
</tbody></table>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_compress</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// Compress request body
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_decompress</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// Decompress response body
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_compress(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// Compress request body
</span><span style="color:#323232;">cli.set_decompress(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// Decompress response body
</span></pre>
</div></div>
<h2>Proxy</h2>
<p>You can connect through an HTTP proxy.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_proxy</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">proxy.example.com</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_proxy_basic_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_proxy(</span><span style="color:#183691;">&quot;proxy.example.com&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_proxy_basic_auth(</span><span style="color:#183691;">&quot;user&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;password&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>Timeouts</h2>
<p>You can set connection, read, and write timeouts individually.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_connection_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">5</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 5 seconds
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_read_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">10</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 10 seconds
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_write_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">10</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 10 seconds
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_connection_timeout(</span><span style="color:#0086b3;">5</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 5 seconds
</span><span style="color:#323232;">cli.set_read_timeout(</span><span style="color:#0086b3;">10</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 10 seconds
</span><span style="color:#323232;">cli.set_write_timeout(</span><span style="color:#0086b3;">10</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 10 seconds
</span></pre>
</div></div>
<h2>Keep-Alive</h2>
<p>If you're making multiple requests to the same server, enable Keep-Alive. It reuses the TCP connection, which is much more efficient.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_keep_alive</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_keep_alive(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>Server Middleware</h2>
<p>You can hook into request processing before and after handlers run.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_pre_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Runs before every request
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;"> httplib::Server::HandlerResponse::Unhandled; </span><span style="color:#747369;">// Continue to normal routing
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_post_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Runs after the response is sent
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_header</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">X-Server</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">cpp-httplib</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_pre_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Runs before every request
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;"> httplib::Server::HandlerResponse::Unhandled; </span><span style="font-style:italic;color:#969896;">// Continue to normal routing
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.set_post_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Runs after the response is sent
</span><span style="color:#323232;"> res.set_header(</span><span style="color:#183691;">&quot;X-Server&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;cpp-httplib&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>Use <code>req.user_data</code> to pass data from middleware to handlers. This is useful for sharing things like decoded auth tokens.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_pre_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> req.</span><span style="color:#f2777a;">user_data</span><span style="color:#d3d0c8;">[&quot;</span><span style="color:#99cc99;">auth_user</span><span style="color:#d3d0c8;">&quot;] = </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">alice</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;"> httplib::Server::HandlerResponse::Unhandled;
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/me</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> user = std::</span><span style="color:#6699cc;">any_cast</span><span style="color:#d3d0c8;">&lt;std::string&gt;(req.</span><span style="color:#f2777a;">user_data</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">auth_user</span><span style="color:#d3d0c8;">&quot;));
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, </span><span style="color:#d3d0c8;">&quot; + user, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_pre_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> req.user_data[</span><span style="color:#183691;">&quot;auth_user&quot;</span><span style="color:#323232;">] </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">std::string(</span><span style="color:#183691;">&quot;alice&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;"> httplib::Server::HandlerResponse::Unhandled;
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/me&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> user </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">std::any_cast&lt;std::string&gt;(req.user_data.at(</span><span style="color:#183691;">&quot;auth_user&quot;</span><span style="color:#323232;">));
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> user, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>You can also customize error and exception handlers.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_error_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Custom Error Page</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/html</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_exception_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res, std::exception_ptr ep) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">= </span><span style="color:#f99157;">500</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Internal Server Error</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_error_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Custom Error Page&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/html&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.set_exception_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res, std::exception_ptr ep) {
</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">500</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Internal Server Error&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Logging</h2>
<p>You can set a logger on both the server and the client.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_logger</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; req.</span><span style="color:#f2777a;">method </span><span style="color:#d3d0c8;">&lt;&lt; &quot; &quot; &lt;&lt; req.</span><span style="color:#f2777a;">path </span><span style="color:#d3d0c8;">&lt;&lt; &quot; &quot; &lt;&lt; res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_logger([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> req.method </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot; &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> req.path </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot; &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Unix Domain Socket</h2>
<p>In addition to TCP, we support Unix Domain Sockets. You can use them for inter-process communication on the same machine.</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// Server
</span><span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_address_family</span><span style="color:#d3d0c8;">(AF_UNIX);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/tmp/httplib.sock</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// Server
</span><span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">svr.set_address_family(AF_UNIX);
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;/tmp/httplib.sock&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// Client
</span><span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_address_family</span><span style="color:#d3d0c8;">(AF_UNIX);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_hostname_addr_map</span><span style="color:#d3d0c8;">({{&quot;</span><span style="color:#99cc99;">localhost</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">/tmp/httplib.sock</span><span style="color:#d3d0c8;">&quot;}});
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// Client
</span><span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_address_family(AF_UNIX);
</span><span style="color:#323232;">cli.set_hostname_addr_map({{</span><span style="color:#183691;">&quot;localhost&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;/tmp/httplib.sock&quot;</span><span style="color:#323232;">}});
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>Learn More</h2>
<p>Want to dig deeper? Check out these resources.</p>
<ul>
<li>Cookbook — A collection of recipes for common use cases</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README.md">README</a> — Full API reference</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-sse.md">README-sse</a> — How to use Server-Sent Events</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-stream.md">README-stream</a> — How to use the Streaming API</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-websocket.md">README-websocket</a> — How to use the WebSocket server</li>
</ul>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Tour of cpp-httplib - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/en/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/en/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/en/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
EN
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;en&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>A Tour of cpp-httplib</h1>
<p>This is a step-by-step tutorial that walks you through the basics of cpp-httplib. Each chapter builds on the previous one, so please read them in order starting from Chapter 1.</p>
<ol>
<li><a href="01-getting-started">Getting Started</a> — Get httplib.h and build a Hello World server</li>
<li><a href="02-basic-client">Basic Client</a> — Send GET/POST requests and use path parameters</li>
<li><a href="03-basic-server">Basic Server</a> — Routing, path parameters, and building responses</li>
<li><a href="04-static-file-server">Static File Server</a> — Serve static files</li>
<li><a href="05-tls-setup">TLS Setup</a> — Set up OpenSSL / mbedTLS</li>
<li><a href="06-https-client">HTTPS Client</a> — Make requests to HTTPS sites</li>
<li><a href="07-https-server">HTTPS Server</a> — Build an HTTPS server</li>
<li><a href="08-websocket">WebSocket</a> — Learn the basics of WebSocket communication</li>
<li><a href="09-whats-next">What's Next</a> — Explore more features</li>
</ol>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
(function() {
var lang = localStorage.getItem('preferred-lang') || 'en';
window.location.replace('/cpp-httplib/' + lang + '/');
})();
</script>
<meta http-equiv="refresh" content="0;url=/cpp-httplib/en/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/cpp-httplib/en/">/cpp-httplib/en/</a>...</p>
</body>
</html>
+109
View File
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cookbook - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;cookbook&#x2F;" class="nav-section-title active">Cookbook</a>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Cookbook</h1>
<p>This section is under construction.</p>
<p>Check back soon for a collection of recipes organized by topic.</p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>cpp-httplib - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
</div>
</header>
<div class="layout no-sidebar">
<main class="content portal">
<article>
<h1>cpp-httplib</h1>
<p><a href="https://github.com/yhirose/cpp-httplib">cpp-httplib</a>は、C++用のHTTP/HTTPSライブラリです。<a href="https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h"><code>httplib.h</code></a> というヘッダーファイルを1枚コピーするだけで使えます。</p>
<p>C++でちょっとしたHTTPサーバーやクライアントが必要になったとき、すぐに動くものが欲しいですよね。cpp-httplibはまさにそのために作られました。サーバーもクライアントも、数行のコードで書き始められます。</p>
<p>APIはラムダ式をベースにした直感的な設計で、C++11以降のコンパイラーがあればどこでも動きます。Windows、macOS、Linux — お使いの環境をそのまま使えます。</p>
<p>HTTPSも使えます。OpenSSLやmbedTLSをリンクするだけで、サーバー・クライアントの両方がTLSに対応します。Content-Encodinggzip, brotli等)、ファイルアップロードなど、実際の開発で必要になる機能もひと通り揃っています。WebSocketもサポートしています。</p>
<p>内部的にはブロッキングI/Oとスレッドプールを使っています。大量の同時接続を捌くような用途には向きませんが、APIサーバーやツールの組み込みHTTP、テスト用のモックサーバーなど、多くのユースケースで十分な性能を発揮します。</p>
<p>「今日の課題を、今日中に解決する」— cpp-httplibが目指しているのは、そういうシンプルさです。</p>
<h2>ドキュメント</h2>
<ul>
<li><a href="tour/">A Tour of cpp-httplib</a> — 基本を順を追って学べるチュートリアル。初めての方はここから</li>
<li><a href="cookbook/">Cookbook</a> — 目的別のレシピ集。必要なトピックから読めます</li>
</ul>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+234
View File
@@ -0,0 +1,234 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Getting Started - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="active">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Getting Started</h1>
<p>cpp-httplibを始めるのに必要なのは、<code>httplib.h</code>とC++コンパイラーだけです。ファイルをダウンロードして、Hello Worldサーバーを動かすところまでやってみましょう。</p>
<h2>httplib.h の入手</h2>
<p>GitHubから直接ダウンロードできます。常に最新版を使ってください。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -LO</span><span style="color:#d3d0c8;"> https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -LO https://github.com/yhirose/cpp-httplib/raw/refs/tags/latest/httplib.h
</span></pre>
</div></div>
<p>ダウンロードした <code>httplib.h</code> をプロジェクトのディレクトリに置けば、準備完了です。</p>
<h2>コンパイラーの準備</h2>
<table><thead><tr><th>OS</th><th>開発環境</th><th>セットアップ</th></tr></thead><tbody>
<tr><td>macOS</td><td>Apple Clang</td><td>Xcode Command Line Tools (<code>xcode-select --install</code>)</td></tr>
<tr><td>Ubuntu</td><td>clang++ または g++</td><td><code>apt install clang</code> または <code>apt install g++</code></td></tr>
<tr><td>Windows</td><td>MSVC</td><td>Visual Studio 2022 以降(C++ コンポーネントを含めてインストール)</td></tr>
</tbody></table>
<h2>Hello World サーバー</h2>
<p>次のコードを <code>server.cpp</code> として保存しましょう。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request&amp;, httplib::Response&amp; res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, World!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::Response</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;"> res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, World!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>たった数行で、HTTPリクエストに応答するサーバーが書けます。</p>
<h2>コンパイルと実行</h2>
<p>このチュートリアルのサンプルコードは、コードを簡潔に書けるC++17で書いています。cpp-httplib自体はC++11でもコンパイルできます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Linux
</span><span style="color:#747369;"># `-pthread`: cpp-httplibは内部でスレッドを使用
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -pthread -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows (Developer Command Prompt)
</span><span style="color:#747369;"># `/EHsc`: C++例外処理を有効化
</span><span style="color:#6699cc;">cl</span><span style="color:#d3d0c8;"> /EHsc /std:c++17 server.cpp
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Linux
</span><span style="font-style:italic;color:#969896;"># `-pthread`: cpp-httplibは内部でスレッドを使用
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -pthread -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows (Developer Command Prompt)
</span><span style="font-style:italic;color:#969896;"># `/EHsc`: C++例外処理を有効化
</span><span style="color:#323232;">cl /EHsc /std:c++17 server.cpp
</span></pre>
</div></div>
<p>コンパイルできたら実行します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS / Linux
</span><span style="color:#6699cc;">./server
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows
</span><span style="color:#6699cc;">server.exe
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS / Linux
</span><span style="color:#323232;">./server
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows
</span><span style="color:#323232;">server.exe
</span></pre>
</div></div>
<p>ブラウザで <code>http://localhost:8080</code> を開いてください。"Hello, World!" と表示されれば成功です。</p>
<p><code>curl</code> でも確認できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/
</span><span style="color:#747369;"># Hello, World!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/
</span><span style="font-style:italic;color:#969896;"># Hello, World!
</span></pre>
</div></div>
<p>サーバーを停止するには、ターミナルで <code>Ctrl+C</code> を押します。</p>
<h2>次のステップ</h2>
<p>サーバーの基本がわかりましたね。次は、クライアント側を見てみましょう。cpp-httplibはHTTPクライアント機能も備えています。</p>
<p><strong>次:</strong> <a href="../02-basic-client">Basic Client</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+527
View File
@@ -0,0 +1,527 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basic Client - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="active">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Basic Client</h1>
<p>cpp-httplibはサーバーだけでなく、HTTPクライアント機能も備えています。<code>httplib::Client</code> を使って、GETやPOSTリクエストを送ってみましょう。</p>
<h2>テスト用サーバーの準備</h2>
<p>クライアントの動作を確認するために、リクエストを受け付けるサーバーを用意します。次のコードを保存し、前章と同じ手順でコンパイル・実行してください。サーバーの詳しい解説は次章で行います。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">];
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">];
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<h2>GETリクエスト</h2>
<p>サーバーが起動したら、別のターミナルを開いて試してみましょう。まず、最もシンプルなGETリクエストです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>httplib::Client</code> のコンストラクターにサーバーのアドレスを渡し、<code>Get()</code> でリクエストを送ります。戻り値の <code>res</code> からステータスコードやボディを取得できます。</p>
<p>対応する <code>curl</code> コマンドはこうなります。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/hi
</span><span style="color:#747369;"># Hello!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/hi
</span><span style="font-style:italic;color:#969896;"># Hello!
</span></pre>
</div></div>
<h2>レスポンスの確認</h2>
<p>レスポンスには、ステータスコードとボディ以外にもヘッダー情報が含まれています。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// ステータスコード
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// ボディ
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello!
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// ヘッダー
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Content-Type</span><span style="color:#d3d0c8;">&quot;) &lt;&lt; std::endl; </span><span style="color:#747369;">// text/plain
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// ステータスコード
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// ボディ
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello!
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// ヘッダー
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;get_header_value(</span><span style="color:#183691;">&quot;Content-Type&quot;</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// text/plain
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>res-&gt;body</code><code>std::string</code> なので、JSON レスポンスをパースしたい場合は <a href="https://github.com/nlohmann/json">nlohmann/json</a> などの JSON ライブラリにそのまま渡せます。</p>
<h2>クエリパラメーター</h2>
<p>GETリクエストにクエリパラメーターを付けるには、URLに直接書くか、<code>httplib::Params</code> を使います。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, httplib::Params{{&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">cpp-httplib</span><span style="color:#d3d0c8;">&quot;}});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Query: cpp-httplib
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, httplib::Params{{</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;cpp-httplib&quot;</span><span style="color:#323232;">}});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Query: cpp-httplib
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>httplib::Params</code> を使うと、特殊文字のURLエンコードを自動で行ってくれます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">http://localhost:8080/search?q=cpp-httplib</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#747369;"># Query: cpp-httplib
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl </span><span style="color:#183691;">&quot;http://localhost:8080/search?q=cpp-httplib&quot;
</span><span style="font-style:italic;color:#969896;"># Query: cpp-httplib
</span></pre>
</div></div>
<h2>パスパラメーター</h2>
<p>URLのパスに値を直接埋め込む場合も、クライアント側は特別なAPIは不要です。パスをそのまま <code>Get()</code> に渡すだけです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/42</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// User ID: 42
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/users/42&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// User ID: 42
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/users/42
</span><span style="color:#747369;"># User ID: 42
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/users/42
</span><span style="font-style:italic;color:#969896;"># User ID: 42
</span></pre>
</div></div>
<p>テスト用サーバーには、正規表現でIDを数字のみに絞った <code>/files/(\d+)</code> もあります。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/files/42</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// File ID: 42
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/files/42&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// File ID: 42
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080/files/42
</span><span style="color:#747369;"># File ID: 42
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080/files/42
</span><span style="font-style:italic;color:#969896;"># File ID: 42
</span></pre>
</div></div>
<p><code>/files/abc</code> のように数字以外を渡すと404が返ります。仕組みは次章で解説します。</p>
<h2>リクエストヘッダー</h2>
<p>カスタムHTTPヘッダーを付けるには、<code>httplib::Headers</code> を渡します。<code>Get()</code><code>Post()</code> のどちらでも使えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, httplib::Headers{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Bearer my-token</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, httplib::Headers{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Bearer my-token&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -H </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Authorization: Bearer my-token</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/hi
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -H </span><span style="color:#183691;">&quot;Authorization: Bearer my-token&quot;</span><span style="color:#323232;"> http://localhost:8080/hi
</span></pre>
</div></div>
<h2>POSTリクエスト</h2>
<p>テキストデータをPOSTしてみましょう。<code>Post()</code> の第2引数にボディ、第3引数にContent-Typeを指定します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Hello, Server!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, Server!
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Hello, Server!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, Server!
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>テスト用サーバーの <code>/post</code> はボディをそのまま返すので、送った文字列がそのまま返ってきます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -X</span><span style="color:#d3d0c8;"> POST</span><span style="color:#f2777a;"> -H </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Content-Type: text/plain</span><span style="color:#d3d0c8;">&quot;</span><span style="color:#f2777a;"> -d </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">Hello, Server!</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/post
</span><span style="color:#747369;"># Hello, Server!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -X POST -H </span><span style="color:#183691;">&quot;Content-Type: text/plain&quot;</span><span style="color:#323232;"> -d </span><span style="color:#183691;">&quot;Hello, Server!&quot;</span><span style="color:#323232;"> http://localhost:8080/post
</span><span style="font-style:italic;color:#969896;"># Hello, Server!
</span></pre>
</div></div>
<h2>フォームデータの送信</h2>
<p>HTMLフォームのように、キーと値のペアを送ることもできます。<code>httplib::Params</code> を使います。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, httplib::Params{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">name</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Alice</span><span style="color:#d3d0c8;">&quot;},
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">age</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">30</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// age = 30
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// name = Alice
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, httplib::Params{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;name&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Alice&quot;</span><span style="color:#323232;">},
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;age&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;30&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// age = 30
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// name = Alice
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>これは <code>application/x-www-form-urlencoded</code> 形式で送信されます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -X</span><span style="color:#d3d0c8;"> POST</span><span style="color:#f2777a;"> -d </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">name=Alice&amp;age=30</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/submit
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -X POST -d </span><span style="color:#183691;">&quot;name=Alice&amp;age=30&quot;</span><span style="color:#323232;"> http://localhost:8080/submit
</span></pre>
</div></div>
<h2>ファイルのPOST</h2>
<p>ファイルをアップロードするには、<code>httplib::UploadFormDataItems</code> を使ってマルチパートフォームデータとして送信します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, httplib::UploadFormDataItems{
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">Hello, File!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">hello.txt</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// hello.txt (12 bytes)
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, httplib::UploadFormDataItems{
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;Hello, File!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;hello.txt&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// hello.txt (12 bytes)
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>UploadFormDataItems</code> の各要素は <code>{name, content, filename, content_type}</code> の4つのフィールドで構成されます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -F </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">file=Hello, File!;filename=hello.txt;type=text/plain</span><span style="color:#d3d0c8;">&quot; http://localhost:8080/upload
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -F </span><span style="color:#183691;">&quot;file=Hello, File!;filename=hello.txt;type=text/plain&quot;</span><span style="color:#323232;"> http://localhost:8080/upload
</span></pre>
</div></div>
<h2>エラーハンドリング</h2>
<p>ネットワーク通信では、サーバーに接続できない場合があります。<code>res</code> が有効かどうかを必ず確認しましょう。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:9999</span><span style="color:#d3d0c8;">&quot;); </span><span style="color:#747369;">// 存在しないポート
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(!res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// 接続エラー
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// Error: Connection
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// ここに到達すればレスポンスを受信できている
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">!= </span><span style="color:#f99157;">200</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">HTTP Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:9999&quot;</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 存在しないポート
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">!</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// 接続エラー
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// Error: Connection
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// ここに到達すればレスポンスを受信できている
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">!= </span><span style="color:#0086b3;">200</span><span style="color:#323232;">) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;HTTP Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span></pre>
</div></div>
<p>エラーには2つのレベルがあります。</p>
<ul>
<li><strong>接続エラー</strong>: サーバーに到達できなかった場合。<code>res</code> が偽になり、<code>res.error()</code> でエラーの種類を取得できます</li>
<li><strong>HTTPエラー</strong>: サーバーからエラーステータス(404、500など)が返ってきた場合。<code>res</code> は真ですが、<code>res-&gt;status</code> を確認する必要があります</li>
</ul>
<h2>次のステップ</h2>
<p>クライアントからリクエストを送る方法がわかりました。次は、サーバー側をもっと詳しく見てみましょう。ルーティングやパスパラメータなど、サーバーの機能を掘り下げます。</p>
<p><strong>次:</strong> <a href="../03-basic-server">Basic Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+482
View File
@@ -0,0 +1,482 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basic Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="active">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Basic Server</h1>
<p>前章ではクライアントからリクエストを送りました。そのとき、テスト用サーバーを用意しましたね。この章では、あのサーバーの仕組みをひとつずつ紐解いていきます。</p>
<h2>サーバーの起動</h2>
<p>ルーティングを登録したら、最後に <code>svr.listen()</code> を呼んでサーバーを起動します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>第1引数はホスト、第2引数はポート番号です。<code>"0.0.0.0"</code> を指定すると、すべてのネットワークインターフェースでリクエストを受け付けます。自分のマシンからのアクセスだけに限定したいときは <code>"127.0.0.1"</code> を使います。</p>
<p><code>listen()</code> はブロッキング呼び出しです。サーバーが停止するまで、この行から先には進みません。ターミナルで <code>Ctrl+C</code> を押すか、別スレッドから <code>svr.stop()</code> を呼ぶまでサーバーは動き続けます。</p>
<h2>ルーティング</h2>
<p>サーバーの核になるのは「ルーティング」です。どのURLに、どのHTTPメソッドでアクセスされたら、何をするか。それを登録する仕組みです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;req, httplib::Response &amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">req, httplib::Response </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>svr.Get()</code> は、GETリクエストに対するハンドラーを登録します。第1引数がパス、第2引数がハンドラー関数です。<code>/hi</code> にGETリクエストが来たら、このラムダが呼ばれます。</p>
<p>HTTPメソッドごとにメソッドが用意されています。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// GET
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// POST
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Put</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// PUT
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Delete</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/path</span><span style="color:#d3d0c8;">&quot;, handler); </span><span style="color:#747369;">// DELETE
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// GET
</span><span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// POST
</span><span style="color:#323232;">svr.Put(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// PUT
</span><span style="color:#323232;">svr.Delete(</span><span style="color:#183691;">&quot;/path&quot;</span><span style="color:#323232;">, handler); </span><span style="font-style:italic;color:#969896;">// DELETE
</span></pre>
</div></div>
<p>ハンドラーのシグネチャは <code>(const httplib::Request &amp;req, httplib::Response &amp;res)</code> です。<code>auto</code> を使って短く書くこともできます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>パスが一致したときだけハンドラーが呼ばれます。登録されていないパスにアクセスすると、自動的に404が返ります。</p>
<h2>リクエストオブジェクト</h2>
<p>ハンドラーの第1引数 <code>req</code> から、クライアントが送ってきた情報を読み取れます。</p>
<h3>ボディ</h3>
<p><code>req.body</code> でリクエストボディを取得できます。型は <code>std::string</code> です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// クライアントが送ったボディをそのまま返す
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// クライアントが送ったボディをそのまま返す
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>ヘッダー</h3>
<p><code>req.get_header_value()</code> でリクエストヘッダーの値を取得できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/check</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> auth = req.</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Auth: </span><span style="color:#d3d0c8;">&quot; + auth, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/check&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> auth </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_header_value(</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Auth: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> auth, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>クエリパラメーターとフォームデータ</h3>
<p><code>req.get_param_value()</code> でパラメーターを取得できます。GETのクエリパラメーターと、POSTのフォームデータの両方に使えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>/search?q=cpp-httplib</code> にアクセスすると、<code>q</code> の値は <code>"cpp-httplib"</code> になります。</p>
<p>すべてのパラメーターをループで処理したいときは、<code>req.params</code> を使います。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>ファイルアップロード</h3>
<p>マルチパートフォームでアップロードされたファイルは、<code>req.form.get_file()</code> で取得します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>f.filename</code> でファイル名、<code>f.content</code> でファイルの中身にアクセスできます。</p>
<h2>パスパラメーター</h2>
<p>URLの一部を変数として受け取りたいことがあります。たとえば <code>/users/42</code><code>42</code> を取得したい場合です。<code>:param</code> 記法を使うと、URLの一部をキャプチャできます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>/users/42</code> にアクセスすると、<code>req.path_params.at("id")</code><code>"42"</code> を返します。<code>/users/100</code> なら <code>"100"</code> です。</p>
<p>複数のパスパラメーターも使えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:user_id/posts/:post_id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> user_id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user_id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> post_id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">post_id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User: </span><span style="color:#d3d0c8;">&quot; + user_id + &quot;</span><span style="color:#99cc99;">, Post: </span><span style="color:#d3d0c8;">&quot; + post_id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:user_id/posts/:post_id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> user_id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;user_id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> post_id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;post_id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> user_id </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;, Post: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> post_id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>正規表現パターン</h3>
<p><code>:param</code> の代わりに正規表現をパスに書くこともできます。キャプチャグループの値は <code>req.matches</code> で取得します。型は <code>std::smatch</code> です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// 数字のみのIDを受け付ける
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">]; </span><span style="color:#747369;">// 最初のキャプチャグループ
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// 数字のみのIDを受け付ける
</span><span style="color:#323232;">svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">]; </span><span style="font-style:italic;color:#969896;">// 最初のキャプチャグループ
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>/files/42</code> にはマッチしますが、<code>/files/abc</code> にはマッチしません。入力値を絞り込みたいときに便利です。</p>
<h2>レスポンスの組み立て</h2>
<p>ハンドラーの第2引数 <code>res</code> を使って、クライアントに返すレスポンスを組み立てます。</p>
<h3>ボディとContent-Type</h3>
<p><code>res.set_content()</code> でボディとContent-Typeを設定します。これだけでステータスコード200のレスポンスが返ります。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>ステータスコード</h3>
<p>ステータスコードを変えたいときは、<code>res.status</code> に代入します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/not-found</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">= </span><span style="color:#f99157;">404</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Not found</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/not-found&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">404</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Not found&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h3>レスポンスヘッダー</h3>
<p><code>res.set_header()</code> でレスポンスヘッダーを追加できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/with-header</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_header</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">X-Custom</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">my-value</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/with-header&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_header(</span><span style="color:#183691;">&quot;X-Custom&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;my-value&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>前章のサーバーを読み解く</h2>
<p>ここまでの知識を使って、前章で用意したテスト用サーバーを改めて見てみましょう。</p>
<h3>GET /hi</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/hi</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/hi&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>最もシンプルなハンドラーです。リクエストの情報は使わないので、<code>req</code> の変数名を省略しています。<code>"Hello!"</code> というテキストをそのまま返します。</p>
<h3>GET /search</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/search</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> q = req.</span><span style="color:#6699cc;">get_param_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">q</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Query: </span><span style="color:#d3d0c8;">&quot; + q, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/search&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> q </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_param_value(</span><span style="color:#183691;">&quot;q&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Query: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> q, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>req.get_param_value("q")</code> でクエリパラメーター <code>q</code> の値を取り出します。<code>/search?q=cpp-httplib</code> なら、レスポンスは <code>"Query: cpp-httplib"</code> になります。</p>
<h3>POST /post</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/post</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(req.</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/post&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(req.body, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>クライアントが送ったリクエストボディを、そのままレスポンスとして返すエコーサーバーです。<code>req.body</code> にボディが丸ごと入っています。</p>
<h3>POST /submit</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/submit</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::string result;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">for </span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;[key, val] : req.</span><span style="color:#f2777a;">params</span><span style="color:#d3d0c8;">) {
</span><span style="color:#d3d0c8;"> result += key + &quot;</span><span style="color:#99cc99;"> = </span><span style="color:#d3d0c8;">&quot; + val + &quot;</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(result, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/submit&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::string result;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">[key, val] </span><span style="font-weight:bold;color:#a71d5d;">:</span><span style="color:#323232;"> req.params) {
</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> key </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; = &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> val </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot;</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> res.set_content(result, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>フォームデータとして送られたキーと値のペアを、<code>req.params</code> でループ処理しています。構造化束縛 <code>auto &amp;[key, val]</code> を使って、各ペアを取り出しています。</p>
<h3>POST /upload</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> f = req.</span><span style="color:#f2777a;">form</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">get_file</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> content = f.</span><span style="color:#f2777a;">filename </span><span style="color:#d3d0c8;">+ &quot;</span><span style="color:#99cc99;"> (</span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::to_string</span><span style="color:#d3d0c8;">(f.</span><span style="color:#f2777a;">content</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()) + &quot;</span><span style="color:#99cc99;"> bytes)</span><span style="color:#d3d0c8;">&quot;;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(content, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> f </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.form.get_file(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> content </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> f.filename </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; (&quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::to_string(f.content.size()) </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#183691;">&quot; bytes)&quot;</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(content, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>マルチパートフォームで送られたファイルを受け取ります。<code>req.form.get_file("file")</code><code>"file"</code> という名前のフィールドを取得し、<code>f.filename</code><code>f.content.size()</code> でファイル名とサイズを返しています。</p>
<h3>GET /users/:id</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/users/:id</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">path_params</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">id</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">User ID: </span><span style="color:#d3d0c8;">&quot; + id, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/users/:id&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.path_params.at(</span><span style="color:#183691;">&quot;id&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;User ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> id, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>:id</code> の部分がパスパラメーターです。<code>req.path_params.at("id")</code> で値を取り出しています。<code>/users/42</code> なら <code>"42"</code><code>/users/alice</code> なら <code>"alice"</code> が得られます。</p>
<h3>GET /files/(\d+)</h3>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">/files/(\d+)</span><span style="color:#d3d0c8;">)&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> id = req.</span><span style="color:#f2777a;">matches</span><span style="color:#d3d0c8;">[</span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">];
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">File ID: </span><span style="color:#d3d0c8;">&quot; + </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(id), &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.Get(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;(/files/(\d+))&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> id </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.matches[</span><span style="color:#0086b3;">1</span><span style="color:#323232;">];
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;File ID: &quot; </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#323232;">std::string(id), </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>正規表現 <code>(\d+)</code> で数字だけのIDにマッチします。<code>/files/42</code> にはマッチしますが、<code>/files/abc</code> は404になります。<code>req.matches[1]</code> で最初のキャプチャグループの値を取得しています。</p>
<h2>次のステップ</h2>
<p>サーバーの基本がわかりましたね。ルーティング、リクエストの読み取り、レスポンスの組み立て。これだけで、十分に実用的なAPIサーバーが作れます。</p>
<p>次は、静的ファイルの配信を見てみましょう。HTMLやCSSを配信するサーバーを作ります。</p>
<p><strong>次:</strong> <a href="../04-static-file-server">Static File Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Static File Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="active">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>Static File Server</h1>
<p>cpp-httplibは、HTMLやCSS、画像ファイルなどの静的ファイルも配信できます。面倒な設定は要りません。<code>set_mount_point()</code> を1行呼ぶだけです。</p>
<h2>set_mount_point の基本</h2>
<p>さっそくやってみましょう。<code>set_mount_point()</code> は、URLのパスとローカルディレクトリを紐づけます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./html</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./html&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>第1引数がURLのマウントポイント、第2引数がローカルのディレクトリパスです。この例だと、<code>/</code> へのリクエストを <code>./html</code> ディレクトリから配信します。</p>
<p>試してみましょう。まず <code>html</code> ディレクトリを作って、<code>index.html</code> を置きます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">mkdir</span><span style="color:#d3d0c8;"> html
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">mkdir html
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;!</span><span style="color:#f2777a;">DOCTYPE</span><span style="color:#d3d0c8;"> html&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">html</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">head</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">title</span><span style="color:#d3d0c8;">&gt;My Page&lt;/</span><span style="color:#f2777a;">title</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">head</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;"> &lt;</span><span style="color:#f2777a;">h1</span><span style="color:#d3d0c8;">&gt;Hello from cpp-httplib!&lt;/</span><span style="color:#f2777a;">h1</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;"> &lt;</span><span style="color:#f2777a;">p</span><span style="color:#d3d0c8;">&gt;This is a static file.&lt;/</span><span style="color:#f2777a;">p</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">html</span><span style="color:#d3d0c8;">&gt;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">&lt;!</span><span style="color:#63a35c;">DOCTYPE</span><span style="color:#323232;"> html&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">html</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">head</span><span style="color:#323232;">&gt;&lt;</span><span style="color:#63a35c;">title</span><span style="color:#323232;">&gt;My Page&lt;/</span><span style="color:#63a35c;">title</span><span style="color:#323232;">&gt;&lt;/</span><span style="color:#63a35c;">head</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;</span><span style="color:#63a35c;">body</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;"> &lt;</span><span style="color:#63a35c;">h1</span><span style="color:#323232;">&gt;Hello from cpp-httplib!&lt;/</span><span style="color:#63a35c;">h1</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;"> &lt;</span><span style="color:#63a35c;">p</span><span style="color:#323232;">&gt;This is a static file.&lt;/</span><span style="color:#63a35c;">p</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;/</span><span style="color:#63a35c;">body</span><span style="color:#323232;">&gt;
</span><span style="color:#323232;">&lt;/</span><span style="color:#63a35c;">html</span><span style="color:#323232;">&gt;
</span></pre>
</div></div>
<p>コンパイルして起動します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">g++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp</span><span style="color:#f2777a;"> -pthread
</span><span style="color:#6699cc;">./server
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">g++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -o server server.cpp -pthread
</span><span style="color:#323232;">./server
</span></pre>
</div></div>
<p>ブラウザで <code>http://localhost:8080</code> を開いてみてください。<code>html/index.html</code> の内容が表示されるはずです。<code>http://localhost:8080/index.html</code> でも同じページが返ります。</p>
<p>もちろん、前章のクライアントコードや <code>curl</code> でもアクセスできますよ。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// HTMLが表示される
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// HTMLが表示される
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> http://localhost:8080
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl http://localhost:8080
</span></pre>
</div></div>
<h2>複数のマウントポイント</h2>
<p><code>set_mount_point()</code> は何回でも呼べます。URLのパスごとに、別々のディレクトリを割り当てられます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/assets</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./static/assets</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/docs</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./documentation</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/assets&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./static/assets&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/docs&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./documentation&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p><code>/assets/style.css</code> なら <code>./static/assets/style.css</code> を、<code>/docs/guide.html</code> なら <code>./documentation/guide.html</code> を配信します。</p>
<h2>ハンドラーとの組み合わせ</h2>
<p>静的ファイルの配信と、前章で学んだルーティングハンドラーは共存できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// APIエンドポイント
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/api/hello</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(</span><span style="color:#cc99cc;">R</span><span style="color:#d3d0c8;">&quot;(</span><span style="color:#99cc99;">{&quot;message&quot;:&quot;Hello!&quot;}</span><span style="color:#d3d0c8;">)&quot;, &quot;</span><span style="color:#99cc99;">application/json</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// 静的ファイル配信
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// APIエンドポイント
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/api/hello&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="font-weight:bold;color:#a71d5d;">R</span><span style="color:#183691;">&quot;({&quot;message&quot;:&quot;Hello!&quot;})&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;application/json&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// 静的ファイル配信
</span><span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>ハンドラーが先に評価されます。<code>/api/hello</code> にはハンドラーが応答し、それ以外のパスは <code>./public</code> ディレクトリからファイルを探します。</p>
<h2>レスポンスヘッダーの追加</h2>
<p><code>set_mount_point()</code> の第3引数にヘッダーを渡すと、静的ファイルのレスポンスにカスタムヘッダーを付けられます。キャッシュ制御に便利です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_mount_point</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">./public</span><span style="color:#d3d0c8;">&quot;, {
</span><span style="color:#d3d0c8;"> {&quot;</span><span style="color:#99cc99;">Cache-Control</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">max-age=3600</span><span style="color:#d3d0c8;">&quot;}
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_mount_point(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;./public&quot;</span><span style="color:#323232;">, {
</span><span style="color:#323232;"> {</span><span style="color:#183691;">&quot;Cache-Control&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;max-age=3600&quot;</span><span style="color:#323232;">}
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>こうすると、ブラウザは配信されたファイルを1時間キャッシュします。</p>
<h2>静的ファイルサーバー用のDockerファイル</h2>
<p>cpp-httplibのリポジトリには、静的ファイルサーバー用の <code>Dockerfile</code> が含まれています。Docker Hubにビルド済みイメージも公開しているので、1コマンドで起動できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&gt; docker run</span><span style="color:#f2777a;"> -p</span><span style="color:#d3d0c8;"> 8080:80</span><span style="color:#f2777a;"> -v</span><span style="color:#d3d0c8;"> ./my-site:/html yhirose4dockerhub/cpp-httplib-server
</span><span style="color:#6699cc;">Serving</span><span style="color:#d3d0c8;"> HTTP on 0.0.0.0:80
</span><span style="color:#6699cc;">Mount</span><span style="color:#d3d0c8;"> point: / -&gt; ./html
</span><span style="color:#6699cc;">Press</span><span style="color:#d3d0c8;"> Ctrl+C to shutdown gracefully...
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:00 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET / HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 200 256 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:00 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET /style.css HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 200 1024 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#6699cc;">192.168.65.1</span><span style="color:#d3d0c8;"> - - </span><span style="color:#cc99cc;">[</span><span style="color:#d3d0c8;">22/Feb/2026:12:00:01 +0000</span><span style="color:#cc99cc;">] </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">GET /favicon.ico HTTP/1.1</span><span style="color:#d3d0c8;">&quot; 404 152 &quot;</span><span style="color:#99cc99;">-</span><span style="color:#d3d0c8;">&quot; &quot;</span><span style="color:#99cc99;">Mozilla/5.0 ...</span><span style="color:#d3d0c8;">&quot;
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> docker run -p 8080:80 -v ./my-site:/html yhirose4dockerhub/cpp-httplib-server
</span><span style="color:#323232;">Serving HTTP on 0.0.0.0:80
</span><span style="color:#323232;">Mount point: / -</span><span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> ./html
</span><span style="color:#323232;">Press Ctrl+C to shutdown gracefully...
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:00 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET / HTTP/1.1&quot;</span><span style="color:#323232;"> 200 256 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:00 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET /style.css HTTP/1.1&quot;</span><span style="color:#323232;"> 200 1024 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span><span style="color:#323232;">192.168.65.1 - - </span><span style="font-weight:bold;color:#a71d5d;">[</span><span style="color:#323232;">22/Feb/2026:12:00:01 +0000</span><span style="font-weight:bold;color:#a71d5d;">] </span><span style="color:#183691;">&quot;GET /favicon.ico HTTP/1.1&quot;</span><span style="color:#323232;"> 404 152 </span><span style="color:#183691;">&quot;-&quot; &quot;Mozilla/5.0 ...&quot;
</span></pre>
</div></div>
<p><code>./my-site</code> ディレクトリの中身が、そのままポート8080で配信されます。NGINXと同じログ形式で、アクセスの様子を確認できますよ。</p>
<h2>次のステップ</h2>
<p>静的ファイルの配信ができるようになりましたね。HTMLやCSS、JavaScriptを配信するWebサーバーが、これだけのコードで作れます。</p>
<p>次は、HTTPSで暗号化通信をしてみましょう。まずはTLSライブラリのセットアップからです。</p>
<p><strong>次:</strong> <a href="../05-tls-setup">TLS Setup</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+229
View File
@@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TLS Setup - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="active">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>TLS Setup</h1>
<p>ここまではHTTP(平文)でやってきましたが、実際のWebではHTTPS(暗号化通信)が当たり前ですよね。cpp-httplibでHTTPSを使うには、TLSライブラリが必要です。</p>
<p>このTourではOpenSSLを使います。最も広く使われていて、情報も豊富です。</p>
<h2>OpenSSLのインストール</h2>
<p>お使いのOSに合わせてインストールしましょう。</p>
<table><thead><tr><th>OS</th><th>インストール方法</th></tr></thead><tbody>
<tr><td>macOS</td><td><a href="https://brew.sh/">Homebrew</a> (<code>brew install openssl</code>)</td></tr>
<tr><td>Ubuntu / Debian</td><td><code>sudo apt install libssl-dev</code></td></tr>
<tr><td>Windows</td><td><a href="https://vcpkg.io/">vcpkg</a> (<code>vcpkg install openssl</code>)</td></tr>
</tbody></table>
<h2>コンパイルオプション</h2>
<p>TLS機能を有効にするには、<code>CPPHTTPLIB_OPENSSL_SUPPORT</code> マクロを定義してコンパイルします。前章までのコンパイルコマンドに、いくつかオプションが増えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;"># macOS (Homebrew)
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -DCPPHTTPLIB_OPENSSL_SUPPORT </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -I</span><span style="color:#d3d0c8;">$</span><span style="color:#f2777a;">(</span><span style="color:#6699cc;">brew</span><span style="color:#f2777a;"> --prefix openssl)</span><span style="color:#d3d0c8;">/include \
</span><span style="color:#f2777a;"> -L</span><span style="color:#d3d0c8;">$</span><span style="color:#f2777a;">(</span><span style="color:#6699cc;">brew</span><span style="color:#f2777a;"> --prefix openssl)</span><span style="color:#d3d0c8;">/lib \
</span><span style="color:#f2777a;"> -lssl -lcrypto </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -framework</span><span style="color:#d3d0c8;"> CoreFoundation</span><span style="color:#f2777a;"> -framework</span><span style="color:#d3d0c8;"> Security \
</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Linux
</span><span style="color:#6699cc;">clang++</span><span style="color:#f2777a;"> -std</span><span style="color:#d3d0c8;">=c++17</span><span style="color:#f2777a;"> -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -lssl -lcrypto </span><span style="color:#d3d0c8;">\
</span><span style="color:#f2777a;"> -o</span><span style="color:#d3d0c8;"> server server.cpp
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;"># Windows (Developer Command Prompt)
</span><span style="color:#6699cc;">cl</span><span style="color:#d3d0c8;"> /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;"># macOS (Homebrew)
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -DCPPHTTPLIB_OPENSSL_SUPPORT \
</span><span style="color:#323232;"> -I$(brew --prefix openssl)/include \
</span><span style="color:#323232;"> -L$(brew --prefix openssl)/lib \
</span><span style="color:#323232;"> -lssl -lcrypto \
</span><span style="color:#323232;"> -framework CoreFoundation -framework Security \
</span><span style="color:#323232;"> -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Linux
</span><span style="color:#323232;">clang++ -std</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;">c++17 -pthread -DCPPHTTPLIB_OPENSSL_SUPPORT \
</span><span style="color:#323232;"> -lssl -lcrypto \
</span><span style="color:#323232;"> -o server server.cpp
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;"># Windows (Developer Command Prompt)
</span><span style="color:#323232;">cl /EHsc /std:c++17 /DCPPHTTPLIB_OPENSSL_SUPPORT server.cpp libssl.lib libcrypto.lib
</span></pre>
</div></div>
<p>それぞれのオプションの役割を見てみましょう。</p>
<ul>
<li><strong><code>-DCPPHTTPLIB_OPENSSL_SUPPORT</code></strong> — TLS機能を有効にするマクロ定義</li>
<li><strong><code>-lssl -lcrypto</code></strong> — OpenSSLのライブラリをリンク</li>
<li><strong><code>-I</code> / <code>-L</code></strong>macOSのみ)— Homebrew版OpenSSLのパスを指定</li>
<li><strong><code>-framework CoreFoundation -framework Security</code></strong>macOSのみ)— Keychainからシステム証明書を自動で読み込むために必要です</li>
</ul>
<h2>動作確認</h2>
<p>ちゃんと動くか確認してみましょう。<code>httplib::Client</code> にHTTPSのURLを渡してアクセスするだけのプログラムです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://www.google.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Status: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://www.google.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Status: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>コンパイルして実行してみてください。<code>Status: 200</code> と表示されれば、セットアップ完了です。</p>
<h2>他のTLSバックエンド</h2>
<p>cpp-httplibはOpenSSL以外にも、Mbed TLSとwolfSSLに対応しています。マクロ定義とリンクするライブラリを変えるだけで切り替えられます。</p>
<table><thead><tr><th style="text-align: left">バックエンド</th><th style="text-align: left">マクロ定義</th><th style="text-align: left">リンクするライブラリ</th></tr></thead><tbody>
<tr><td style="text-align: left">OpenSSL</td><td style="text-align: left"><code>CPPHTTPLIB_OPENSSL_SUPPORT</code></td><td style="text-align: left"><code>libssl</code>, <code>libcrypto</code></td></tr>
<tr><td style="text-align: left">Mbed TLS</td><td style="text-align: left"><code>CPPHTTPLIB_MBEDTLS_SUPPORT</code></td><td style="text-align: left"><code>libmbedtls</code>, <code>libmbedx509</code>, <code>libmbedcrypto</code></td></tr>
<tr><td style="text-align: left">wolfSSL</td><td style="text-align: left"><code>CPPHTTPLIB_WOLFSSL_SUPPORT</code></td><td style="text-align: left"><code>libwolfssl</code></td></tr>
</tbody></table>
<p>このTourではOpenSSLを前提に進めますが、APIはどのバックエンドでも共通です。</p>
<h2>次のステップ</h2>
<p>TLSの準備ができましたね。次は、HTTPSサイトにリクエストを送ってみましょう。</p>
<p><strong>次:</strong> <a href="../06-https-client">HTTPS Client</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+287
View File
@@ -0,0 +1,287 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTPS Client - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="active">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>HTTPS Client</h1>
<p>前章でOpenSSLのセットアップが済んだので、さっそくHTTPSクライアントを使ってみましょう。2章で使った <code>httplib::Client</code> がそのまま使えます。コンストラクタに <code>https://</code> 付きのURLを渡すだけです。</p>
<h2>GETリクエスト</h2>
<p>実在するHTTPSサイトにアクセスしてみましょう。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">substr</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">) &lt;&lt; std::endl; </span><span style="color:#747369;">// HTMLの先頭部分
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Error: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; </span><span style="color:#6699cc;">httplib::to_string</span><span style="color:#d3d0c8;">(res.</span><span style="color:#6699cc;">error</span><span style="color:#d3d0c8;">()) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body.substr(</span><span style="color:#0086b3;">0</span><span style="color:#323232;">, </span><span style="color:#0086b3;">100</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// HTMLの先頭部分
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Error: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#323232;">httplib::to_string(res.error()) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>2章では <code>httplib::Client cli("http://localhost:8080")</code> と書きましたよね。スキームを <code>https://</code> に変えるだけです。<code>Get()</code><code>Post()</code> など、2章で学んだAPIはすべてそのまま使えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> https://nghttp2.org/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl https://nghttp2.org/
</span></pre>
</div></div>
<h2>ポートの指定</h2>
<p>HTTPSのデフォルトポートは443です。別のポートを使いたい場合は、URLにポートを含めます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>CA証明書の検証</h2>
<p><code>httplib::Client</code> はHTTPS接続時、デフォルトでサーバー証明書を検証します。信頼できるCA(認証局)が発行した証明書を持つサーバーにしか接続しません。</p>
<p>CA証明書は、macOSならKeychain、LinuxならシステムのCA証明書ストア、WindowsならWindowsの証明書ストアから自動で読み込みます。ほとんどの場合、追加の設定は要りません。</p>
<h3>CA証明書ファイルの指定</h3>
<p>環境によってはシステムのCA証明書が見つからないこともあります。そのときは <code>set_ca_cert_path()</code> でパスを直接指定してください。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_ca_cert_path</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/etc/ssl/certs/ca-certificates.crt</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_ca_cert_path(</span><span style="color:#183691;">&quot;/etc/ssl/certs/ca-certificates.crt&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> --cacert</span><span style="color:#d3d0c8;"> /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl --cacert /etc/ssl/certs/ca-certificates.crt https://nghttp2.org/
</span></pre>
</div></div>
<h3>証明書検証の無効化</h3>
<p>開発中、自己署名証明書のサーバーに接続したいときは、検証を無効にできます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">enable_server_certificate_verification</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.enable_server_certificate_verification(</span><span style="color:#0086b3;">false</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -k</span><span style="color:#d3d0c8;"> https://localhost:8443/
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -k https://localhost:8443/
</span></pre>
</div></div>
<p>本番では絶対に無効にしないでください。中間者攻撃のリスクがあります。</p>
<h2>リダイレクトの追跡</h2>
<p>HTTPSサイトへのアクセスでは、リダイレクトに遭遇することがよくあります。たとえば <code>http://</code> から <code>https://</code> へ、あるいは <code>www</code> なしから <code>www</code> ありへ転送されるケースです。</p>
<p>デフォルトではリダイレクトを追跡しません。リダイレクト先は <code>Location</code> ヘッダーで確認できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/httpbin/redirect/3</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 302
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Location</span><span style="color:#d3d0c8;">&quot;) &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/httpbin/redirect/3&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 302
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;get_header_value(</span><span style="color:#183691;">&quot;Location&quot;</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#d3d0c8;"> https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div></div>
<p><code>set_follow_location(true)</code> を設定すると、リダイレクトを自動で追跡して、最終的なレスポンスを返してくれます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://nghttp2.org</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_follow_location</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/httpbin/redirect/3</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// 200(最終的なレスポンス)
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://nghttp2.org&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_follow_location(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/httpbin/redirect/3&quot;</span><span style="color:#323232;">);
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// 200(最終的なレスポンス)
</span><span style="color:#323232;">}
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -L</span><span style="color:#d3d0c8;"> https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -L https://nghttp2.org/httpbin/redirect/3
</span></pre>
</div></div>
<h2>次のステップ</h2>
<p>HTTPSクライアントの使い方がわかりましたね。次は自分でHTTPSサーバーを立ててみましょう。自己署名証明書の作り方から始めます。</p>
<p><strong>次:</strong> <a href="../07-https-server">HTTPS Server</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+276
View File
@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTPS Server - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="active">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>HTTPS Server</h1>
<p>前章ではHTTPSクライアントを使いました。今度は自分でHTTPSサーバーを立ててみましょう。3章の <code>httplib::Server</code><code>httplib::SSLServer</code> に置き換えるだけです。</p>
<p>ただし、TLSサーバーにはサーバー証明書と秘密鍵が必要です。まずはそこから準備しましょう。</p>
<h2>自己署名証明書の作成</h2>
<p>開発やテスト用なら、自己署名証明書(いわゆるオレオレ証明書)で十分です。OpenSSLのコマンドでサクッと作れます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">openssl</span><span style="color:#d3d0c8;"> req</span><span style="color:#f2777a;"> -x509 -noenc -keyout</span><span style="color:#d3d0c8;"> key.pem</span><span style="color:#f2777a;"> -out</span><span style="color:#d3d0c8;"> cert.pem</span><span style="color:#f2777a;"> -subj</span><span style="color:#d3d0c8;"> /CN=localhost
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">openssl req -x509 -noenc -keyout key.pem -out cert.pem -subj /CN=localhost
</span></pre>
</div></div>
<p>これで2つのファイルができます。</p>
<ul>
<li><strong><code>cert.pem</code></strong> — サーバー証明書</li>
<li><strong><code>key.pem</code></strong> — 秘密鍵</li>
</ul>
<h2>最小のHTTPSサーバー</h2>
<p>証明書ができたら、さっそくサーバーを書いてみましょう。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::SSLServer </span><span style="color:#6699cc;">svr</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">key.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, HTTPS!</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on https://localhost:8443</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8443</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::SSLServer svr(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;key.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, HTTPS!&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on https://localhost:8443&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8443</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>httplib::SSLServer</code> のコンストラクタに証明書と秘密鍵のパスを渡すだけです。ルーティングの書き方は3章の <code>httplib::Server</code> とまったく同じですよ。</p>
<p>コンパイルして起動しましょう。</p>
<h2>動作確認</h2>
<p>サーバーが起動したら、<code>curl</code> でアクセスしてみましょう。自己署名証明書なので、<code>-k</code> オプションで証明書検証をスキップします。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">curl</span><span style="color:#f2777a;"> -k</span><span style="color:#d3d0c8;"> https://localhost:8443/
</span><span style="color:#747369;"># Hello, HTTPS!
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">curl -k https://localhost:8443/
</span><span style="font-style:italic;color:#969896;"># Hello, HTTPS!
</span></pre>
</div></div>
<p>ブラウザで <code>https://localhost:8443</code> を開くと、「この接続は安全ではありません」と警告が出ます。自己署名証明書なので正常です。気にせず進めてください。</p>
<h2>クライアントからの接続</h2>
<p>前章の <code>httplib::Client</code> で接続してみましょう。自己署名証明書のサーバーに接続するには、2つの方法があります。</p>
<h3>方法1: 証明書検証を無効にする</h3>
<p>開発時の手軽な方法です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> cli.</span><span style="color:#6699cc;">enable_server_certificate_verification</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, HTTPS!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> cli.enable_server_certificate_verification(</span><span style="color:#0086b3;">false</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, HTTPS!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<h3>方法2: 自己署名証明書をCA証明書として指定する</h3>
<p>こちらのほうが安全です。<code>cert.pem</code> をCA証明書として信頼するよう指定します。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#define </span><span style="color:#d3d0c8;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://localhost:8443</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> cli.</span><span style="color:#6699cc;">set_ca_cert_path</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; res-&gt;</span><span style="color:#f2777a;">body </span><span style="color:#d3d0c8;">&lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, HTTPS!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#define </span><span style="color:#323232;">CPPHTTPLIB_OPENSSL_SUPPORT
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Client cli(</span><span style="color:#183691;">&quot;https://localhost:8443&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> cli.set_ca_cert_path(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res-&gt;body </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, HTTPS!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>この方法なら、指定した証明書のサーバーにだけ接続を許可して、なりすましを防げます。テスト環境でもなるべくこちらを使いましょう。</p>
<h2>Server と SSLServer の比較</h2>
<p>3章で学んだ <code>httplib::Server</code> のAPIは、<code>httplib::SSLServer</code> でもそのまま使えます。違いはコンストラクタだけです。</p>
<table><thead><tr><th></th><th><code>httplib::Server</code></th><th><code>httplib::SSLServer</code></th></tr></thead><tbody>
<tr><td>コンストラクタ</td><td>引数なし</td><td>証明書と秘密鍵のパス</td></tr>
<tr><td>プロトコル</td><td>HTTP</td><td>HTTPS</td></tr>
<tr><td>ポート(慣例)</td><td>8080</td><td>8443</td></tr>
<tr><td>ルーティング</td><td>共通</td><td>共通</td></tr>
</tbody></table>
<p>HTTPサーバーをHTTPSに切り替えるには、コンストラクタを変えるだけです。</p>
<h2>次のステップ</h2>
<p>HTTPSサーバーが動きましたね。これでHTTP/HTTPSのクライアントとサーバー、両方の基本がそろいました。</p>
<p>次は、cpp-httplibに新しく加わったWebSocket機能を見てみましょう。</p>
<p><strong>次:</strong> <a href="../08-websocket">WebSocket</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+328
View File
@@ -0,0 +1,328 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WebSocket - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="active">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>WebSocket</h1>
<p>cpp-httplibはWebSocketにも対応しています。HTTPのリクエスト/レスポンスと違い、WebSocketはサーバーとクライアントが双方向にメッセージをやり取りできます。チャットやリアルタイム通知に便利です。</p>
<p>さっそく、エコーサーバーとクライアントを作ってみましょう。</p>
<h2>エコーサーバー</h2>
<p>受け取ったメッセージをそのまま返すエコーサーバーです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::Server svr;
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg); </span><span style="color:#747369;">// 受け取ったメッセージをそのまま返す
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Listening on port 8080...</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::Server svr;
</span><span style="color:#323232;">
</span><span style="color:#323232;"> svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg); </span><span style="font-style:italic;color:#969896;">// 受け取ったメッセージをそのまま返す
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Listening on port 8080...&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>svr.WebSocket()</code> でWebSocketハンドラーを登録します。3章の <code>svr.Get()</code><code>svr.Post()</code> と同じ感覚ですね。</p>
<p>ハンドラーの中では、<code>ws.read(msg)</code> でメッセージを待ちます。接続が閉じられると <code>read()</code><code>false</code> を返すので、ループを抜けます。<code>ws.send(msg)</code> でメッセージを送り返します。</p>
<h2>クライアントからの接続</h2>
<p><code>httplib::ws::WebSocketClient</code> を使ってサーバーに接続してみましょう。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">httplib.h</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#cc99cc;">#include </span><span style="color:#d3d0c8;">&lt;</span><span style="color:#99cc99;">iostream</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">int </span><span style="color:#6699cc;">main</span><span style="color:#d3d0c8;">() {
</span><span style="color:#d3d0c8;"> httplib::ws::WebSocketClient </span><span style="color:#6699cc;">client</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">ws://localhost:8080/ws</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(!client.</span><span style="color:#6699cc;">connect</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; &quot;</span><span style="color:#99cc99;">Connection failed</span><span style="color:#d3d0c8;">&quot; &lt;&lt; std::endl;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">1</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// メッセージを送信
</span><span style="color:#d3d0c8;"> client.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, WebSocket!</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// サーバーからの応答を受信
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(client.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; msg &lt;&lt; std::endl; </span><span style="color:#747369;">// Hello, WebSocket!
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> client.</span><span style="color:#6699cc;">close</span><span style="color:#d3d0c8;">();
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&quot;httplib.h&quot;
</span><span style="font-weight:bold;color:#a71d5d;">#include </span><span style="color:#183691;">&lt;iostream&gt;
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;"> httplib::ws::WebSocketClient client(</span><span style="color:#183691;">&quot;ws://localhost:8080/ws&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">!</span><span style="color:#323232;">client.connect()) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;Connection failed&quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// メッセージを送信
</span><span style="color:#323232;"> client.send(</span><span style="color:#183691;">&quot;Hello, WebSocket!&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// サーバーからの応答を受信
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(client.read(msg)) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl; </span><span style="font-style:italic;color:#969896;">// Hello, WebSocket!
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> client.close();
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p>コンストラクタには <code>ws://host:port/path</code> 形式のURLを渡します。<code>connect()</code> で接続を開始し、<code>send()</code><code>read()</code> でメッセージをやり取りします。</p>
<h2>テキストとバイナリ</h2>
<p>WebSocketにはテキストとバイナリの2種類のメッセージがあります。<code>read()</code> の戻り値で区別できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> httplib::ws::ReadResult ret;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">((ret = ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg))) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(ret == httplib::ws::Binary) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg.</span><span style="color:#6699cc;">data</span><span style="color:#d3d0c8;">(), msg.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">()); </span><span style="color:#747369;">// バイナリとして送信
</span><span style="color:#d3d0c8;"> } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg); </span><span style="color:#747369;">// テキストとして送信
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> httplib::ws::ReadResult ret;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">((ret </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> ws.read(msg))) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(ret </span><span style="font-weight:bold;color:#a71d5d;">==</span><span style="color:#323232;"> httplib::ws::Binary) {
</span><span style="color:#323232;"> ws.send(msg.data(), msg.size()); </span><span style="font-style:italic;color:#969896;">// バイナリとして送信
</span><span style="color:#323232;"> } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;"> ws.send(msg); </span><span style="font-style:italic;color:#969896;">// テキストとして送信
</span><span style="color:#323232;"> }
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<ul>
<li><code>ws.send(const std::string &amp;)</code> — テキストメッセージとして送信</li>
<li><code>ws.send(const char *, size_t)</code> — バイナリメッセージとして送信</li>
</ul>
<p>クライアント側も同じAPIです。</p>
<h2>リクエスト情報へのアクセス</h2>
<p>ハンドラーの第1引数 <code>req</code> から、ハンドシェイク時のHTTPリクエスト情報を読み取れます。認証トークンの確認などに便利です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;req, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> token = req.</span><span style="color:#6699cc;">get_header_value</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Authorization</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(token.</span><span style="color:#6699cc;">empty</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">close</span><span style="color:#d3d0c8;">(httplib::ws::CloseStatus::PolicyViolation, &quot;</span><span style="color:#99cc99;">unauthorized</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg);
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">req, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> token </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> req.get_header_value(</span><span style="color:#183691;">&quot;Authorization&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(token.empty()) {
</span><span style="color:#323232;"> ws.close(httplib::ws::CloseStatus::PolicyViolation, </span><span style="color:#183691;">&quot;unauthorized&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;">;
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg);
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>WSSで使う</h2>
<p>HTTPS上のWebSocket(WSS)にも対応しています。サーバー側は <code>httplib::SSLServer</code> にWebSocketハンドラーを登録するだけです。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::SSLServer </span><span style="color:#6699cc;">svr</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">cert.pem</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">key.pem</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">WebSocket</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/ws</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::Request &amp;, httplib::ws::WebSocket &amp;ws) {
</span><span style="color:#d3d0c8;"> std::string msg;
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(ws.</span><span style="color:#6699cc;">read</span><span style="color:#d3d0c8;">(msg)) {
</span><span style="color:#d3d0c8;"> ws.</span><span style="color:#6699cc;">send</span><span style="color:#d3d0c8;">(msg);
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">0.0.0.0</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8443</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::SSLServer </span><span style="font-weight:bold;color:#795da3;">svr</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;cert.pem&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;key.pem&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.WebSocket(</span><span style="color:#183691;">&quot;/ws&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::Request </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">, httplib::ws::WebSocket </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">ws) {
</span><span style="color:#323232;"> std::string msg;
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(ws.read(msg)) {
</span><span style="color:#323232;"> ws.send(msg);
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;0.0.0.0&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8443</span><span style="color:#323232;">);
</span></pre>
</div></div>
<p>クライアント側は <code>wss://</code> スキームを使います。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::ws::WebSocketClient </span><span style="color:#6699cc;">client</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">wss://localhost:8443/ws</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::ws::WebSocketClient </span><span style="font-weight:bold;color:#795da3;">client</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;wss://localhost:8443/ws&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>次のステップ</h2>
<p>WebSocketの基本がわかりましたね。ここまでで Tourは終わりです。</p>
<p>次のページでは、Tourで取り上げなかった機能をまとめて紹介します。</p>
<p><strong>次:</strong> <a href="../09-whats-next">What's Next</a></p>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+460
View File
@@ -0,0 +1,460 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>What&#x27;s Next - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="active">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>What&#x27;s Next</h1>
<p>Tourお疲れさまでした! cpp-httplibの基本はひと通り押さえましたね。でも、まだまだ便利な機能があります。Tourで取り上げなかった機能をカテゴリー別に紹介します。</p>
<h2>Streaming API</h2>
<p>LLMのストリーミング応答や大きなファイルのダウンロードでは、レスポンス全体をメモリに載せたくないですよね。<code>stream::Get()</code> を使えば、データをチャンクごとに処理できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:11434</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> result = </span><span style="color:#6699cc;">httplib::stream::Get</span><span style="color:#d3d0c8;">(cli, &quot;</span><span style="color:#99cc99;">/api/generate</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(result) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">while </span><span style="color:#d3d0c8;">(result.</span><span style="color:#6699cc;">next</span><span style="color:#d3d0c8;">()) {
</span><span style="color:#d3d0c8;"> std::cout.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(result.</span><span style="color:#6699cc;">data</span><span style="color:#d3d0c8;">(), result.</span><span style="color:#6699cc;">size</span><span style="color:#d3d0c8;">());
</span><span style="color:#d3d0c8;"> }
</span><span style="color:#d3d0c8;">}
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:11434&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">httplib::stream::Get(cli, </span><span style="color:#183691;">&quot;/api/generate&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(result) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">(result.next()) {
</span><span style="color:#323232;"> std::cout.write(result.data(), result.size());
</span><span style="color:#323232;"> }
</span><span style="color:#323232;">}
</span></pre>
</div></div>
<p><code>Get()</code><code>content_receiver</code> コールバックを渡す方法もあります。こちらはKeep-Aliveと併用できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/stream</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const char </span><span style="color:#d3d0c8;">*data, size_t len) {
</span><span style="color:#d3d0c8;"> std::cout.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(data, len);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">cli.Get(</span><span style="color:#183691;">&quot;/stream&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const char *</span><span style="color:#323232;">data, </span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> len) {
</span><span style="color:#323232;"> std::cout.write(data, len);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>サーバー側には <code>set_content_provider()</code><code>set_chunked_content_provider()</code> があります。サイズがわかっているなら前者、不明なら後者を使ってください。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// サイズ指定あり(Content-Length が設定される)
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/file</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> size = </span><span style="color:#6699cc;">get_file_size</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">large.bin</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content_provider</span><span style="color:#d3d0c8;">(size, &quot;</span><span style="color:#99cc99;">application/octet-stream</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;"> [](size_t offset, size_t length, httplib::DataSink &amp;sink) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// offset から length バイト分を送る
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#747369;">// サイズ不明(Chunked Transfer Encoding
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/stream</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_chunked_content_provider</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;"> [](size_t offset, httplib::DataSink &amp;sink) {
</span><span style="color:#d3d0c8;"> sink.</span><span style="color:#6699cc;">write</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">chunk</span><span style="color:#66cccc;">\n</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">6</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">; </span><span style="color:#747369;">// falseを返すと終了
</span><span style="color:#d3d0c8;"> });
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// サイズ指定あり(Content-Length が設定される)
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/file&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> size </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">get_file_size(</span><span style="color:#183691;">&quot;large.bin&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> res.set_content_provider(size, </span><span style="color:#183691;">&quot;application/octet-stream&quot;</span><span style="color:#323232;">,
</span><span style="color:#323232;"> [](</span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> offset, </span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> length, httplib::DataSink </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">sink) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// offset から length バイト分を送る
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// サイズ不明(Chunked Transfer Encoding
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/stream&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_chunked_content_provider(</span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">,
</span><span style="color:#323232;"> [](</span><span style="color:#0086b3;">size_t</span><span style="color:#323232;"> offset, httplib::DataSink </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">sink) {
</span><span style="color:#323232;"> sink.write(</span><span style="color:#183691;">&quot;chunk</span><span style="color:#0086b3;">\n</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">6</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#0086b3;">true</span><span style="color:#323232;">; </span><span style="font-style:italic;color:#969896;">// falseを返すと終了
</span><span style="color:#323232;"> });
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>大きなファイルのアップロードには <code>make_file_provider()</code> が便利です。ファイルを全部メモリに読み込まず、ストリーミングで送れます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Post</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/upload</span><span style="color:#d3d0c8;">&quot;, {}, {
</span><span style="color:#d3d0c8;"> </span><span style="color:#6699cc;">httplib::make_file_provider</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">file</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">/path/to/large-file.zip</span><span style="color:#d3d0c8;">&quot;)
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Post(</span><span style="color:#183691;">&quot;/upload&quot;</span><span style="color:#323232;">, {}, {
</span><span style="color:#323232;"> httplib::make_file_provider(</span><span style="color:#183691;">&quot;file&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;/path/to/large-file.zip&quot;</span><span style="color:#323232;">)
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Server-Sent Events (SSE)</h2>
<p>SSEクライアントも用意しています。自動再接続や <code>Last-Event-ID</code> による再開にも対応しています。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost:8080</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">httplib::sse::SSEClient </span><span style="color:#6699cc;">sse</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">cli</span><span style="color:#d3d0c8;">, &quot;</span><span style="color:#99cc99;">/events</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">on_message</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::sse::SSEMessage &amp;msg) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; msg.</span><span style="color:#f2777a;">event </span><span style="color:#d3d0c8;">&lt;&lt; &quot;</span><span style="color:#99cc99;">: </span><span style="color:#d3d0c8;">&quot; &lt;&lt; msg.</span><span style="color:#f2777a;">data </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">start</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// ブロッキング、自動再接続あり
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost:8080&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">httplib::sse::SSEClient </span><span style="font-weight:bold;color:#795da3;">sse</span><span style="color:#323232;">(cli, </span><span style="color:#183691;">&quot;/events&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">sse.on_message([](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::sse::SSEMessage </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">msg) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg.event </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot;: &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> msg.data </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">sse.start(); </span><span style="font-style:italic;color:#969896;">// ブロッキング、自動再接続あり
</span></pre>
</div></div>
<p>イベントタイプごとにハンドラーを分けることもできますよ。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">sse.</span><span style="color:#6699cc;">on_event</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">update</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const</span><span style="color:#d3d0c8;"> httplib::sse::SSEMessage &amp;msg) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// &quot;update&quot; イベントだけ処理
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">sse.on_event(</span><span style="color:#183691;">&quot;update&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const</span><span style="color:#323232;"> httplib::sse::SSEMessage </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">msg) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// &quot;update&quot; イベントだけ処理
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>認証</h2>
<p>クライアントにはBasic認証、Bearer Token認証、Digest認証のヘルパーを用意しています。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://api.example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_basic_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_bearer_token_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">my-token</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://api.example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_basic_auth(</span><span style="color:#183691;">&quot;user&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;password&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_bearer_token_auth(</span><span style="color:#183691;">&quot;my-token&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>圧縮</h2>
<p>gzip、Brotli、Zstandardによる圧縮・展開に対応しています。使いたい方式のマクロを定義してコンパイルしましょう。</p>
<table><thead><tr><th>圧縮方式</th><th>マクロ定義</th></tr></thead><tbody>
<tr><td>gzip</td><td><code>CPPHTTPLIB_ZLIB_SUPPORT</code></td></tr>
<tr><td>Brotli</td><td><code>CPPHTTPLIB_BROTLI_SUPPORT</code></td></tr>
<tr><td>Zstandard</td><td><code>CPPHTTPLIB_ZSTD_SUPPORT</code></td></tr>
</tbody></table>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_compress</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// リクエストボディを圧縮
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_decompress</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// レスポンスボディを展開
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_compress(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// リクエストボディを圧縮
</span><span style="color:#323232;">cli.set_decompress(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// レスポンスボディを展開
</span></pre>
</div></div>
<h2>プロキシ</h2>
<p>HTTPプロキシ経由で接続できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_proxy</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">proxy.example.com</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">8080</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_proxy_basic_auth</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">user</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_proxy(</span><span style="color:#183691;">&quot;proxy.example.com&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8080</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_proxy_basic_auth(</span><span style="color:#183691;">&quot;user&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;password&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>タイムアウト</h2>
<p>接続・読み取り・書き込みのタイムアウトを個別に設定できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_connection_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">5</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 5秒
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_read_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">10</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 10秒
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_write_timeout</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">10</span><span style="color:#d3d0c8;">, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">); </span><span style="color:#747369;">// 10秒
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_connection_timeout(</span><span style="color:#0086b3;">5</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 5秒
</span><span style="color:#323232;">cli.set_read_timeout(</span><span style="color:#0086b3;">10</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 10秒
</span><span style="color:#323232;">cli.set_write_timeout(</span><span style="color:#0086b3;">10</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">); </span><span style="font-style:italic;color:#969896;">// 10秒
</span></pre>
</div></div>
<h2>Keep-Alive</h2>
<p>同じサーバーに何度もリクエストするなら、Keep-Aliveを有効にしましょう。TCP接続を再利用するので効率的です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">https://example.com</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_keep_alive</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;https://example.com&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_keep_alive(</span><span style="color:#0086b3;">true</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>サーバーのミドルウェア</h2>
<p>リクエスト処理の前後にフックを挟めます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_pre_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// すべてのリクエストの前に実行される
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;"> httplib::Server::HandlerResponse::Unhandled; </span><span style="color:#747369;">// 通常のルーティングに進む
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_post_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#747369;">// レスポンスが返された後に実行される
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_header</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">X-Server</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">cpp-httplib</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_pre_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// すべてのリクエストの前に実行される
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;"> httplib::Server::HandlerResponse::Unhandled; </span><span style="font-style:italic;color:#969896;">// 通常のルーティングに進む
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.set_post_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-style:italic;color:#969896;">// レスポンスが返された後に実行される
</span><span style="color:#323232;"> res.set_header(</span><span style="color:#183691;">&quot;X-Server&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;cpp-httplib&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p><code>req.user_data</code> を使うと、ミドルウェアからハンドラーにデータを渡せます。認証トークンのデコード結果を共有するときに便利です。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_pre_routing_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> req.</span><span style="color:#f2777a;">user_data</span><span style="color:#d3d0c8;">[&quot;</span><span style="color:#99cc99;">auth_user</span><span style="color:#d3d0c8;">&quot;] = </span><span style="color:#6699cc;">std::string</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">alice</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">return</span><span style="color:#d3d0c8;"> httplib::Server::HandlerResponse::Unhandled;
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/me</span><span style="color:#d3d0c8;">&quot;, [](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> </span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> user = std::</span><span style="color:#6699cc;">any_cast</span><span style="color:#d3d0c8;">&lt;std::string&gt;(req.</span><span style="color:#f2777a;">user_data</span><span style="color:#d3d0c8;">.</span><span style="color:#6699cc;">at</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">auth_user</span><span style="color:#d3d0c8;">&quot;));
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Hello, </span><span style="color:#d3d0c8;">&quot; + user, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_pre_routing_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> req.user_data[</span><span style="color:#183691;">&quot;auth_user&quot;</span><span style="color:#323232;">] </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">std::string(</span><span style="color:#183691;">&quot;alice&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">return</span><span style="color:#323232;"> httplib::Server::HandlerResponse::Unhandled;
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.Get(</span><span style="color:#183691;">&quot;/me&quot;</span><span style="color:#323232;">, [](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> </span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> user </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">std::any_cast&lt;std::string&gt;(req.user_data.at(</span><span style="color:#183691;">&quot;auth_user&quot;</span><span style="color:#323232;">));
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Hello, &quot; </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> user, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<p>エラーや例外のハンドラーもカスタマイズできますよ。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_error_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Custom Error Page</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/html</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_exception_handler</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">auto </span><span style="color:#d3d0c8;">&amp;res, std::exception_ptr ep) {
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">= </span><span style="color:#f99157;">500</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;"> res.</span><span style="color:#6699cc;">set_content</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">Internal Server Error</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">text/plain</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_error_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Custom Error Page&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/html&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span><span style="color:#323232;">
</span><span style="color:#323232;">svr.set_exception_handler([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">auto &amp;</span><span style="color:#323232;">res, std::exception_ptr ep) {
</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">500</span><span style="color:#323232;">;
</span><span style="color:#323232;"> res.set_content(</span><span style="color:#183691;">&quot;Internal Server Error&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;text/plain&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>ロギング</h2>
<p>サーバーでもクライアントでもロガーを設定できます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_logger</span><span style="color:#d3d0c8;">([](</span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;req, </span><span style="color:#cc99cc;">const auto </span><span style="color:#d3d0c8;">&amp;res) {
</span><span style="color:#d3d0c8;"> std::cout &lt;&lt; req.</span><span style="color:#f2777a;">method </span><span style="color:#d3d0c8;">&lt;&lt; &quot; &quot; &lt;&lt; req.</span><span style="color:#f2777a;">path </span><span style="color:#d3d0c8;">&lt;&lt; &quot; &quot; &lt;&lt; res.</span><span style="color:#f2777a;">status </span><span style="color:#d3d0c8;">&lt;&lt; std::endl;
</span><span style="color:#d3d0c8;">});
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="color:#323232;">svr.set_logger([](</span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">req, </span><span style="font-weight:bold;color:#a71d5d;">const auto &amp;</span><span style="color:#323232;">res) {
</span><span style="color:#323232;"> std::cout </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> req.method </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot; &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> req.path </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt; </span><span style="color:#183691;">&quot; &quot; </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> res.status </span><span style="font-weight:bold;color:#a71d5d;">&lt;&lt;</span><span style="color:#323232;"> std::endl;
</span><span style="color:#323232;">});
</span></pre>
</div></div>
<h2>Unix Domain Socket</h2>
<p>TCP以外に、Unix Domain Socketでの通信にも対応しています。同じマシン上のプロセス間通信に使えます。</p>
<div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// サーバー
</span><span style="color:#d3d0c8;">httplib::Server svr;
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">set_address_family</span><span style="color:#d3d0c8;">(AF_UNIX);
</span><span style="color:#d3d0c8;">svr.</span><span style="color:#6699cc;">listen</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/tmp/httplib.sock</span><span style="color:#d3d0c8;">&quot;, </span><span style="color:#f99157;">0</span><span style="color:#d3d0c8;">);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// サーバー
</span><span style="color:#323232;">httplib::Server svr;
</span><span style="color:#323232;">svr.set_address_family(AF_UNIX);
</span><span style="color:#323232;">svr.listen(</span><span style="color:#183691;">&quot;/tmp/httplib.sock&quot;</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0</span><span style="color:#323232;">);
</span></pre>
</div></div><div class="code-block-wrapper"><div data-code-theme="dark"><pre style="background-color:#2d2d2d;">
<span style="color:#747369;">// クライアント
</span><span style="color:#d3d0c8;">httplib::Client </span><span style="color:#6699cc;">cli</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">http://localhost</span><span style="color:#d3d0c8;">&quot;);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_address_family</span><span style="color:#d3d0c8;">(AF_UNIX);
</span><span style="color:#d3d0c8;">cli.</span><span style="color:#6699cc;">set_hostname_addr_map</span><span style="color:#d3d0c8;">({{&quot;</span><span style="color:#99cc99;">localhost</span><span style="color:#d3d0c8;">&quot;, &quot;</span><span style="color:#99cc99;">/tmp/httplib.sock</span><span style="color:#d3d0c8;">&quot;}});
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">auto</span><span style="color:#d3d0c8;"> res = cli.</span><span style="color:#6699cc;">Get</span><span style="color:#d3d0c8;">(&quot;</span><span style="color:#99cc99;">/</span><span style="color:#d3d0c8;">&quot;);
</span></pre>
</div><div data-code-theme="light"><pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// クライアント
</span><span style="color:#323232;">httplib::Client </span><span style="font-weight:bold;color:#795da3;">cli</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;http://localhost&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">cli.set_address_family(AF_UNIX);
</span><span style="color:#323232;">cli.set_hostname_addr_map({{</span><span style="color:#183691;">&quot;localhost&quot;</span><span style="color:#323232;">, </span><span style="color:#183691;">&quot;/tmp/httplib.sock&quot;</span><span style="color:#323232;">}});
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">auto</span><span style="color:#323232;"> res </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> cli.Get(</span><span style="color:#183691;">&quot;/&quot;</span><span style="color:#323232;">);
</span></pre>
</div></div>
<h2>さらに詳しく</h2>
<p>もっと詳しく知りたいときは、以下を参照してください。</p>
<ul>
<li>Cookbook — よくあるユースケースのレシピ集</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README.md">README</a> — 全APIのリファレンス</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-sse.md">README-sse</a> — Server-Sent Eventsの使い方</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-stream.md">README-stream</a> — Streaming APIの使い方</li>
<li><a href="https://github.com/yhirose/cpp-httplib/blob/master/README-websocket.md">README-websocket</a> — WebSocketサーバーの使い方</li>
</ul>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="ja" data-base-path="&#x2F;cpp-httplib">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Tour of cpp-httplib - cpp-httplib</title>
<link rel="icon" type="image/svg+xml" href="&#x2F;cpp-httplib/favicon.svg">
<link rel="stylesheet" href="&#x2F;cpp-httplib/css/main.css">
<script>
(function() {
var t = localStorage.getItem('preferred-theme');
if (!t) t = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
if (t === 'light') document.documentElement.setAttribute('data-theme', 'light');
})();
</script>
</head>
<body>
<header class="header">
<div class="header-inner">
<a href="&#x2F;cpp-httplib/ja/" class="header-title">cpp-httplib <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v0.35.0</span></a>
<div class="header-spacer"></div>
<nav class="header-nav">
<a href="&#x2F;cpp-httplib/ja/">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Home
</a>
<a href="&#x2F;cpp-httplib/ja/tour&#x2F;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>
Tour
</a>
<a href="https:&#x2F;&#x2F;github.com&#x2F;yhirose&#x2F;cpp-httplib" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
</nav>
<div class="header-tools">
<button class="search-btn" aria-label="Search (⌘K)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
</button>
<button class="theme-toggle" aria-label="Toggle theme"></button>
<div class="lang-selector">
<button class="lang-btn" aria-label="Language">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
JA
</button>
<ul class="lang-popup">
<li><a href="#" data-lang="en">EN</a></li>
<li><a href="#" data-lang="ja">JA</a></li>
</ul>
</div>
</div>
<button class="sidebar-toggle" aria-label="Menu">&#9776;</button>
</div>
</header>
<div class="layout has-sidebar">
<aside class="sidebar">
<nav class="sidebar-nav">
<div class="nav-section">
<a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;" class="nav-section-title active">A Tour of cpp-httplib</a>
<ul class="nav-list">
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;01-getting-started&#x2F;" class="">Getting Started</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;02-basic-client&#x2F;" class="">Basic Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;03-basic-server&#x2F;" class="">Basic Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;04-static-file-server&#x2F;" class="">Static File Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;05-tls-setup&#x2F;" class="">TLS Setup</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;06-https-client&#x2F;" class="">HTTPS Client</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;07-https-server&#x2F;" class="">HTTPS Server</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;08-websocket&#x2F;" class="">WebSocket</a></li>
<li><a href="&#x2F;cpp-httplib&#x2F;ja&#x2F;tour&#x2F;09-whats-next&#x2F;" class="">What&#x27;s Next</a></li>
</ul>
</div>
</nav>
</aside>
<main class="content">
<article>
<h1>A Tour of cpp-httplib</h1>
<p>cpp-httplibの基本を、順番に学んでいくチュートリアルです。各章は前の章の内容を踏まえて進む構成なので、1章から順に読んでください。</p>
<ol>
<li><a href="01-getting-started">Getting Started</a> — httplib.h の入手とHello Worldサーバー</li>
<li><a href="02-basic-client">Basic Client</a> — GET/POST・パスパラメーターのリクエスト送信</li>
<li><a href="03-basic-server">Basic Server</a> — ルーティング、パスパラメーター、レスポンスの組み立て</li>
<li><a href="04-static-file-server">Static File Server</a> — 静的ファイルの配信</li>
<li><a href="05-tls-setup">TLS Setup</a> — OpenSSL / mbedTLS のセットアップ</li>
<li><a href="06-https-client">HTTPS Client</a> — HTTPSサイトへのリクエスト</li>
<li><a href="07-https-server">HTTPS Server</a> — HTTPSサーバーの構築</li>
<li><a href="08-websocket">WebSocket</a> — WebSocket通信の基本</li>
<li><a href="09-whats-next">What's Next</a> — さらなる機能の紹介</li>
</ol>
</article>
</main>
</div>
<footer class="footer">
&copy; 2026 yhirose. All rights reserved.
</footer>
<!-- Search modal -->
<div class="search-overlay" id="search-overlay">
<div class="search-modal">
<div class="search-input-wrap">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" placeholder="Search..." autocomplete="off" spellcheck="false">
<kbd class="search-esc">ESC</kbd>
</div>
<ul class="search-results" id="search-results"></ul>
</div>
</div>
<script src="&#x2F;cpp-httplib/js/main.js"></script>
</body>
</html>
+297
View File
@@ -0,0 +1,297 @@
// Language selector
(function () {
var btn = document.querySelector('.lang-btn');
var popup = document.querySelector('.lang-popup');
if (!btn || !popup) return;
btn.addEventListener('click', function (e) {
e.stopPropagation();
popup.classList.toggle('open');
});
document.addEventListener('click', function () {
popup.classList.remove('open');
});
popup.addEventListener('click', function (e) {
var link = e.target.closest('[data-lang]');
if (!link) return;
e.preventDefault();
var lang = link.getAttribute('data-lang');
localStorage.setItem('preferred-lang', lang);
var basePath = document.documentElement.getAttribute('data-base-path') || '';
var path = window.location.pathname;
// Strip base path prefix, replace lang, then re-add base path
var pathWithoutBase = path.slice(basePath.length);
var newPath = basePath + pathWithoutBase.replace(/^\/[a-z]{2}\//, '/' + lang + '/');
window.location.href = newPath;
});
})();
// Theme toggle
(function () {
var btn = document.querySelector('.theme-toggle');
if (!btn) return;
// Feather Icons: sun (light mode) and moon (dark mode)
var sunSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
var moonSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
function getTheme() {
var stored = localStorage.getItem('preferred-theme');
if (stored) return stored;
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
}
function applyTheme(theme) {
if (theme === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
} else {
document.documentElement.removeAttribute('data-theme');
}
btn.innerHTML = theme === 'light' ? sunSVG : moonSVG;
}
applyTheme(getTheme());
btn.addEventListener('click', function () {
var current = getTheme();
var next = current === 'dark' ? 'light' : 'dark';
localStorage.setItem('preferred-theme', next);
applyTheme(next);
});
})();
// Mobile sidebar toggle
(function () {
var toggle = document.querySelector('.sidebar-toggle');
var sidebar = document.querySelector('.sidebar');
if (!toggle || !sidebar) return;
toggle.addEventListener('click', function () {
sidebar.classList.toggle('open');
});
document.addEventListener('click', function (e) {
if (!sidebar.contains(e.target) && e.target !== toggle) {
sidebar.classList.remove('open');
}
});
})();
// Site search (⌘K / Ctrl+K)
(function () {
var overlay = document.getElementById('search-overlay');
var input = document.getElementById('search-input');
var resultsList = document.getElementById('search-results');
if (!overlay || !input || !resultsList) return;
var searchBtn = document.querySelector('.search-btn');
var pagesData = null; // cached pages-data.json
var activeIndex = -1;
function getCurrentLang() {
return document.documentElement.getAttribute('lang') || 'en';
}
function getBasePath() {
return document.documentElement.getAttribute('data-base-path') || '';
}
function openSearch() {
overlay.classList.add('open');
input.value = '';
resultsList.innerHTML = '';
activeIndex = -1;
input.focus();
loadPagesData();
}
function closeSearch() {
overlay.classList.remove('open');
input.value = '';
resultsList.innerHTML = '';
activeIndex = -1;
}
function loadPagesData() {
if (pagesData) return;
var basePath = getBasePath();
fetch(basePath + '/pages-data.json')
.then(function (res) { return res.json(); })
.then(function (data) { pagesData = data; })
.catch(function () { pagesData = []; });
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlightText(text, query) {
if (!query) return text;
var escaped = escapeRegExp(query);
var re = new RegExp('(' + escaped + ')', 'gi');
return text.replace(re, '<mark>$1</mark>');
}
function buildSnippet(body, query) {
if (!query || !body) return '';
var lower = body.toLowerCase();
var idx = lower.indexOf(query.toLowerCase());
var start, end, snippet;
if (idx === -1) {
snippet = body.substring(0, 120);
} else {
start = Math.max(0, idx - 40);
end = Math.min(body.length, idx + query.length + 80);
snippet = (start > 0 ? '...' : '') + body.substring(start, end) + (end < body.length ? '...' : '');
}
return highlightText(snippet, query);
}
function search(query) {
if (!pagesData || !query) {
resultsList.innerHTML = '';
activeIndex = -1;
return;
}
var lang = getCurrentLang();
var q = query.toLowerCase();
// Score and filter
var scored = [];
for (var i = 0; i < pagesData.length; i++) {
var page = pagesData[i];
if (page.lang !== lang) continue;
var score = 0;
var titleLower = page.title.toLowerCase();
var bodyLower = (page.body || '').toLowerCase();
if (titleLower.indexOf(q) !== -1) {
score += 10;
// Bonus for exact title match
if (titleLower === q) score += 5;
}
if (bodyLower.indexOf(q) !== -1) {
score += 3;
}
if (page.section.toLowerCase().indexOf(q) !== -1) {
score += 1;
}
if (score > 0) {
scored.push({ page: page, score: score });
}
}
// Sort by score descending
scored.sort(function (a, b) { return b.score - a.score; });
// Limit results
var results = scored.slice(0, 20);
if (results.length === 0) {
resultsList.innerHTML = '<li class="search-no-results">No results found.</li>';
activeIndex = -1;
return;
}
var html = '';
for (var j = 0; j < results.length; j++) {
var r = results[j];
var snippet = buildSnippet(r.page.body, query);
html += '<li data-url="' + r.page.url + '">'
+ '<div class="search-result-title">' + highlightText(r.page.title, query) + '</div>'
+ (snippet ? '<div class="search-result-snippet">' + snippet + '</div>' : '')
+ '</li>';
}
resultsList.innerHTML = html;
activeIndex = -1;
}
function setActive(index) {
var items = resultsList.querySelectorAll('li[data-url]');
if (items.length === 0) return;
// Remove previous active
for (var i = 0; i < items.length; i++) {
items[i].classList.remove('active');
}
if (index < 0) index = items.length - 1;
if (index >= items.length) index = 0;
activeIndex = index;
items[activeIndex].classList.add('active');
items[activeIndex].scrollIntoView({ block: 'nearest' });
}
function navigateToActive() {
var items = resultsList.querySelectorAll('li[data-url]');
if (activeIndex >= 0 && activeIndex < items.length) {
var url = items[activeIndex].getAttribute('data-url');
if (url) {
closeSearch();
window.location.href = url;
}
}
}
// Event: search button
if (searchBtn) {
searchBtn.addEventListener('click', function (e) {
e.stopPropagation();
openSearch();
});
}
// Use capture phase to intercept keys before browser default behavior
// (e.g. ESC clearing input text in some browsers)
document.addEventListener('keydown', function (e) {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
overlay.classList.contains('open') ? closeSearch() : openSearch();
return;
}
if (!overlay.classList.contains('open')) return;
if (e.key === 'Escape') {
e.preventDefault();
closeSearch();
} else if (e.key === 'ArrowDown') {
e.preventDefault();
setActive(activeIndex + 1);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setActive(activeIndex - 1);
} else if (e.key === 'Enter') {
e.preventDefault();
navigateToActive();
}
}, true); // capture phase
// Event: click overlay background to close
overlay.addEventListener('click', function (e) {
if (e.target === overlay) {
closeSearch();
}
});
// Event: click result item
resultsList.addEventListener('click', function (e) {
var li = e.target.closest('li[data-url]');
if (!li) return;
var url = li.getAttribute('data-url');
if (url) {
closeSearch();
window.location.href = url;
}
});
// Event: input for live search
var debounceTimer = null;
input.addEventListener('input', function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function () {
search(input.value.trim());
}, 150);
});
})();
File diff suppressed because one or more lines are too long
+5 -2
View File
@@ -18,7 +18,7 @@ ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = $(PREFIX)/opt/brotli
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client accept_header
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli wsecho benchmark one_time_request server_and_client accept_header
server : server.cc ../httplib.h Makefile
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
@@ -47,6 +47,9 @@ ssesvr : ssesvr.cc ../httplib.h Makefile
ssecli : ssecli.cc ../httplib.h Makefile
$(CXX) -o ssecli $(CXXFLAGS) ssecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
wsecho : wsecho.cc ../httplib.h Makefile
$(CXX) -o wsecho $(CXXFLAGS) wsecho.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
benchmark : benchmark.cc ../httplib.h Makefile
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
@@ -64,4 +67,4 @@ pem:
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
clean:
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client accept_header *.pem
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli wsecho benchmark one_time_request server_and_client accept_header *.pem
+1 -1
View File
@@ -21,7 +21,7 @@ struct StopWatch {
int main(void) {
string body(1024 * 5, 'a');
httplib::Client cli("httpbin.org", 80);
httplib::Client cli("httpcan.org", 80);
for (int i = 0; i < 3; i++) {
StopWatch sw(to_string(i).c_str());
+2 -2
View File
@@ -1,7 +1,7 @@
//
// client.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
@@ -30,7 +30,7 @@ int main(void) {
} else {
cout << "error code: " << res.error() << std::endl;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto result = cli.get_openssl_verify_result();
auto result = cli.get_verify_result();
if (result) {
cout << "verify error: " << X509_verify_cert_error_string(result) << endl;
}
+1 -1
View File
@@ -1,7 +1,7 @@
//
// hello.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
+1 -1
View File
@@ -1,7 +1,7 @@
//
// redirect.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
+1 -1
View File
@@ -1,7 +1,7 @@
//
// sample.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
+1 -1
View File
@@ -1,7 +1,7 @@
//
// server_and_client.cc
//
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
+1 -1
View File
@@ -1,7 +1,7 @@
//
// simplecli.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//
+1 -1
View File
@@ -1,7 +1,7 @@
//
// simplesvr.cc
//
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
// Copyright (c) 2026 Yuji Hirose. All rights reserved.
// MIT License
//

Some files were not shown because too many files have changed in this diff Show More