mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-21 11:39:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe332fa06b | ||
|
|
fa981cedae | ||
|
|
39ec7d0508 | ||
|
|
e7e7bf7b44 | ||
|
|
78ff94e604 | ||
|
|
018ce8e4ca | ||
|
|
77bdf7921a | ||
|
|
79d83feb18 | ||
|
|
fe56a07da5 | ||
|
|
907257f51d | ||
|
|
0c2f535b74 | ||
|
|
c7ba963a17 | ||
|
|
4465e81b9f | ||
|
|
44215e23e9 | ||
|
|
91219d4508 | ||
|
|
c86c192f3e | ||
|
|
008e107d0f | ||
|
|
d278f965cc | ||
|
|
4c4b62dd7e | ||
|
|
b1792ef29c | ||
|
|
0f3d063f0a | ||
|
|
0d7d637466 | ||
|
|
1ff0c8588d | ||
|
|
b1cc8095a8 |
No files matched your search
@@ -120,6 +120,155 @@ jobs:
|
|||||||
- name: build and run ThreadPool test
|
- name: build and run ThreadPool test
|
||||||
run: cd test && make test_thread_pool && ./test_thread_pool
|
run: cd test && make test_thread_pool && ./test_thread_pool
|
||||||
|
|
||||||
|
# BoringSSL is Google's fork of OpenSSL. It has no API stability guarantee
|
||||||
|
# and is not packaged by distros, so we build it from source. cpp-httplib
|
||||||
|
# treats it as an OpenSSL backend variant via the OPENSSL_IS_BORINGSSL
|
||||||
|
# macro (see httplib.h). This job is best-effort: continue-on-error keeps
|
||||||
|
# upstream API drift from blocking PRs while still surfacing breakage.
|
||||||
|
ubuntu-boringssl:
|
||||||
|
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')
|
||||||
|
continue-on-error: true
|
||||||
|
name: ubuntu (boringssl, best-effort)
|
||||||
|
env:
|
||||||
|
# Tracking HEAD keeps us honest about upstream churn. If breakage
|
||||||
|
# becomes routine, replace HEAD with a 40-char commit SHA; the
|
||||||
|
# resolve step uses the SHA directly when it matches that shape.
|
||||||
|
BORINGSSL_REF: HEAD
|
||||||
|
BORINGSSL_PREFIX: ${{ github.workspace }}/boringssl-install
|
||||||
|
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: resolve BoringSSL commit
|
||||||
|
id: boringssl-rev
|
||||||
|
# Accept either a ref name (resolved via git ls-remote) or a full
|
||||||
|
# 40-char SHA used directly. ls-remote does not list arbitrary
|
||||||
|
# commit SHAs, so pinning requires the second path.
|
||||||
|
run: |
|
||||||
|
if [[ "${BORINGSSL_REF}" =~ ^[0-9a-f]{40}$ ]]; then
|
||||||
|
sha="${BORINGSSL_REF}"
|
||||||
|
echo "Using pinned BoringSSL SHA: ${sha}"
|
||||||
|
else
|
||||||
|
sha=$(git ls-remote https://boringssl.googlesource.com/boringssl "${BORINGSSL_REF}" | awk '{print $1}')
|
||||||
|
if [ -z "$sha" ]; then
|
||||||
|
echo "Failed to resolve BoringSSL ref ${BORINGSSL_REF}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Resolved ${BORINGSSL_REF} -> ${sha}"
|
||||||
|
fi
|
||||||
|
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: cache BoringSSL build
|
||||||
|
id: boringssl-cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.BORINGSSL_PREFIX }}
|
||||||
|
key: boringssl-${{ runner.os }}-${{ steps.boringssl-rev.outputs.sha }}
|
||||||
|
- name: build BoringSSL
|
||||||
|
if: steps.boringssl-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
git clone https://boringssl.googlesource.com/boringssl boringssl
|
||||||
|
cd boringssl
|
||||||
|
git checkout "${{ steps.boringssl-rev.outputs.sha }}"
|
||||||
|
cmake -S . -B build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${BORINGSSL_PREFIX}"
|
||||||
|
cmake --build build -j"$(nproc)" --target install
|
||||||
|
- name: build and run tests (BoringSSL)
|
||||||
|
# Override OPENSSL_SUPPORT to point the existing OpenSSL Makefile path
|
||||||
|
# at BoringSSL's prefix. BoringSSL defines OPENSSL_IS_BORINGSSL in
|
||||||
|
# <openssl/base.h>, which httplib.h and test.cc use to switch on API
|
||||||
|
# differences (e.g. SAN-only hostname verification, no CN fallback).
|
||||||
|
#
|
||||||
|
# BoringSSL's public headers (<openssl/stack.h>) use std::enable_if_t,
|
||||||
|
# so consumers must compile with C++14 or later. cpp-httplib itself
|
||||||
|
# supports C++11, but anyone pairing it with BoringSSL inherits this
|
||||||
|
# constraint. EXTRA_CXXFLAGS appends after the Makefile's -std=c++11
|
||||||
|
# and the later flag wins.
|
||||||
|
run: |
|
||||||
|
cd test
|
||||||
|
BORINGSSL_FLAGS="-DCPPHTTPLIB_OPENSSL_SUPPORT -I${BORINGSSL_PREFIX}/include -L${BORINGSSL_PREFIX}/lib -lssl -lcrypto -lpthread"
|
||||||
|
make test_split OPENSSL_SUPPORT="${BORINGSSL_FLAGS}" EXTRA_CXXFLAGS="-std=c++17"
|
||||||
|
make test_openssl_parallel OPENSSL_SUPPORT="${BORINGSSL_FLAGS}" EXTRA_CXXFLAGS="-std=c++17"
|
||||||
|
env:
|
||||||
|
LSAN_OPTIONS: suppressions=lsan_suppressions.txt
|
||||||
|
|
||||||
|
# macOS counterpart of the BoringSSL job. Same best-effort posture; the
|
||||||
|
# extra framework links cover the macOS Keychain integration that
|
||||||
|
# httplib.h auto-enables for any TLS backend on macOS.
|
||||||
|
macos-boringssl:
|
||||||
|
runs-on: macos-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_macos == 'true')
|
||||||
|
continue-on-error: true
|
||||||
|
name: macos (boringssl, best-effort)
|
||||||
|
env:
|
||||||
|
BORINGSSL_REF: HEAD
|
||||||
|
BORINGSSL_PREFIX: ${{ github.workspace }}/boringssl-install
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: resolve BoringSSL commit
|
||||||
|
id: boringssl-rev
|
||||||
|
# Accept either a ref name (resolved via git ls-remote) or a full
|
||||||
|
# 40-char SHA used directly. ls-remote does not list arbitrary
|
||||||
|
# commit SHAs, so pinning requires the second path.
|
||||||
|
run: |
|
||||||
|
if [[ "${BORINGSSL_REF}" =~ ^[0-9a-f]{40}$ ]]; then
|
||||||
|
sha="${BORINGSSL_REF}"
|
||||||
|
echo "Using pinned BoringSSL SHA: ${sha}"
|
||||||
|
else
|
||||||
|
sha=$(git ls-remote https://boringssl.googlesource.com/boringssl "${BORINGSSL_REF}" | awk '{print $1}')
|
||||||
|
if [ -z "$sha" ]; then
|
||||||
|
echo "Failed to resolve BoringSSL ref ${BORINGSSL_REF}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Resolved ${BORINGSSL_REF} -> ${sha}"
|
||||||
|
fi
|
||||||
|
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: cache BoringSSL build
|
||||||
|
id: boringssl-cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.BORINGSSL_PREFIX }}
|
||||||
|
key: boringssl-${{ runner.os }}-${{ steps.boringssl-rev.outputs.sha }}
|
||||||
|
- name: build BoringSSL
|
||||||
|
if: steps.boringssl-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
git clone https://boringssl.googlesource.com/boringssl boringssl
|
||||||
|
cd boringssl
|
||||||
|
git checkout "${{ steps.boringssl-rev.outputs.sha }}"
|
||||||
|
cmake -S . -B build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${BORINGSSL_PREFIX}"
|
||||||
|
cmake --build build -j"$(sysctl -n hw.ncpu)" --target install
|
||||||
|
- name: build and run tests (BoringSSL)
|
||||||
|
run: |
|
||||||
|
cd test
|
||||||
|
# CoreFoundation/Security frameworks satisfy the Keychain integration
|
||||||
|
# auto-enabled in httplib.h for macOS TLS builds.
|
||||||
|
BORINGSSL_FLAGS="-DCPPHTTPLIB_OPENSSL_SUPPORT -I${BORINGSSL_PREFIX}/include -L${BORINGSSL_PREFIX}/lib -lssl -lcrypto -framework CoreFoundation -framework Security"
|
||||||
|
make test_split OPENSSL_SUPPORT="${BORINGSSL_FLAGS}" EXTRA_CXXFLAGS="-std=c++17"
|
||||||
|
make test_openssl_parallel OPENSSL_SUPPORT="${BORINGSSL_FLAGS}" EXTRA_CXXFLAGS="-std=c++17"
|
||||||
|
env:
|
||||||
|
LSAN_OPTIONS: suppressions=lsan_suppressions.txt
|
||||||
|
|
||||||
# Reproducer for https://github.com/yhirose/cpp-httplib/issues/2431.
|
# Reproducer for https://github.com/yhirose/cpp-httplib/issues/2431.
|
||||||
# On Linux/glibc, getaddrinfo_with_timeout() schedules an asynchronous
|
# On Linux/glibc, getaddrinfo_with_timeout() schedules an asynchronous
|
||||||
# DNS lookup with getaddrinfo_a(GAI_NOWAIT) using a stack-local gaicb.
|
# DNS lookup with getaddrinfo_a(GAI_NOWAIT) using a stack-local gaicb.
|
||||||
@@ -250,6 +399,54 @@ jobs:
|
|||||||
- name: build and run ThreadPool test
|
- name: build and run ThreadPool test
|
||||||
run: cd test && make test_thread_pool && ./test_thread_pool
|
run: cd test && make test_thread_pool && ./test_thread_pool
|
||||||
|
|
||||||
|
ios-parse-check:
|
||||||
|
runs-on: macos-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_macos == 'true')
|
||||||
|
name: ios header parse check (not officially supported)
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: install OpenSSL headers
|
||||||
|
run: brew install openssl@3
|
||||||
|
- name: verify header parses on iOS target
|
||||||
|
run: |
|
||||||
|
IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
|
||||||
|
OPENSSL_INC=$(brew --prefix openssl@3)/include
|
||||||
|
echo "Using iOS SDK: $IOS_SDK"
|
||||||
|
echo '#include "httplib.h"' | clang++ \
|
||||||
|
-isysroot "$IOS_SDK" \
|
||||||
|
-target arm64-apple-ios16.0 \
|
||||||
|
-std=c++11 \
|
||||||
|
-DCPPHTTPLIB_OPENSSL_SUPPORT \
|
||||||
|
-I"$OPENSSL_INC" \
|
||||||
|
-I. -Wall -Wextra \
|
||||||
|
-fsyntax-only -x c++ -
|
||||||
|
- name: verify CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is rejected on iOS
|
||||||
|
run: |
|
||||||
|
IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
|
||||||
|
OPENSSL_INC=$(brew --prefix openssl@3)/include
|
||||||
|
out=$(echo '#include "httplib.h"' | clang++ \
|
||||||
|
-isysroot "$IOS_SDK" \
|
||||||
|
-target arm64-apple-ios16.0 \
|
||||||
|
-std=c++11 \
|
||||||
|
-DCPPHTTPLIB_OPENSSL_SUPPORT \
|
||||||
|
-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN \
|
||||||
|
-I"$OPENSSL_INC" \
|
||||||
|
-I. \
|
||||||
|
-fsyntax-only -x c++ - 2>&1 || true)
|
||||||
|
if echo "$out" | grep -q "only supported on macOS"; then
|
||||||
|
echo "OK: #error fired as expected"
|
||||||
|
else
|
||||||
|
echo "FAIL: expected #error did not fire"
|
||||||
|
echo "--- compiler output ---"
|
||||||
|
echo "$out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
if: >
|
if: >
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ cpp-httplib supports multiple TLS backends through an abstraction layer:
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable.
|
> **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> **BoringSSL (best-effort):** BoringSSL builds under `CPPHTTPLIB_OPENSSL_SUPPORT` and is exercised by CI against current upstream. Because BoringSSL does not guarantee API stability, support is best-effort — breakage may occasionally land. Two known behavioral differences vs OpenSSL: (1) BoringSSL's public headers require C++14 or later, so consumers must compile accordingly; (2) hostname verification is SAN-only per RFC 6125 §6.4.4 (no CN fallback).
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
// Use either OpenSSL, Mbed TLS, or wolfSSL
|
// Use either OpenSSL, Mbed TLS, or wolfSSL
|
||||||
#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT
|
#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT
|
||||||
@@ -447,6 +450,8 @@ svr.set_post_routing_handler([](const auto& req, auto& res) {
|
|||||||
|
|
||||||
### Pre request handler
|
### Pre request handler
|
||||||
|
|
||||||
|
The pre-request handler runs after the route has been matched (so `req.matched_route` and `req.path_params` are available) but **before the request body is read**. This means you can reject a request — for example on a failed authentication or authorization check — without forcing the server to buffer a potentially large body.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
svr.set_pre_request_handler([](const auto& req, auto& res) {
|
svr.set_pre_request_handler([](const auto& req, auto& res) {
|
||||||
if (req.matched_route == "/user/:user") {
|
if (req.matched_route == "/user/:user") {
|
||||||
@@ -461,6 +466,38 @@ svr.set_pre_request_handler([](const auto& req, auto& res) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Because the body has not been read yet, `req.body` and form fields parsed from the body are not available in the pre-request handler. Inspect headers, the path, query parameters, or `req.matched_route` instead.
|
||||||
|
|
||||||
|
### Handler execution order
|
||||||
|
|
||||||
|
`set_start_handler` runs once when the server starts. For each request, handlers run in the following order:
|
||||||
|
|
||||||
|
```
|
||||||
|
Request received
|
||||||
|
│
|
||||||
|
├─ pre_routing_handler route not matched yet, body not read
|
||||||
|
│ └─ returns Handled → stop here
|
||||||
|
│
|
||||||
|
├─ file_request_handler (GET/HEAD, static file serving)
|
||||||
|
│
|
||||||
|
├─ expect_100_continue_handler (when the request has "Expect: 100-continue")
|
||||||
|
│
|
||||||
|
├─ route matching → req.matched_route is set
|
||||||
|
│
|
||||||
|
├─ pre_request_handler route matched, body NOT read yet
|
||||||
|
│ └─ returns Handled → stop here (route handler is skipped)
|
||||||
|
│
|
||||||
|
├─ route handler Get/Post/...; the request body is read first
|
||||||
|
│
|
||||||
|
└─ post_routing_handler after routing completes
|
||||||
|
|
||||||
|
On a thrown exception → exception_handler
|
||||||
|
On an error status (4xx/5xx) → error_handler
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `pre_routing_handler` to reject a request as early as possible, before the route is known. Use `pre_request_handler` for route-specific checks, since `req.matched_route` is available and the body has not been read yet.
|
||||||
|
|
||||||
### Response user data
|
### Response user data
|
||||||
|
|
||||||
`res.user_data` is a type-safe key-value store that lets pre-routing or pre-request handlers pass arbitrary data to route handlers.
|
`res.user_data` is a type-safe key-value store that lets pre-routing or pre-request handlers pass arbitrary data to route handlers.
|
||||||
@@ -1178,6 +1215,17 @@ cli.set_proxy_bearer_token_auth("pass");
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> OpenSSL is required for Digest Authentication.
|
> OpenSSL is required for Digest Authentication.
|
||||||
|
|
||||||
|
#### Bypass the proxy for specific hosts (`NO_PROXY`)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", "*.dev.local"});
|
||||||
|
```
|
||||||
|
|
||||||
|
Each pattern is `*`, a hostname suffix, an IP literal, or a CIDR block.
|
||||||
|
Hostname matching is case-insensitive with a dot-boundary rule. See the
|
||||||
|
[NO_PROXY cookbook](https://yhirose.github.io/cpp-httplib/en/cookbook/c16-proxy)
|
||||||
|
for details and for reading the variable from the environment.
|
||||||
|
|
||||||
### Range
|
### Range
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
+3
-2
@@ -7,11 +7,12 @@ CPPHTTPLIB_FLAGS = -DCPPHTTPLIB_THREAD_POOL_COUNT=16
|
|||||||
|
|
||||||
BENCH = bombardier -c 10 -d 5s localhost:8080
|
BENCH = bombardier -c 10 -d 5s localhost:8080
|
||||||
MONITOR = ali http://localhost:8080
|
MONITOR = ali http://localhost:8080
|
||||||
|
WAIT = while ! nc -z localhost 8080 >/dev/null 2>&1; do sleep 0.05; done
|
||||||
|
|
||||||
# cpp-httplib
|
# cpp-httplib
|
||||||
bench: server
|
bench: server
|
||||||
@echo "--------------------\n cpp-httplib latest\n--------------------\n"
|
@echo "--------------------\n cpp-httplib latest\n--------------------\n"
|
||||||
@./server & export PID=$$!; $(BENCH); kill $${PID}
|
@./server & export PID=$$!; $(WAIT); $(BENCH); kill $${PID}
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
||||||
monitor: server
|
monitor: server
|
||||||
@@ -26,7 +27,7 @@ server : cpp-httplib/main.cpp ../httplib.h
|
|||||||
# crow
|
# crow
|
||||||
bench-crow: server-crow
|
bench-crow: server-crow
|
||||||
@echo "-------------\n Crow v1.3.1\n-------------\n"
|
@echo "-------------\n Crow v1.3.1\n-------------\n"
|
||||||
@./server-crow & export PID=$$!; $(BENCH); kill $${PID}
|
@./server-crow & export PID=$$!; $(WAIT); $(BENCH); kill $${PID}
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
||||||
monitor-crow: server-crow
|
monitor-crow: server-crow
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ if(@HTTPLIB_IS_USING_ZSTD@)
|
|||||||
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
||||||
set(httplib_fd_zstd_required_arg REQUIRED)
|
set(httplib_fd_zstd_required_arg REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
find_package(zstd QUIET)
|
find_package(zstd 1.5.6 CONFIG QUIET)
|
||||||
if(NOT zstd_FOUND)
|
if(NOT zstd_FOUND)
|
||||||
find_package(PkgConfig ${httplib_fd_zstd_quiet_arg} ${httplib_fd_zstd_required_arg})
|
find_package(PkgConfig ${httplib_fd_zstd_quiet_arg} ${httplib_fd_zstd_required_arg})
|
||||||
if(PKG_CONFIG_FOUND)
|
if(PKG_CONFIG_FOUND)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ langs = ["en", "ja"]
|
|||||||
|
|
||||||
[site]
|
[site]
|
||||||
title = "cpp-httplib"
|
title = "cpp-httplib"
|
||||||
version = "0.45.0"
|
version = "0.47.0"
|
||||||
hostname = "https://yhirose.github.io"
|
hostname = "https://yhirose.github.io"
|
||||||
base_path = "/cpp-httplib"
|
base_path = "/cpp-httplib"
|
||||||
footer_message = "© 2026 Yuji Hirose. All rights reserved."
|
footer_message = "© 2026 Yuji Hirose. All rights reserved."
|
||||||
|
|||||||
@@ -49,4 +49,39 @@ cli.set_bearer_token_auth("api-token"); // for the end server
|
|||||||
|
|
||||||
`Proxy-Authorization` is sent to the proxy, `Authorization` to the end server.
|
`Proxy-Authorization` is sent to the proxy, `Authorization` to the end server.
|
||||||
|
|
||||||
> **Note:** cpp-httplib does not read `HTTP_PROXY` or `HTTPS_PROXY` environment variables automatically. If you want to honor them, read them in your application and pass the values to `set_proxy()`.
|
## Bypass the proxy for specific hosts
|
||||||
|
|
||||||
|
You often want internal endpoints to skip the proxy. Configure a bypass list with `set_no_proxy()`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
cli.set_proxy("proxy.internal", 8080);
|
||||||
|
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", "*.dev.local"});
|
||||||
|
```
|
||||||
|
|
||||||
|
Each entry is one of:
|
||||||
|
|
||||||
|
- `*` — bypass the proxy for all hosts
|
||||||
|
- a hostname suffix (e.g. `example.com`) — matches `example.com` itself and any subdomain (`foo.example.com`). A leading dot is permitted but informational; both forms are equivalent.
|
||||||
|
- a single IP literal (e.g. `192.168.1.1`, `::1`)
|
||||||
|
- a CIDR block (e.g. `10.0.0.0/8`, `fe80::/10`)
|
||||||
|
|
||||||
|
Hostname matching is case-insensitive and uses a dot-boundary rule, so an entry of `example.com` does **not** match `evilexample.com`. IP comparisons are normalized through `inet_pton`, so `127.0.0.1` cannot be bypassed via alternate string forms (e.g. `127.000.000.001`). When an entry matches, the `Proxy-Authorization` header is suppressed as well.
|
||||||
|
|
||||||
|
Malformed entries are silently dropped. Port-specific entries such as `example.com:8080` are not supported (cpp-httplib's other host-keyed APIs are also keyed on hostname only).
|
||||||
|
|
||||||
|
## Read proxy settings from the environment
|
||||||
|
|
||||||
|
cpp-httplib doesn't touch `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` on its own — the config API is always explicit, the same way `set_ca_cert_path()` is. If you'd like that behavior, read the variables in your application and feed them to `set_proxy()` and `set_no_proxy()`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (const char *v = std::getenv("no_proxy")) {
|
||||||
|
std::vector<std::string> patterns;
|
||||||
|
std::stringstream ss(v);
|
||||||
|
for (std::string item; std::getline(ss, item, ',');) {
|
||||||
|
if (!item.empty()) { patterns.push_back(item); }
|
||||||
|
}
|
||||||
|
cli.set_no_proxy(patterns);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you also read `HTTP_PROXY` yourself, honor the lowercase `http_proxy` only. The uppercase form is poisoned in CGI/FastCGI environments by the `Proxy:` request header ([CVE-2016-5385 / "httpoxy"](https://httpoxy.org/)). `HTTPS_PROXY` and `NO_PROXY` are safe in either case because their names don't begin with `HTTP_`.
|
||||||
@@ -8,13 +8,15 @@ The `set_pre_routing_handler()` from [S09. Add pre-processing to all routes](s09
|
|||||||
|
|
||||||
## Pre-routing vs. pre-request
|
## Pre-routing vs. pre-request
|
||||||
|
|
||||||
| Hook | When it runs | Route info |
|
| Hook | When it runs | Route info | Request body |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `set_pre_routing_handler` | Before routing | Not available |
|
| `set_pre_routing_handler` | Before routing | Not available | Not read yet |
|
||||||
| `set_pre_request_handler` | After routing, right before the route handler | Available via `req.matched_route` |
|
| `set_pre_request_handler` | After routing, right before the route handler | Available via `req.matched_route` | Not read yet |
|
||||||
|
|
||||||
In a pre-request handler, `req.matched_route` holds the **pattern string** that matched. You can vary behavior based on the route definition itself.
|
In a pre-request handler, `req.matched_route` holds the **pattern string** that matched. You can vary behavior based on the route definition itself.
|
||||||
|
|
||||||
|
Because the body has not been read when the pre-request handler runs, you can reject a request — for example on a failed auth check — without consuming a (potentially large) request body. Note that this also means `req.body` and form fields parsed from the body are not available here; inspect headers, the path, query parameters, or `req.matched_route` instead.
|
||||||
|
|
||||||
## Switch auth per route
|
## Switch auth per route
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -49,4 +49,39 @@ cli.set_bearer_token_auth("api-token"); // エンドサーバー向け
|
|||||||
|
|
||||||
プロキシには`Proxy-Authorization`、エンドサーバーには`Authorization`ヘッダーが送られます。
|
プロキシには`Proxy-Authorization`、エンドサーバーには`Authorization`ヘッダーが送られます。
|
||||||
|
|
||||||
> **Note:** 環境変数の`HTTP_PROXY`や`HTTPS_PROXY`は自動的には読まれません。必要ならアプリケーション側で読み取って`set_proxy()`に渡してください。
|
## 特定のホストだけプロキシをバイパスする
|
||||||
|
|
||||||
|
社内エンドポイントなどはプロキシを経由させたくないことがあります。`set_no_proxy()`で除外リストを指定できます。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
cli.set_proxy("proxy.internal", 8080);
|
||||||
|
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", "*.dev.local"});
|
||||||
|
```
|
||||||
|
|
||||||
|
エントリは次のいずれかです。
|
||||||
|
|
||||||
|
- `*` — すべてのホストでバイパス
|
||||||
|
- ホスト名サフィックス(例: `example.com`)— `example.com`本体と任意のサブドメイン(`foo.example.com`)にマッチ。先頭にドットを付けても同じ意味です(`.example.com`)。
|
||||||
|
- 単一のIPリテラル(例: `192.168.1.1`、`::1`)
|
||||||
|
- CIDRブロック(例: `10.0.0.0/8`、`fe80::/10`)
|
||||||
|
|
||||||
|
ホスト名のマッチは大文字小文字を区別せず、ドット境界でしか一致しません。たとえば`example.com`というエントリは`evilexample.com`にはマッチしません。IPの比較は`inet_pton`で正規化されるので、`127.0.0.1`を`127.000.000.001`のような別表記でバイパスすることはできません。マッチした場合、`Proxy-Authorization`ヘッダーも自動的に外れます。
|
||||||
|
|
||||||
|
不正な書式のエントリは黙って捨てられます。`example.com:8080`のようなポート指定エントリはサポート外です(cpp-httplibの他のホストキーAPIもホスト名のみを扱う設計のため)。
|
||||||
|
|
||||||
|
## 環境変数からプロキシ設定を読み込む
|
||||||
|
|
||||||
|
cpp-httplib本体は`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`を読みません。`set_ca_cert_path()`と同じで、設定APIは常に明示的にしています。環境変数を反映させたい場合は、アプリ側で読んで`set_proxy()`や`set_no_proxy()`に渡してください。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (const char *v = std::getenv("no_proxy")) {
|
||||||
|
std::vector<std::string> patterns;
|
||||||
|
std::stringstream ss(v);
|
||||||
|
for (std::string item; std::getline(ss, item, ',');) {
|
||||||
|
if (!item.empty()) { patterns.push_back(item); }
|
||||||
|
}
|
||||||
|
cli.set_no_proxy(patterns);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`HTTP_PROXY`も自分で読むなら、小文字の`http_proxy`だけを採用してください。大文字の方はCGI/FastCGI環境で`Proxy:`リクエストヘッダーから汚染される可能性があります([CVE-2016-5385 / "httpoxy"](https://httpoxy.org/))。`HTTPS_PROXY`と`NO_PROXY`は名前が`HTTP_`で始まらないので、どちらの大文字小文字でも安全です。
|
||||||
@@ -8,13 +8,15 @@ status: "draft"
|
|||||||
|
|
||||||
## Pre-routingとの違い
|
## Pre-routingとの違い
|
||||||
|
|
||||||
| フック | 呼ばれるタイミング | ルート情報 |
|
| フック | 呼ばれるタイミング | ルート情報 | リクエストボディ |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `set_pre_routing_handler` | ルーティングの前 | 取得できない |
|
| `set_pre_routing_handler` | ルーティングの前 | 取得できない | まだ読まれていない |
|
||||||
| `set_pre_request_handler` | ルーティング後、ルートハンドラの直前 | `req.matched_route`で取得可能 |
|
| `set_pre_request_handler` | ルーティング後、ルートハンドラの直前 | `req.matched_route`で取得可能 | まだ読まれていない |
|
||||||
|
|
||||||
Pre-requestハンドラなら、`req.matched_route`に「マッチしたパターン文字列」が入っているので、ルートに応じて処理を変えられます。
|
Pre-requestハンドラなら、`req.matched_route`に「マッチしたパターン文字列」が入っているので、ルートに応じて処理を変えられます。
|
||||||
|
|
||||||
|
Pre-requestハンドラが呼ばれる時点ではボディがまだ読まれていないので、認証に失敗したリクエストなどを、(巨大かもしれない)ボディを読み込む前に拒否できます。その代わり、`req.body`やボディから解析されるフォームフィールドはこの時点では参照できません。ヘッダ・パス・クエリパラメータ・`req.matched_route`を使って判断してください。
|
||||||
|
|
||||||
## ルートごとに認証を切り替える
|
## ルートごとに認証を切り替える
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
+18
-3
@@ -62,7 +62,7 @@ HEAD_SHORT=$(git rev-parse --short HEAD)
|
|||||||
echo " Latest commit: $HEAD_SHORT"
|
echo " Latest commit: $HEAD_SHORT"
|
||||||
|
|
||||||
# Fetch all workflow runs for the HEAD commit
|
# Fetch all workflow runs for the HEAD commit
|
||||||
RUNS=$(gh run list --commit "$HEAD_SHA" --json name,conclusion,headSha)
|
RUNS=$(gh run list --commit "$HEAD_SHA" --json name,status,conclusion,headSha)
|
||||||
|
|
||||||
NUM_RUNS=$(echo "$RUNS" | jq 'length')
|
NUM_RUNS=$(echo "$RUNS" | jq 'length')
|
||||||
|
|
||||||
@@ -75,8 +75,17 @@ fi
|
|||||||
echo " Found $NUM_RUNS workflow run(s):"
|
echo " Found $NUM_RUNS workflow run(s):"
|
||||||
|
|
||||||
FAILED=0
|
FAILED=0
|
||||||
|
RUNNING=0
|
||||||
ABIDIFF_PASSED=0
|
ABIDIFF_PASSED=0
|
||||||
while IFS=$'\t' read -r name conclusion; do
|
while IFS=$'\t' read -r name status conclusion; do
|
||||||
|
# A run that hasn't completed yet has an empty conclusion; don't treat it
|
||||||
|
# as a failure — the release should wait until CI finishes.
|
||||||
|
if [ "$status" != "completed" ]; then
|
||||||
|
echo " [ .. ] $name (still running)"
|
||||||
|
RUNNING=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$name" == *abidiff* ]] || [[ "$name" == *abi* && "$name" != *stability* ]]; then
|
if [[ "$name" == *abidiff* ]] || [[ "$name" == *abi* && "$name" != *stability* ]]; then
|
||||||
if [ "$conclusion" = "success" ]; then
|
if [ "$conclusion" = "success" ]; then
|
||||||
echo " [ OK ] $name"
|
echo " [ OK ] $name"
|
||||||
@@ -94,7 +103,13 @@ while IFS=$'\t' read -r name conclusion; do
|
|||||||
echo " [FAIL] $name ($conclusion)"
|
echo " [FAIL] $name ($conclusion)"
|
||||||
FAILED=1
|
FAILED=1
|
||||||
fi
|
fi
|
||||||
done < <(echo "$RUNS" | jq -r '.[] | [.name, .conclusion] | @tsv')
|
done < <(echo "$RUNS" | jq -r '.[] | [.name, .status, .conclusion] | @tsv')
|
||||||
|
|
||||||
|
if [ "$RUNNING" -eq 1 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Error: Some CI checks are still running. Wait for them to complete before releasing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$FAILED" -eq 1 ]; then
|
if [ "$FAILED" -eq 1 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
+1461
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user