mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27c0e1186c | |||
| c54c71a3e5 | |||
| 3409c00e6f | |||
| f8ef5fab64 | |||
| 5b397d455d | |||
| f977558a28 | |||
| c2e156e0e0 | |||
| 7aba2938d3 | |||
| d587548250 | |||
| 21f9c51556 | |||
| 985ceba525 | |||
| e62a4b02e5 | |||
| ff34749572 | |||
| e5804d4a50 | |||
| 3956a2b790 | |||
| b33aa52dc2 | |||
| 76230db97f | |||
| a66a013ed7 | |||
| f4b02dfdc1 | |||
| 4cf218643e | |||
| 8f96b69a25 | |||
| d262033ded | |||
| 5745eabe69 | |||
| 5f18642271 | |||
| 88a9278872 | |||
| 9bb3ca8169 | |||
| f2f4728489 | |||
| d1b616286f | |||
| df74526f91 | |||
| 9f7ae0737a | |||
| 1ebb8412c5 | |||
| 7b69999c37 | |||
| c7e959a948 | |||
| ba5884e779 | |||
| cdaa5c48db | |||
| bab5c0e907 | |||
| 016838fd10 | |||
| 75053bf855 | |||
| ae3a6dd2a9 | |||
| 6d963fbe8d | |||
| 88f6245c84 | |||
| 0e7d2f9f93 | |||
| 4e6ded1f36 | |||
| d663588491 | |||
| 439caf5b79 | |||
| c4ba43ca6f | |||
| 20cba2ecd9 | |||
| 0ff2e16d69 | |||
| 51607ec752 | |||
| 7992b14896 | |||
| 7e420aeed3 | |||
| 227d2c2050 | |||
| 93e53c91f7 | |||
| 58cffd3223 | |||
| 8f32271e8c | |||
| c8c1c3d376 | |||
| 9f512acb42 | |||
| c0b461a3b7 | |||
| 74fe5a5029 | |||
| 9d0a9d4e23 | |||
| 5758769ad3 | |||
| e7eadc3605 | |||
| 07c6e58951 | |||
| 87994811a1 | |||
| 42feb7e8be | |||
| 26196b70af | |||
| 93a51979c4 | |||
| ad7edc7b27 | |||
| 27cd4e6ffe | |||
| cae5a8be1c | |||
| 8e10d4e8e7 | |||
| b57f79f438 | |||
| a9cf097951 | |||
| 5c3624e1af | |||
| cba9ef8c0b | |||
| 4f8407a3a7 | |||
| 656e936f49 | |||
| d92c314466 | |||
| b747fb111d | |||
| 7e0a0b2d0c | |||
| 362d064afa | |||
| 1bd88de2e5 | |||
| 0b541ffebc | |||
| 106be19c3e | |||
| 25d72bf881 | |||
| 9d5b5297cc | |||
| 462884bebb | |||
| b1cc24b795 | |||
| f0eb55b327 | |||
| 6dc285b5ca | |||
| 07e614eef7 | |||
| 916b2a8fd3 | |||
| 869f5bb279 | |||
| 3e21338f82 | |||
| 37bb3c6a77 | |||
| d4ab2fa0e6 | |||
| 72d3f4896a | |||
| 5e6f973b99 | |||
| 7ed77b02ad | |||
| 127a64d5a0 | |||
| caa31aafda | |||
| dae318495f | |||
| 305a7abcb9 | |||
| 219d13b718 | |||
| df20c27696 | |||
| a5a62768c0 | |||
| 4001637beb | |||
| 47044c05a8 | |||
| a449d82723 | |||
| fee8e97b4e | |||
| 72d9ed4056 | |||
| 1be1b3a86d | |||
| 9452c0a4b6 | |||
| 307b729549 | |||
| 696239d6e1 | |||
| 6929d90353 | |||
| 348d032029 | |||
| d1d3fcdfd5 | |||
| abf3a67dd0 | |||
| d87abeecf0 | |||
| 4e28e4f741 | |||
| 80a55cedeb | |||
| d05c343602 | |||
| 33f67386fe | |||
| 56d8168dc4 | |||
| 5d87cc0558 | |||
| cb41947eb4 | |||
| 0857eba17b | |||
| 020b0db090 | |||
| bf0760fde4 | |||
| bb8e45383e | |||
| a1df576e4f | |||
| 7fb0254794 | |||
| c82d1e52cc | |||
| 846151b605 | |||
| e44e31dd5b | |||
| f7b9501662 | |||
| e12fe4cbbb | |||
| 49d2e1f135 | |||
| 8191fd8e6c | |||
| d73395e1dc | |||
| 64d001162b | |||
| bb00a23116 | |||
| 63d6e9b91b | |||
| 66eed5681a | |||
| 8ecdb11979 | |||
| 894fcc8e02 | |||
| 7f43f0f3ff | |||
| 87e03dd1ce | |||
| e5cacb465d | |||
| ee8371f753 | |||
| 081723f983 | |||
| b61f36579c | |||
| 33f53aa458 | |||
| 412ab5f063 | |||
| 11e02e901c | |||
| 65a8f4cf44 | |||
| 27d128bbb4 | |||
| 070f9bec58 | |||
| f817032513 | |||
| 17abe221c0 | |||
| 4a7a81e039 | |||
| 37fd4eb643 | |||
| 865b0e4c03 | |||
| b324921c1a | |||
| 63f72caf30 | |||
| 99ac17b90a | |||
| 4b0ed9ee88 | |||
| 20056f6cda | |||
| 3b35279b16 | |||
| 27deb44df5 | |||
| 24a3ef949b | |||
| bc3e098964 | |||
| c247dcdd7b | |||
| 793ae9855e | |||
| 9fa426d51b | |||
| cec6288a99 | |||
| 9639578c2a | |||
| 743ecbd365 | |||
| 084c643973 | |||
| 824e7682e4 | |||
| f9074684dd | |||
| ddff782133 | |||
| 3051152103 | |||
| 06026bb47d | |||
| 226388ae27 | |||
| ea7548b4cc | |||
| c7486ead96 | |||
| 90a291214c | |||
| c111c42a86 | |||
| 6fb5b63018 | |||
| ec56dfa35e | |||
| 943cd51b67 | |||
| 301faa074c | |||
| dc0481e832 | |||
| 4f8fcdbaf7 | |||
| b80aa7fee3 | |||
| c384be02c9 | |||
| d17ac3bb40 | |||
| c7554ccac2 | |||
| 35ef1c7bae | |||
| d87d0672a8 | |||
| 3da42fd1e8 | |||
| 503aa61325 | |||
| e4c276d0c2 | |||
| e07f7691a8 | |||
| 623ab4a96e | |||
| e1efa337a2 | |||
| 549cdf2f7d | |||
| 3c522386e9 | |||
| c202aa9ce9 | |||
| e3e28c6231 | |||
| 4e05368086 | |||
| e1afe74fe2 | |||
| 461acb02f5 | |||
| 415edc237c | |||
| e20ecd2574 | |||
| ab477b5631 | |||
| 0823d5c7f2 | |||
| 1cc6930363 | |||
| 4297500928 | |||
| a58f042614 | |||
| 469c6bc2b6 | |||
| 887074efd2 | |||
| 9c2c15ca45 | |||
| 1b3b098329 | |||
| 6f7075e3aa | |||
| ccbddd8842 | |||
| 879dd261c2 | |||
| 52f5eb5980 | |||
| ea2f69a0d7 | |||
| 9f2064a8ed | |||
| e3750d9ddf | |||
| c1eee3012e | |||
| 6b08babbd2 | |||
| 215b81342e | |||
| 06bfa7e08b | |||
| 3d83cbb872 | |||
| 8a803b30f6 | |||
| 80be649de7 | |||
| 9648f950f5 | |||
| 6b9ffc8bec | |||
| d903053faf | |||
| 676f1b5a26 | |||
| b8dec12f15 | |||
| fc9b223acc | |||
| ba824089d7 | |||
| 1a2faf09e0 | |||
| 5a43bb8149 | |||
| 0104614656 | |||
| 77a77f6d2d | |||
| 089b9daa1c | |||
| ba34ea4ee8 | |||
| 2917b8a005 | |||
| dcf24d45a2 | |||
| 75fdb06696 | |||
| e00ad37580 | |||
| 5cfb70c2b4 | |||
| 2a70c45697 | |||
| c58b00580e | |||
| 7c60e69c33 | |||
| 33e94891ee | |||
| 73e0729f63 | |||
| 21c529229c | |||
| 63643e6386 | |||
| 6cc2edce99 | |||
| d122ff3ca8 | |||
| 14c6d526b4 | |||
| 28e07bca16 | |||
| faa5f1d802 | |||
| 9d3365df54 | |||
| 6ff84d34d1 | |||
| b845425cd0 | |||
| 89519c88e2 | |||
| ff813bf99d | |||
| cf475bcb50 | |||
| bc80d7c789 | |||
| b7566f6961 | |||
| 0542fdb8e4 | |||
| 78c474c744 | |||
| 88411a1f52 | |||
| ae6cf70bc4 | |||
| 68d1281759 | |||
| 0308d60cb2 | |||
| 59f5fdbb33 | |||
| 13184f5f80 | |||
| 8d9a477edb | |||
| 85b4abbf16 | |||
| e42a358da8 | |||
| f008fe4539 | |||
| ddf41d29ef | |||
| 3f88a46c4a | |||
| 242706ea34 | |||
| a9f5f8683f | |||
| 60c2213893 | |||
| eb2d28bca2 | |||
| 86f637a246 | |||
| 2c07ec4600 | |||
| 871d8d67b0 | |||
| 7299713195 | |||
| 96afa7e108 | |||
| 55f57af0b9 | |||
| 6b35cd0116 | |||
| 99f2229e48 | |||
| b9641048fc | |||
| e9c6c6e609 | |||
| 40db42108f | |||
| 24bb1387d6 | |||
| d0bd4afb0b | |||
| 78ea786abd | |||
| 9cac2c9ceb | |||
| 0cff3245df | |||
| 0e3925db3f | |||
| c9a13d214b | |||
| 0954af2d4c | |||
| 7c1c952f5a | |||
| a6edfc730a | |||
| c1264bfedc | |||
| 90a5b6ceb0 | |||
| eb240ad2e5 | |||
| 88c961f37e | |||
| b952376968 | |||
| 5dd605d3a2 | |||
| 9c0c98b1ed | |||
| 615867322d | |||
| 02d3cd5909 | |||
| 47e5af15ea | |||
| a5c239c174 | |||
| c2afc5ca44 | |||
| cee062d4c9 | |||
| b21dc8cbe0 | |||
| e1133a2dcb | |||
| e273fec93c | |||
| 95d0b073bd | |||
| 9c7d841b37 | |||
| f086bf5310 | |||
| 6613d7b7ad | |||
| 6adf130bf3 | |||
| b6b2eaf5bc | |||
| eb4b7c70a9 | |||
| 84661ea6ed | |||
| 041122908c | |||
| 401de608df | |||
| 726c64cf10 | |||
| e1f781a21a | |||
| 72b81badad | |||
| 17428a8fbf | |||
| 6e1879dfae | |||
| eb1d2e04bc | |||
| c909ffa758 | |||
| ff5677ad19 | |||
| 8b1b31ac20 | |||
| 953600c177 | |||
| 536e7eb7f2 | |||
| 6d66721ba1 | |||
| 3b29cd0bdc | |||
| 109b624dfe | |||
| 0ed70c4d9f | |||
| a50b7591ca | |||
| bf8fc11b53 | |||
| bc4a613b6d | |||
| 4bb001351c | |||
| e155ba44bb | |||
| a4a9637738 | |||
| 5292142046 | |||
| cc5147ad72 | |||
| fffbf1a669 | |||
| d37bc0fb4d | |||
| 6d60dc8839 | |||
| b713a3a651 | |||
| 7e8db1dc68 | |||
| 316add860b | |||
| 79ce6f1745 | |||
| 09fdf4eacd | |||
| 143b2dd15a | |||
| e2c4e9d95c | |||
| d87082f04b | |||
| cc14855ba0 | |||
| 56c418745f | |||
| 4ce9911837 | |||
| 559c407552 | |||
| a2f4e29a7b | |||
| b8cf739d27 | |||
| aec2f9521d | |||
| 7b55ecdc59 | |||
| e9575bcb78 | |||
| 308aeb187b | |||
| 05d18f2bc5 | |||
| 3da4a0ac69 | |||
| 9d12b3f20e | |||
| 852a374748 | |||
| 3b5bab3308 | |||
| 69e75f4a67 | |||
| b0fd4befb1 | |||
| 3e80666a74 | |||
| 3e4567bae8 | |||
| db075d8cf9 | |||
| 16df0ef37e | |||
| f1a2ac5108 | |||
| e5743b358d | |||
| 1184bbe4cb | |||
| 9dcffda7ae | |||
| e5903635e2 | |||
| 510b4eaaae | |||
| d7e63b4316 | |||
| e5dd410256 | |||
| 951e46929e | |||
| c8adac30f4 | |||
| b2b4f7635f | |||
| 649b1d2172 | |||
| dc5f9ba164 | |||
| cf084e1db1 | |||
| e0e5898601 | |||
| dfec2de5b5 | |||
| 04002d57bd | |||
| 38a7706c8b | |||
| abaf875c42 | |||
| 5f76cb01c7 | |||
| ae54e833ea | |||
| 0dd3659de5 | |||
| 999f6abd2c | |||
| 48da75dd35 | |||
| 4f84eeb298 | |||
| a5b4cfadb9 | |||
| 3e906a9b8c | |||
| 110393eadb | |||
| 98caa8c058 | |||
| ef65f09608 | |||
| e130cf3a3b | |||
| 8a348f17fd | |||
| 797d1f27e8 | |||
| 6cde600922 |
@@ -0,0 +1,26 @@
|
||||
name: CIFuzz
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cpp-httplib'
|
||||
dry-run: false
|
||||
language: c++
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cpp-httplib'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
language: c++
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
@@ -8,10 +8,34 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-latest]
|
||||
os: [macOS-latest, ubuntu-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: prepare git for checkout on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
- name: checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v3
|
||||
- name: install brotli library on ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt update && sudo apt-get install -y libbrotli-dev
|
||||
- name: install brotli library on macOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: brew install brotli
|
||||
- name: make
|
||||
run: cd test && make
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: cd test && make -j2
|
||||
- name: check fuzz test target
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cd test && make fuzz_test
|
||||
- name: setup msbuild on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
- name: make-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cd test
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
||||
x64\Release\test.exe
|
||||
|
||||
@@ -10,10 +10,15 @@ example/redirect
|
||||
example/sse*
|
||||
example/upload
|
||||
example/*.pem
|
||||
test/httplib.cc
|
||||
test/httplib.h
|
||||
test/test
|
||||
test/server_fuzzer
|
||||
test/test_proxy
|
||||
test/test_split
|
||||
test/test.xcodeproj/xcuser*
|
||||
test/test.xcodeproj/*/xcuser*
|
||||
test/*.o
|
||||
test/*.pem
|
||||
test/*.srl
|
||||
|
||||
|
||||
+64
-69
@@ -1,20 +1,23 @@
|
||||
#[[
|
||||
Build options:
|
||||
* BUILD_SHARED_LIBS (default off) builds as a static library (if HTTPLIB_COMPILE is ON)
|
||||
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
|
||||
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
||||
* HTTPLIB_REQUIRE_ZLIB (default off)
|
||||
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
|
||||
* HTTPLIB_REQUIRE_BROTLI (default off)
|
||||
* HTTPLIB_COMPILE (default off)
|
||||
* HTTPLIB_INSTALL (default 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) is available.
|
||||
This creates a httplib::httplib target (if found).
|
||||
After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli) is available.
|
||||
This creates a httplib::httplib target (if found and if listed components are supported).
|
||||
It can be linked like so:
|
||||
|
||||
target_link_libraries(your_exe httplib::httplib)
|
||||
@@ -42,6 +45,7 @@
|
||||
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL 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_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain 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).
|
||||
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
|
||||
@@ -60,22 +64,11 @@
|
||||
]]
|
||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||
|
||||
# Gets the latest tag as a string like "v0.6.6"
|
||||
# Can silently fail if git isn't on the system
|
||||
execute_process(COMMAND git describe --tags --abbrev=0
|
||||
OUTPUT_VARIABLE _raw_version_string
|
||||
ERROR_VARIABLE _git_tag_error
|
||||
)
|
||||
# Get the CPPHTTPLIB_VERSION value and use it as a version
|
||||
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
|
||||
# This is so the maintainer doesn't actually need to update this manually.
|
||||
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
|
||||
|
||||
# execute_process can fail silenty, so check for an error
|
||||
# if there was an error, just use the user agent as a version
|
||||
if(_git_tag_error)
|
||||
message(WARNING "cpp-httplib failed to find the latest git tag, falling back to using user agent as the version.")
|
||||
# Get the user agent and use it as a version
|
||||
# This gets the string with the user agent from the header.
|
||||
# This is so the maintainer doesn't actually need to update this manually.
|
||||
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
||||
endif()
|
||||
# Needed since git tags have "v" prefixing them.
|
||||
# Also used if the fallback to user agent string is being used.
|
||||
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||
@@ -89,19 +82,22 @@ set(_HTTPLIB_OPENSSL_MIN_VER "1.1.1")
|
||||
# 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_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
|
||||
# Allow for a build to casually enable OpenSSL/ZLIB support, but silenty continue if not found.
|
||||
# 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_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)
|
||||
# Lets you disable the installation (useful when fetched from another CMake project)
|
||||
option(HTTPLIB_INSTALL "Enables the installation target" ON)
|
||||
# Just setting this variable here for people building in-tree
|
||||
if(HTTPLIB_COMPILE)
|
||||
set(HTTPLIB_IS_COMPILED TRUE)
|
||||
endif()
|
||||
|
||||
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 compression support." ON)
|
||||
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)
|
||||
# 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)
|
||||
@@ -111,6 +107,7 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
||||
endif()
|
||||
|
||||
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
||||
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||
find_package(Threads REQUIRED)
|
||||
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||
@@ -146,6 +143,10 @@ if(Brotli_FOUND)
|
||||
set(HTTPLIB_IS_USING_BROTLI TRUE)
|
||||
endif()
|
||||
|
||||
if(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
||||
set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN TRUE)
|
||||
endif()
|
||||
|
||||
# Used for default, common dirs that the end-user can change (if needed)
|
||||
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
|
||||
include(GNUInstallDirs)
|
||||
@@ -171,7 +172,7 @@ if(HTTPLIB_COMPILE)
|
||||
ERROR_VARIABLE _httplib_split_error
|
||||
)
|
||||
if(_httplib_split_error)
|
||||
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||
endif()
|
||||
|
||||
# split.py puts output in "out"
|
||||
@@ -183,6 +184,11 @@ if(HTTPLIB_COMPILE)
|
||||
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
||||
)
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
VERSION ${${PROJECT_NAME}_VERSION}
|
||||
SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}"
|
||||
)
|
||||
else()
|
||||
# This is for header-only.
|
||||
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
||||
@@ -193,22 +199,12 @@ endif()
|
||||
# Only useful if building in-tree, versus using it from an installation.
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Might be missing some, but this list is somewhat comprehensive
|
||||
# Require C++11
|
||||
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
cxx_std_11
|
||||
cxx_nullptr
|
||||
cxx_lambdas
|
||||
cxx_override
|
||||
cxx_defaulted_functions
|
||||
cxx_attribute_deprecated
|
||||
cxx_auto_type
|
||||
cxx_decltype
|
||||
cxx_deleted_functions
|
||||
cxx_range_for
|
||||
cxx_sizeof_member
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
|
||||
$<BUILD_INTERFACE:${_httplib_build_includedir}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
@@ -220,6 +216,8 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
$<$<PLATFORM_ID:Windows>:crypt32>
|
||||
$<$<PLATFORM_ID:Windows>:cryptui>
|
||||
# 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>"
|
||||
# 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>
|
||||
@@ -231,20 +229,14 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
|
||||
# Set the definitions to enable optional features
|
||||
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:"CPPHTTPLIB_BROTLI_SUPPORT">
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:"CPPHTTPLIB_ZLIB_SUPPORT">
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:"CPPHTTPLIB_OPENSSL_SUPPORT">
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_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>
|
||||
)
|
||||
|
||||
# Cmake's find_package search path is different based on the system
|
||||
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}")
|
||||
else()
|
||||
# On Non-Windows, it should be /usr/lib/cmake/<name>/<name>Config.cmake
|
||||
# NOTE: This may or may not work for macOS...
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
endif()
|
||||
# CMake configuration files installation directory
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
@@ -254,51 +246,54 @@ configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
|
||||
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
|
||||
# Passes the includedir install path
|
||||
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
||||
# There aren't any components, so don't use the macro
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
if(HTTPLIB_COMPILE)
|
||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Example: if you find_package(httplib 0.5.4)
|
||||
# then anything >= 0.5 and <= 1.0 is accepted
|
||||
COMPATIBILITY SameMajorVersion
|
||||
COMPATIBILITY SameMinorVersion
|
||||
)
|
||||
else()
|
||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Example: if you find_package(httplib 0.5.4)
|
||||
# then anything >= 0.5 and <= 1.0 is accepted
|
||||
COMPATIBILITY SameMajorVersion
|
||||
COMPATIBILITY SameMinorVersion
|
||||
# Tells Cmake that it's a header-only lib
|
||||
# Mildly useful for end-users :)
|
||||
ARCH_INDEPENDENT
|
||||
)
|
||||
endif()
|
||||
|
||||
# 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
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
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
|
||||
)
|
||||
|
||||
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
|
||||
|
||||
install(FILES
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Install it so it can be used later by the httplibConfig.cmake file.
|
||||
# Put it in the same dir as our config file instead of a global path so we don't potentially stomp on other packages.
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindBrotli.cmake"
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
|
||||
# NOTE: This path changes depending on if it's on Windows or Linux
|
||||
install(EXPORT httplibTargets
|
||||
# Puts the targets into the httplib namespace
|
||||
# So this makes httplib::httplib linkable after doing find_package(httplib)
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
# NOTE: This path changes depending on if it's on Windows or Linux
|
||||
install(EXPORT httplibTargets
|
||||
# Puts the targets into the httplib namespace
|
||||
# So this makes httplib::httplib linkable after doing find_package(httplib)
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HTTPLIB_TEST)
|
||||
include(CTest)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
@@ -2,16 +2,84 @@ cpp-httplib
|
||||
===========
|
||||
|
||||
[](https://github.com/yhirose/cpp-httplib/actions)
|
||||
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
||||
|
||||
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
|
||||
It's extremely easy to setup. Just include **httplib.h** file in your code!
|
||||
It's extremely easy to setup. Just include the **httplib.h** file in your code!
|
||||
|
||||
For Windows users: Please read [this note](https://github.com/yhirose/cpp-httplib#windows).
|
||||
NOTE: 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.
|
||||
|
||||
Server Example
|
||||
--------------
|
||||
Simple examples
|
||||
---------------
|
||||
|
||||
#### Server (Multi-threaded)
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// HTTP
|
||||
httplib::Server svr;
|
||||
|
||||
// HTTPS
|
||||
httplib::SSLServer svr;
|
||||
|
||||
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
```
|
||||
|
||||
#### Client
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// HTTP
|
||||
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
|
||||
|
||||
// HTTPS
|
||||
httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co");
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
res->status;
|
||||
res->body;
|
||||
```
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
|
||||
|
||||
NOTE for macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234"); // scheme + host
|
||||
httplib::SSLClient cli("localhost:1234"); // host
|
||||
httplib::SSLClient cli("localhost", 1234); // host, port
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_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.
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
```c++
|
||||
#include <httplib.h>
|
||||
@@ -90,24 +158,39 @@ svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
|
||||
|
||||
The followings are built-in mappings:
|
||||
|
||||
| Extension | MIME Type |
|
||||
| :-------- | :--------------------- |
|
||||
| txt | text/plain |
|
||||
| html, htm | text/html |
|
||||
| css | text/css |
|
||||
| jpeg, jpg | image/jpg |
|
||||
| png | image/png |
|
||||
| gif | image/gif |
|
||||
| svg | image/svg+xml |
|
||||
| ico | image/x-icon |
|
||||
| json | application/json |
|
||||
| pdf | application/pdf |
|
||||
| js | application/javascript |
|
||||
| wasm | application/wasm |
|
||||
| xml | application/xml |
|
||||
| xhtml | application/xhtml+xml |
|
||||
| Extension | MIME Type | Extension | MIME Type |
|
||||
| :--------- | :-------------------------- | :--------- | :-------------------------- |
|
||||
| css | text/css | mpga | audio/mpeg |
|
||||
| csv | text/csv | weba | audio/webm |
|
||||
| txt | text/plain | wav | audio/wave |
|
||||
| vtt | text/vtt | otf | font/otf |
|
||||
| html, htm | text/html | ttf | font/ttf |
|
||||
| apng | image/apng | woff | font/woff |
|
||||
| avif | image/avif | woff2 | font/woff2 |
|
||||
| bmp | image/bmp | 7z | application/x-7z-compressed |
|
||||
| gif | image/gif | atom | application/atom+xml |
|
||||
| png | image/png | pdf | application/pdf |
|
||||
| svg | image/svg+xml | mjs, js | application/javascript |
|
||||
| webp | image/webp | json | application/json |
|
||||
| ico | image/x-icon | rss | application/rss+xml |
|
||||
| tif | image/tiff | tar | application/x-tar |
|
||||
| tiff | image/tiff | xhtml, xht | application/xhtml+xml |
|
||||
| jpeg, jpg | image/jpeg | xslt | application/xslt+xml |
|
||||
| mp4 | video/mp4 | xml | application/xml |
|
||||
| mpeg | video/mpeg | gz | application/gzip |
|
||||
| webm | video/webm | zip | application/zip |
|
||||
| mp3 | audio/mp3 | wasm | application/wasm |
|
||||
|
||||
NOTE: These the static file server methods are not thread safe.
|
||||
### File request handler
|
||||
|
||||
```cpp
|
||||
// The handler is called right before the response is sent to a client
|
||||
svr.set_file_request_handler([](const Request &req, Response &res) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
NOTE: These static file server methods are not thread-safe.
|
||||
|
||||
### Logging
|
||||
|
||||
@@ -128,6 +211,47 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Exception handler
|
||||
The exception handler gets called if a user routing handler throws an error.
|
||||
|
||||
```cpp
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
|
||||
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||
char buf[BUFSIZ];
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (std::exception &e) {
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
} catch (...) { // See the following NOTE
|
||||
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
|
||||
}
|
||||
res.set_content(buf, "text/html");
|
||||
res.status = 500;
|
||||
});
|
||||
```
|
||||
|
||||
NOTE: if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||
if (req.path == "/hello") {
|
||||
res.set_content("world", "text/html");
|
||||
return Server::HandlerResponse::Handled;
|
||||
}
|
||||
return Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
```
|
||||
|
||||
### Post routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_post_routing_handler([](const auto& req, auto& res) {
|
||||
res.set_header("ADDITIONAL_HEADER", "value");
|
||||
});
|
||||
```
|
||||
|
||||
### 'multipart/form-data' POST data
|
||||
|
||||
```cpp
|
||||
@@ -141,12 +265,13 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Receive content with Content receiver
|
||||
### Receive content with a content receiver
|
||||
|
||||
```cpp
|
||||
svr.Post("/content_receiver",
|
||||
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
||||
if (req.is_multipart_form_data()) {
|
||||
// NOTE: `content_reader` is blocking until every form data field is read
|
||||
MultipartFormDataItems files;
|
||||
content_reader(
|
||||
[&](const MultipartFormData &file) {
|
||||
@@ -163,12 +288,11 @@ svr.Post("/content_receiver",
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
### Send content with the content provider
|
||||
|
||||
```cpp
|
||||
const size_t DATA_CHUNK_SIZE = 4;
|
||||
@@ -178,12 +302,32 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
"text/plain", // Content type
|
||||
[data](size_t offset, size_t length, DataSink &sink) {
|
||||
const auto &d = *data;
|
||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
},
|
||||
[data] { delete data; });
|
||||
[data](bool success) { delete data; });
|
||||
});
|
||||
```
|
||||
|
||||
Without content length:
|
||||
|
||||
```cpp
|
||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
res.set_content_provider(
|
||||
"text/plain", // Content type
|
||||
[&](size_t offset, DataSink &sink) {
|
||||
if (/* there is still data */) {
|
||||
std::vector<char> data;
|
||||
// prepare data...
|
||||
sink.write(data.data(), data.size());
|
||||
} else {
|
||||
sink.done(); // No more data
|
||||
}
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
@@ -192,20 +336,42 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_chunked_content_provider(
|
||||
"text/plain",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done();
|
||||
sink.done(); // No more data
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
With trailer:
|
||||
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_header("Trailer", "Dummy1, Dummy2");
|
||||
res.set_chunked_content_provider(
|
||||
"text/plain",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done_with_trailer({
|
||||
{"Dummy1", "DummyVal1"},
|
||||
{"Dummy2", "DummyVal2"}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 'Expect: 100-continue' handler
|
||||
|
||||
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
|
||||
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
|
||||
|
||||
```cpp
|
||||
// Send a '417 Expectation Failed' response.
|
||||
@@ -225,6 +391,7 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
|
||||
```cpp
|
||||
svr.set_keep_alive_max_count(2); // Default is 5
|
||||
svr.set_keep_alive_timeout(10); // Default is 5
|
||||
```
|
||||
|
||||
### Timeout
|
||||
@@ -235,7 +402,7 @@ svr.set_write_timeout(5, 0); // 5 seconds
|
||||
svr.set_idle_interval(0, 100000); // 100 milliseconds
|
||||
```
|
||||
|
||||
### Set maximum payload length for reading request body
|
||||
### Set maximum payload length for reading a request body
|
||||
|
||||
```c++
|
||||
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
|
||||
@@ -247,13 +414,18 @@ Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/e
|
||||
|
||||
### Default thread pool support
|
||||
|
||||
`ThreadPool` is used as a **default** task queue, and the default thread count is 8, or `std::thread::hardware_concurrency()`. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||
|
||||
`ThreadPool` is used as a default task queue, and the default thread count is set to value from `std::thread::hardware_concurrency()`.
|
||||
If you want to set the thread count at runtime, there is no convenient way... But here is how.
|
||||
|
||||
You can change the thread count by setting `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||
```cpp
|
||||
svr.new_task_queue = [] { return new ThreadPool(12); };
|
||||
```
|
||||
|
||||
### Override the default thread pool with yours
|
||||
|
||||
You can supply your own thread pool implementation according to your need.
|
||||
|
||||
```cpp
|
||||
class YourThreadPoolTaskQueue : public TaskQueue {
|
||||
public:
|
||||
@@ -278,8 +450,8 @@ svr.new_task_queue = [] {
|
||||
};
|
||||
```
|
||||
|
||||
Client Example
|
||||
--------------
|
||||
Client
|
||||
------
|
||||
|
||||
```c++
|
||||
#include <httplib.h>
|
||||
@@ -287,16 +459,53 @@ Client Example
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// IMPORTANT: 1st parameter must be a hostname or an IP address string.
|
||||
httplib::Client cli("localhost", 1234);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
if (auto res = cli.Get("/hi")) {
|
||||
if (res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
} else {
|
||||
auto err = res.error();
|
||||
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: Constructor with scheme-host-port string is now supported!
|
||||
|
||||
```c++
|
||||
httplib::Client cli("localhost");
|
||||
httplib::Client cli("localhost:8080");
|
||||
httplib::Client cli("http://localhost");
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
httplib::Client cli("https://localhost");
|
||||
httplib::SSLClient cli("localhost");
|
||||
```
|
||||
|
||||
### Error code
|
||||
|
||||
Here is the list of errors from `Result::error()`.
|
||||
|
||||
```c++
|
||||
enum Error {
|
||||
Success = 0,
|
||||
Unknown,
|
||||
Connection,
|
||||
BindIPAddress,
|
||||
Read,
|
||||
Write,
|
||||
ExceedRedirectCount,
|
||||
Canceled,
|
||||
SSLConnection,
|
||||
SSLLoadingCerts,
|
||||
SSLServerVerification,
|
||||
UnsupportedMultipartBoundaryChars,
|
||||
Compression,
|
||||
ConnectionTimeout,
|
||||
};
|
||||
```
|
||||
|
||||
### GET with HTTP headers
|
||||
|
||||
```c++
|
||||
@@ -305,6 +514,17 @@ httplib::Headers headers = {
|
||||
};
|
||||
auto res = cli.Get("/hi", headers);
|
||||
```
|
||||
or
|
||||
```c++
|
||||
auto res = cli.Get("/hi", {{"Accept-Encoding", "gzip, deflate"}});
|
||||
```
|
||||
or
|
||||
```c++
|
||||
cli.set_default_headers({
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
});
|
||||
auto res = cli.Get("/hi");
|
||||
```
|
||||
|
||||
### POST
|
||||
|
||||
@@ -374,7 +594,7 @@ cli.set_read_timeout(5, 0); // 5 seconds
|
||||
cli.set_write_timeout(5, 0); // 5 seconds
|
||||
```
|
||||
|
||||
### Receive content with Content receiver
|
||||
### Receive content with a content receiver
|
||||
|
||||
```c++
|
||||
std::string body;
|
||||
@@ -401,12 +621,12 @@ auto res = cli.Get(
|
||||
});
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
### Send content with a content provider
|
||||
|
||||
```cpp
|
||||
std::string body = ...;
|
||||
|
||||
auto res = cli_.Post(
|
||||
auto res = cli.Post(
|
||||
"/stream", body.size(),
|
||||
[](size_t offset, size_t length, DataSink &sink) {
|
||||
sink.write(body.data() + offset, length);
|
||||
@@ -415,19 +635,33 @@ auto res = cli_.Post(
|
||||
"text/plain");
|
||||
```
|
||||
|
||||
### Chunked transfer encoding
|
||||
|
||||
```cpp
|
||||
auto res = cli.Post(
|
||||
"/stream",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.os << "chunked data 1";
|
||||
sink.os << "chunked data 2";
|
||||
sink.os << "chunked data 3";
|
||||
sink.done();
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
"text/plain");
|
||||
```
|
||||
|
||||
### With Progress Callback
|
||||
|
||||
```cpp
|
||||
httplib::Client client(url, port);
|
||||
|
||||
// prints: 0 / 000 bytes => 50% complete
|
||||
std::shared_ptr<httplib::Response> res =
|
||||
cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||
printf("%lld / %lld bytes => %d%% complete\n",
|
||||
len, total,
|
||||
(int)(len*100/total));
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
}
|
||||
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||
printf("%lld / %lld bytes => %d%% complete\n",
|
||||
len, total,
|
||||
(int)(len*100/total));
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
@@ -441,6 +675,9 @@ cli.set_basic_auth("user", "pass");
|
||||
|
||||
// Digest Authentication
|
||||
cli.set_digest_auth("user", "pass");
|
||||
|
||||
// Bearer Token Authentication
|
||||
cli.set_bearer_token_auth("token");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
@@ -455,6 +692,9 @@ cli.set_proxy_basic_auth("user", "pass");
|
||||
|
||||
// Digest Authentication
|
||||
cli.set_proxy_digest_auth("user", "pass");
|
||||
|
||||
// Bearer Token Authentication
|
||||
cli.set_proxy_bearer_token_auth("pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
@@ -504,7 +744,7 @@ res = cli.Get("/");
|
||||
res->status; // 200
|
||||
```
|
||||
|
||||
### Use a specitic network interface
|
||||
### Use a specific network interface
|
||||
|
||||
NOTE: This feature is not available on Windows, yet.
|
||||
|
||||
@@ -512,27 +752,10 @@ NOTE: This feature is not available on Windows, yet.
|
||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||
```
|
||||
|
||||
OpenSSL Support
|
||||
---------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib supports 1.1.1 (until 2023-09-11) and 1.0.2 (2019-12-31).
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
SSLClient cli("localhost", 8080);
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
cli.enable_server_certificate_verification(true);
|
||||
```
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The server can applie compression to the following MIME type contents:
|
||||
The server can apply compression to the following MIME type contents:
|
||||
|
||||
* all text types except text/event-stream
|
||||
* image/svg+xml
|
||||
@@ -565,13 +788,29 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res->body; // Compressed data
|
||||
```
|
||||
|
||||
Use `poll` instead of `select`
|
||||
------------------------------
|
||||
|
||||
`select` system call is used as default since it's more widely supported. If you want to let cpp-httplib use `poll` instead, you can do so with `CPPHTTPLIB_USE_POLL`.
|
||||
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
```bash
|
||||
> python3 split.py
|
||||
> ls out
|
||||
httplib.h httplib.cc
|
||||
```console
|
||||
$ ./split.py -h
|
||||
usage: split.py [-h] [-e EXTENSION] [-o OUT]
|
||||
|
||||
This script splits httplib.h into .h and .cc parts.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-e EXTENSION, --extension EXTENSION
|
||||
extension of the implementation file (default: cc)
|
||||
-o OUT, --out OUT where to write the files (default: out)
|
||||
|
||||
$ ./split.py
|
||||
Wrote out/httplib.h and out/httplib.cc
|
||||
```
|
||||
|
||||
NOTE
|
||||
@@ -596,12 +835,14 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
Note: Cygwin on Windows is not supported.
|
||||
NOTE: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
|
||||
|
||||
NOTE: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin on Windows are not supported.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT license (© 2020 Yuji Hirose)
|
||||
MIT license (© 2023 Yuji Hirose)
|
||||
|
||||
Special Thanks To
|
||||
-----------------
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
build_script:
|
||||
- cmd: >-
|
||||
cd test
|
||||
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Release;Platform=%PLATFORM%
|
||||
|
||||
test_script:
|
||||
- cmd: x64\Release\test.exe
|
||||
+128
-135
@@ -5,181 +5,174 @@
|
||||
# The targets will have the same names, but it will use the static libs.
|
||||
#
|
||||
# Valid find_package COMPONENTS names: "decoder", "encoder", and "common"
|
||||
# Note that if you're requiring "decoder" or "encoder", then "common" will be automatically added as required.
|
||||
#
|
||||
# Defines the libraries (if found): Brotli::decoder, Brotli::encoder, Brotli::common
|
||||
# and the includes path variable: Brotli_INCLUDE_DIR
|
||||
#
|
||||
# If it's failing to find the libraries, try setting BROTLI_ROOT_DIR to the folder containing your library & include dir.
|
||||
|
||||
function(brotli_err_msg _err_msg)
|
||||
# If they asked for a specific version, warn/fail since we don't support it.
|
||||
# TODO: if they start distributing the version somewhere, implement finding it.
|
||||
# But currently there's a version header that doesn't seem to get installed.
|
||||
if(Brotli_FIND_VERSION)
|
||||
set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!")
|
||||
# If the package is required, throw a fatal error
|
||||
# Otherwise, if not running quietly, we throw a warning
|
||||
if(Brotli_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "${_err_msg}")
|
||||
message(FATAL_ERROR "${_brotli_version_error_msg}")
|
||||
elseif(NOT Brotli_FIND_QUIETLY)
|
||||
message(WARNING "${_err_msg}")
|
||||
message(WARNING "${_brotli_version_error_msg}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# If they asked for a specific version, warn/fail since we don't support it.
|
||||
if(Brotli_FIND_VERSION)
|
||||
brotli_err_msg("FindBrotli.cmake doesn't have version support!")
|
||||
endif()
|
||||
|
||||
# Since both decoder & encoder require the common lib (I think), force its requirement..
|
||||
# Since both decoder & encoder require the common lib, force its requirement..
|
||||
# if the user is requiring either of those other libs.
|
||||
if(Brotli_FIND_REQUIRED_decoder OR Brotli_FIND_REQUIRED_encoder)
|
||||
set(Brotli_FIND_REQUIRED_common TRUE)
|
||||
endif()
|
||||
|
||||
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
|
||||
# Credit to FindOpenSSL.cmake for this
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
set(_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
if(WIN32)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
else()
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Make PkgConfig optional, since some users (mainly Windows) don't have it.
|
||||
# But it's a lot more clean than manually using find_library.
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
pkg_check_modules(Brotli_common_STATIC QUIET IMPORTED_TARGET libbrotlicommon)
|
||||
pkg_check_modules(Brotli_decoder_STATIC QUIET IMPORTED_TARGET libbrotlidec)
|
||||
pkg_check_modules(Brotli_encoder_STATIC QUIET IMPORTED_TARGET libbrotlienc)
|
||||
else()
|
||||
pkg_check_modules(Brotli_common QUIET IMPORTED_TARGET libbrotlicommon)
|
||||
pkg_check_modules(Brotli_decoder QUIET IMPORTED_TARGET libbrotlidec)
|
||||
pkg_check_modules(Brotli_encoder QUIET IMPORTED_TARGET libbrotlienc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Only used if the PkgConfig libraries aren't used.
|
||||
find_path(Brotli_INCLUDE_DIR
|
||||
NAMES "brotli/decode.h" "brotli/encode.h"
|
||||
PATH_SUFFIXES "include" "includes"
|
||||
NAMES
|
||||
"brotli/decode.h"
|
||||
"brotli/encode.h"
|
||||
HINTS
|
||||
${BROTLI_ROOT_DIR}
|
||||
PATH_SUFFIXES
|
||||
"include"
|
||||
"includes"
|
||||
DOC "The path to Brotli's include directory."
|
||||
)
|
||||
# Hides this var from the GUI
|
||||
mark_as_advanced(Brotli_INCLUDE_DIR)
|
||||
|
||||
# Also check if Brotli_decoder was defined, as it can be passed by the end-user
|
||||
if(NOT TARGET PkgConfig::Brotli_decoder AND NOT Brotli_decoder)
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_decoder_lib_names
|
||||
"brotlidec-static"
|
||||
"libbrotlidec-static"
|
||||
)
|
||||
else()
|
||||
list(APPEND _brotli_decoder_lib_names
|
||||
"brotlidec"
|
||||
"libbrotlidec"
|
||||
)
|
||||
endif()
|
||||
find_library(Brotli_decoder
|
||||
NAMES ${_brotli_decoder_lib_names}
|
||||
PATH_SUFFIXES
|
||||
"lib"
|
||||
"lib64"
|
||||
"libs"
|
||||
"libs64"
|
||||
"lib/x86_64-linux-gnu"
|
||||
)
|
||||
# Just used for PkgConfig stuff in the loop below
|
||||
set(_brotli_stat_str "")
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
set(_brotli_stat_str "_STATIC")
|
||||
endif()
|
||||
|
||||
# Also check if Brotli_encoder was defined, as it can be passed by the end-user
|
||||
if(NOT TARGET PkgConfig::Brotli_encoder AND NOT Brotli_encoder)
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_encoder_lib_names
|
||||
"brotlienc-static"
|
||||
"libbrotlienc-static"
|
||||
)
|
||||
else()
|
||||
list(APPEND _brotli_encoder_lib_names
|
||||
"brotlienc"
|
||||
"libbrotlienc"
|
||||
)
|
||||
# Lets us know we are using the PkgConfig libraries
|
||||
# Will be set false if any non-pkgconf vars are used
|
||||
set(_brotli_using_pkgconf TRUE)
|
||||
|
||||
# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter)
|
||||
foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
||||
# Split the component name and literal library name from the listvar
|
||||
list(GET _listvar 0 _component_name)
|
||||
list(GET _listvar 1 _libname)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
# These need to be GLOBAL for MinGW when making ALIAS libraries against them.
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
# Have to use _STATIC to tell PkgConfig to find the static libs.
|
||||
pkg_check_modules(Brotli_${_component_name}_STATIC QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||
else()
|
||||
pkg_check_modules(Brotli_${_component_name} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||
endif()
|
||||
endif()
|
||||
find_library(Brotli_encoder
|
||||
NAMES ${_brotli_encoder_lib_names}
|
||||
PATH_SUFFIXES
|
||||
"lib"
|
||||
"lib64"
|
||||
"libs"
|
||||
"libs64"
|
||||
"lib/x86_64-linux-gnu"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Also check if Brotli_common was defined, as it can be passed by the end-user
|
||||
if(NOT TARGET PkgConfig::Brotli_common AND NOT Brotli_common)
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_common_lib_names
|
||||
"brotlicommon-static"
|
||||
"libbrotlicommon-static"
|
||||
)
|
||||
else()
|
||||
list(APPEND _brotli_common_lib_names
|
||||
"brotlicommon"
|
||||
"libbrotlicommon"
|
||||
)
|
||||
endif()
|
||||
find_library(Brotli_common
|
||||
NAMES ${_brotli_common_lib_names}
|
||||
PATH_SUFFIXES
|
||||
"lib"
|
||||
"lib64"
|
||||
"libs"
|
||||
"libs64"
|
||||
"lib/x86_64-linux-gnu"
|
||||
)
|
||||
endif()
|
||||
# Check if the target was already found by Pkgconf
|
||||
if(TARGET PkgConfig::Brotli_${_component_name} OR TARGET PkgConfig::Brotli_${_component_name}_STATIC)
|
||||
# Can't use generators for ALIAS targets, so you get this jank
|
||||
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
||||
|
||||
set(_brotli_req_vars "")
|
||||
# Generic loop to either create all the aliases for the end-user, or throw errors/warnings.
|
||||
# Note that the case here needs to match the case we used elsewhere in this file.
|
||||
foreach(_target_name "common" "decoder" "encoder")
|
||||
# The PkgConfig IMPORTED_TARGET has PkgConfig:: prefixed to it.
|
||||
if(TARGET PkgConfig::Brotli_${_target_name})
|
||||
add_library(Brotli::${_target_name} ALIAS PkgConfig::Brotli_${_target_name})
|
||||
|
||||
if(Brotli_FIND_REQUIRED_${_target_name})
|
||||
# The PkgConfig version of the library has a slightly different path to its lib.
|
||||
# Tells HANDLE_COMPONENTS we found the component
|
||||
set(Brotli_${_component_name}_FOUND TRUE)
|
||||
if(Brotli_FIND_REQUIRED_${_component_name})
|
||||
# If the lib is required, we can add its literal path as a required var for FindPackageHandleStandardArgs
|
||||
# Since it won't accept the PkgConfig targets
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_req_vars "Brotli_${_target_name}_STATIC_LINK_LIBRARIES")
|
||||
list(APPEND _brotli_req_vars "Brotli_${_component_name}_STATIC_LIBRARIES")
|
||||
else()
|
||||
list(APPEND _brotli_req_vars "Brotli_${_target_name}_LINK_LIBRARIES")
|
||||
list(APPEND _brotli_req_vars "Brotli_${_component_name}_LINK_LIBRARIES")
|
||||
endif()
|
||||
endif()
|
||||
# This will only trigger for libraries we found using find_library
|
||||
elseif(Brotli_${_target_name})
|
||||
add_library("Brotli::${_target_name}" UNKNOWN IMPORTED)
|
||||
# Safety-check the includes dir
|
||||
if(NOT Brotli_INCLUDE_DIR)
|
||||
brotli_err_msg("Failed to find Brotli's includes directory. Try manually defining \"Brotli_INCLUDE_DIR\" to Brotli's header path on your system.")
|
||||
endif()
|
||||
# Attach the literal library and include dir to the IMPORTED target for the end-user
|
||||
set_target_properties("Brotli::${_target_name}" PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Brotli_INCLUDE_DIR}"
|
||||
IMPORTED_LOCATION "${Brotli_${_target_name}}"
|
||||
|
||||
# Skip searching for the libs with find_library since it was already found by Pkgconf
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Lets us know we aren't using the PkgConfig libraries
|
||||
set(_brotli_using_pkgconf FALSE)
|
||||
if(Brotli_FIND_REQUIRED_${_component_name})
|
||||
# If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs
|
||||
list(APPEND _brotli_req_vars "Brotli_${_component_name}")
|
||||
endif()
|
||||
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_lib_names
|
||||
"brotli${_libname}-static"
|
||||
"libbrotli${_libname}-static"
|
||||
)
|
||||
# Attach the library from find_library to our required vars (if it's required)
|
||||
if(Brotli_FIND_REQUIRED_${_target_name})
|
||||
list(APPEND _brotli_req_vars "Brotli_${_target_name}")
|
||||
endif()
|
||||
# This will only happen if it's a required library but we didn't find it.
|
||||
elseif(Brotli_FIND_REQUIRED_${_target_name})
|
||||
# Only bother with an error/failure if they actually required the lib.
|
||||
brotli_err_msg("Failed to find Brotli's ${_target_name} library. Try manually defining \"Brotli_${_target_name}\" to its path on your system.")
|
||||
else()
|
||||
list(APPEND _brotli_lib_names
|
||||
"brotli${_libname}"
|
||||
"libbrotli${_libname}"
|
||||
)
|
||||
endif()
|
||||
|
||||
find_library(Brotli_${_component_name}
|
||||
NAMES ${_brotli_lib_names}
|
||||
HINTS ${BROTLI_ROOT_DIR}
|
||||
PATH_SUFFIXES
|
||||
"lib"
|
||||
"lib64"
|
||||
"libs"
|
||||
"libs64"
|
||||
"lib/x86_64-linux-gnu"
|
||||
)
|
||||
# Hide the library variable from the Cmake GUI
|
||||
mark_as_advanced(Brotli_${_component_name})
|
||||
|
||||
# Unset since otherwise it'll stick around for the next loop and break things
|
||||
unset(_brotli_lib_names)
|
||||
|
||||
# Check if find_library found the library
|
||||
if(Brotli_${_component_name})
|
||||
# Tells HANDLE_COMPONENTS we found the component
|
||||
set(Brotli_${_component_name}_FOUND TRUE)
|
||||
|
||||
add_library("Brotli::${_component_name}" UNKNOWN IMPORTED)
|
||||
# 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}"
|
||||
IMPORTED_LOCATION "${Brotli_${_component_name}}"
|
||||
)
|
||||
else()
|
||||
# Tells HANDLE_COMPONENTS we found the component
|
||||
set(Brotli_${_component_name}_FOUND FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# Sets Brotli_FOUND, and fails the find_package(Brotli) call if it was REQUIRED but missing libs.
|
||||
find_package_handle_standard_args(Brotli
|
||||
FOUND_VAR Brotli_FOUND
|
||||
REQUIRED_VARS ${_brotli_req_vars}
|
||||
FOUND_VAR
|
||||
Brotli_FOUND
|
||||
REQUIRED_VARS
|
||||
Brotli_INCLUDE_DIR
|
||||
${_brotli_required_targets}
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
if(Brotli_FOUND)
|
||||
include(FindPackageMessage)
|
||||
foreach(_lib_name ${_brotli_req_vars})
|
||||
# TODO: remove this if/when The Cmake PkgConfig file fixes the non-quiet message about libbrotlicommon being found.
|
||||
if(${_lib_name} MATCHES "common")
|
||||
# This avoids a duplicate "Found Brotli: /usr/lib/libbrotlicommon.so" type message.
|
||||
continue()
|
||||
endif()
|
||||
# Double-expand the var to get the actual path instead of the variable's name.
|
||||
find_package_message(Brotli "Found Brotli: ${${_lib_name}}"
|
||||
"[${${_lib_name}}][${Brotli_INCLUDE_DIR}]"
|
||||
)
|
||||
endforeach()
|
||||
# Restore the original find library ordering
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM alpine as builder
|
||||
WORKDIR /src/example
|
||||
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
|
||||
COPY ./httplib.h /src
|
||||
COPY ./example/hello.cc /src/example
|
||||
COPY ./example/Makefile /src/example
|
||||
RUN make hello
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add brotli libstdc++
|
||||
COPY --from=builder /src/example/hello /bin/hello
|
||||
CMD ["/bin/hello"]
|
||||
+17
-7
@@ -1,16 +1,26 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -std=c++14 -I.. -Wall -Wextra -pthread
|
||||
CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
PREFIX = /usr/local
|
||||
#PREFIX = $(shell brew --prefix)
|
||||
|
||||
OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
|
||||
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
|
||||
endif
|
||||
endif
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
||||
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
|
||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark issue
|
||||
|
||||
server : server.cc ../httplib.h Makefile
|
||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||
@@ -47,4 +57,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 sselci benchmark *.pem
|
||||
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark *.pem
|
||||
|
||||
+2
-2
@@ -23,12 +23,12 @@ int main(void) {
|
||||
httplib::Client cli("localhost", 8080);
|
||||
#endif
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
if (res) {
|
||||
if (auto res = cli.Get("/hi")) {
|
||||
cout << res->status << endl;
|
||||
cout << res->get_header_value("Content-Type") << endl;
|
||||
cout << res->body << endl;
|
||||
} else {
|
||||
cout << "error code: " << res.error() << std::endl;
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto result = cli.get_openssl_verify_result();
|
||||
if (result) {
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@ int main(void) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("localhost", 8080);
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
}
|
||||
|
||||
@@ -8,24 +8,21 @@
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
|
||||
#define CA_CERT_FILE "./ca-bundle.crt"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto res = httplib::Client2("https://localhost:8080")
|
||||
.set_ca_cert_path(CA_CERT_FILE)
|
||||
// .enable_server_certificate_verification(true)
|
||||
.Get("/hi");
|
||||
auto scheme_host_port = "https://localhost:8080";
|
||||
#else
|
||||
auto res = httplib::Client2("http://localhost:8080").Get("/hi");
|
||||
auto scheme_host_port = "http://localhost:8080";
|
||||
#endif
|
||||
|
||||
if (res) {
|
||||
if (auto res = httplib::Client(scheme_host_port).Get("/hi")) {
|
||||
cout << res->status << endl;
|
||||
cout << res->get_header_value("Content-Type") << endl;
|
||||
cout << res->body << endl;
|
||||
} else {
|
||||
cout << res.error() << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
httplib::Client2("http://localhost:1234")
|
||||
httplib::Client("http://localhost:1234")
|
||||
.Get("/event1", [&](const char *data, size_t data_length) {
|
||||
std::cout << string(data, data_length);
|
||||
return true;
|
||||
|
||||
+3
-12
@@ -1,10 +1,3 @@
|
||||
//
|
||||
// sse.cc
|
||||
//
|
||||
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@@ -20,15 +13,13 @@ using namespace std;
|
||||
class EventDispatcher {
|
||||
public:
|
||||
EventDispatcher() {
|
||||
id_ = 0;
|
||||
cid_ = -1;
|
||||
}
|
||||
|
||||
void wait_event(DataSink *sink) {
|
||||
unique_lock<mutex> lk(m_);
|
||||
int id = id_;
|
||||
cv_.wait(lk, [&] { return cid_ == id; });
|
||||
if (sink->is_writable()) { sink->write(message_.data(), message_.size()); }
|
||||
sink->write(message_.data(), message_.size());
|
||||
}
|
||||
|
||||
void send_event(const string &message) {
|
||||
@@ -41,8 +32,8 @@ public:
|
||||
private:
|
||||
mutex m_;
|
||||
condition_variable cv_;
|
||||
atomic_int id_;
|
||||
atomic_int cid_;
|
||||
atomic_int id_{0};
|
||||
atomic_int cid_{-1};
|
||||
string message_;
|
||||
};
|
||||
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#/usr/bin/env bash
|
||||
for i in {1..1000000}
|
||||
do
|
||||
echo "#### $i ####"
|
||||
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
|
||||
done
|
||||
+17
-8
@@ -12,19 +12,19 @@ set(HTTPLIB_VERSION @PROJECT_VERSION@)
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# We add find_dependency calls here to not make the end-user have to call them.
|
||||
find_dependency(Threads REQUIRED)
|
||||
find_dependency(Threads)
|
||||
if(@HTTPLIB_IS_USING_OPENSSL@)
|
||||
# OpenSSL COMPONENTS were added in Cmake v3.11
|
||||
if(CMAKE_VERSION VERSION_LESS "3.11")
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@)
|
||||
else()
|
||||
# Once the COMPONENTS were added, they were made optional when not specified.
|
||||
# Since we use both, we need to search for both.
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED)
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL)
|
||||
endif()
|
||||
endif()
|
||||
if(@HTTPLIB_IS_USING_ZLIB@)
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(@HTTPLIB_IS_USING_BROTLI@)
|
||||
@@ -32,7 +32,7 @@ if(@HTTPLIB_IS_USING_BROTLI@)
|
||||
# Note that the FindBrotli.cmake file is installed in the same dir as this file.
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@)
|
||||
find_dependency(Brotli COMPONENTS common encoder decoder REQUIRED)
|
||||
find_dependency(Brotli COMPONENTS common encoder decoder)
|
||||
endif()
|
||||
|
||||
# Mildly useful for end-users
|
||||
@@ -42,10 +42,19 @@ set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
# This is helpful if you're using Cmake's pre-compiled header feature
|
||||
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
||||
|
||||
# Brings in the target library
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||
# Consider each library support as a "component"
|
||||
set(httplib_OpenSSL_FOUND @HTTPLIB_IS_USING_OPENSSL@)
|
||||
set(httplib_ZLIB_FOUND @HTTPLIB_IS_USING_ZLIB@)
|
||||
set(httplib_Brotli_FOUND @HTTPLIB_IS_USING_BROTLI@)
|
||||
|
||||
# Ouputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||
check_required_components(httplib)
|
||||
|
||||
# Brings in the target library, but only if all required components are found
|
||||
if(NOT DEFINED httplib_FOUND OR httplib_FOUND)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||
endif()
|
||||
|
||||
# Outputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||
include(FindPackageMessage)
|
||||
if(TARGET httplib::httplib)
|
||||
set(HTTPLIB_FOUND TRUE)
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
project(
|
||||
'cpp-httplib',
|
||||
'cpp',
|
||||
license: 'MIT',
|
||||
default_options: [
|
||||
'cpp_std=c++11',
|
||||
'buildtype=release',
|
||||
'b_ndebug=if-release',
|
||||
'b_lto=true',
|
||||
'warning_level=3'
|
||||
],
|
||||
meson_version: '>=0.47.0'
|
||||
)
|
||||
|
||||
# Check just in case downstream decides to edit the source
|
||||
# and add a project version
|
||||
version = meson.project_version()
|
||||
if version == 'undefined'
|
||||
cxx = meson.get_compiler('cpp')
|
||||
version = cxx.get_define('CPPHTTPLIB_VERSION',
|
||||
prefix: '#include <httplib.h>',
|
||||
include_directories: include_directories('.')).strip('"')
|
||||
assert(version != '', 'failed to get version from httplib.h')
|
||||
endif
|
||||
|
||||
deps = [dependency('threads')]
|
||||
args = []
|
||||
|
||||
openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cpp-httplib_openssl'))
|
||||
if openssl_dep.found()
|
||||
deps += openssl_dep
|
||||
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||
if host_machine.system() == 'darwin'
|
||||
macosx_keychain_dep = dependency('appleframeworks', modules: ['CoreFoundation', 'Security'], required: get_option('cpp-httplib_macosx_keychain'))
|
||||
if macosx_keychain_dep.found()
|
||||
deps += macosx_keychain_dep
|
||||
args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
||||
if zlib_dep.found()
|
||||
deps += zlib_dep
|
||||
args += '-DCPPHTTPLIB_ZLIB_SUPPORT'
|
||||
endif
|
||||
|
||||
brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))]
|
||||
brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli'))
|
||||
brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli'))
|
||||
|
||||
brotli_found_all = true
|
||||
foreach brotli_dep : brotli_deps
|
||||
if not brotli_dep.found()
|
||||
brotli_found_all = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if brotli_found_all
|
||||
deps += brotli_deps
|
||||
args += '-DCPPHTTPLIB_BROTLI_SUPPORT'
|
||||
endif
|
||||
|
||||
cpp_httplib_dep = dependency('', required: false)
|
||||
|
||||
if get_option('cpp-httplib_compile')
|
||||
python3 = find_program('python3')
|
||||
|
||||
httplib_ch = custom_target(
|
||||
'split',
|
||||
input: 'httplib.h',
|
||||
output: ['httplib.cc', 'httplib.h'],
|
||||
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
|
||||
install: true,
|
||||
install_dir: [false, get_option('includedir')]
|
||||
)
|
||||
lib = library(
|
||||
'cpp-httplib',
|
||||
sources: httplib_ch,
|
||||
dependencies: deps,
|
||||
cpp_args: args,
|
||||
version: version,
|
||||
soversion: version.split('.')[0] + '.' + version.split('.')[1],
|
||||
install: true
|
||||
)
|
||||
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||
|
||||
import('pkgconfig').generate(
|
||||
lib,
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
extra_cflags: args,
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
else
|
||||
install_headers('httplib.h')
|
||||
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.'))
|
||||
|
||||
import('pkgconfig').generate(
|
||||
name: 'cpp-httplib',
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
install_dir: join_paths(get_option('datadir'), 'pkgconfig'),
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
endif
|
||||
|
||||
if meson.version().version_compare('>=0.54.0')
|
||||
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
|
||||
endif
|
||||
|
||||
if get_option('cpp-httplib_test')
|
||||
subdir('test')
|
||||
endif
|
||||
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
|
||||
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
|
||||
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
|
||||
option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
|
||||
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
|
||||
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
||||
@@ -1,32 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""This script splits httplib.h into .h and .cc parts."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
border = '// ----------------------------------------------------------------------------'
|
||||
|
||||
PythonVersion = sys.version_info[0];
|
||||
args_parser = argparse.ArgumentParser(description=__doc__)
|
||||
args_parser.add_argument(
|
||||
"-e", "--extension", help="extension of the implementation file (default: cc)",
|
||||
default="cc"
|
||||
)
|
||||
args_parser.add_argument(
|
||||
"-o", "--out", help="where to write the files (default: out)", default="out"
|
||||
)
|
||||
args = args_parser.parse_args()
|
||||
|
||||
with open('httplib.h') as f:
|
||||
lines = f.readlines()
|
||||
inImplementation = False
|
||||
|
||||
if PythonVersion < 3:
|
||||
os.makedirs('out')
|
||||
cur_dir = os.path.dirname(sys.argv[0])
|
||||
lib_name = 'httplib'
|
||||
header_name = '/' + lib_name + '.h'
|
||||
source_name = '/' + lib_name + '.' + args.extension
|
||||
# get the input file
|
||||
in_file = cur_dir + header_name
|
||||
# get the output file
|
||||
h_out = args.out + header_name
|
||||
cc_out = args.out + source_name
|
||||
|
||||
# if the modification time of the out file is after the in file,
|
||||
# don't split (as it is already finished)
|
||||
do_split = True
|
||||
|
||||
if os.path.exists(h_out):
|
||||
in_time = os.path.getmtime(in_file)
|
||||
out_time = os.path.getmtime(h_out)
|
||||
do_split = in_time > out_time
|
||||
|
||||
if do_split:
|
||||
with open(in_file) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
python_version = sys.version_info[0]
|
||||
if python_version < 3:
|
||||
os.makedirs(args.out)
|
||||
else:
|
||||
os.makedirs('out', exist_ok=True)
|
||||
|
||||
with open('out/httplib.h', 'w') as fh:
|
||||
with open('out/httplib.cc', 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
isBorderLine = border in line
|
||||
if isBorderLine:
|
||||
inImplementation = not inImplementation
|
||||
else:
|
||||
if inImplementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
pass
|
||||
else:
|
||||
fh.write(line)
|
||||
pass
|
||||
fc.write('} // namespace httplib\n')
|
||||
os.makedirs(args.out, exist_ok=True)
|
||||
|
||||
in_implementation = False
|
||||
cc_out = args.out + source_name
|
||||
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
is_border_line = border in line
|
||||
if is_border_line:
|
||||
in_implementation = not in_implementation
|
||||
elif in_implementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
else:
|
||||
fh.write(line)
|
||||
fc.write('} // namespace httplib\n')
|
||||
|
||||
print("Wrote {} and {}".format(h_out, cc_out))
|
||||
else:
|
||||
print("{} and {} are up to date".format(h_out, cc_out))
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
find_package(GTest)
|
||||
|
||||
if(GTest_FOUND)
|
||||
if(NOT TARGET GTest::gtest_main AND TARGET GTest::Main)
|
||||
# CMake <3.20
|
||||
add_library(GTest::gtest_main INTERFACE IMPORTED)
|
||||
target_link_libraries(GTest::gtest_main INTERFACE GTest::Main)
|
||||
endif()
|
||||
else()
|
||||
if(POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(BUILD_GMOCK OFF)
|
||||
set(INSTALL_GTEST OFF)
|
||||
set(gtest_force_shared_crt ON)
|
||||
|
||||
FetchContent_Declare(
|
||||
gtest
|
||||
URL https://github.com/google/googletest/archive/main.tar.gz
|
||||
)
|
||||
FetchContent_MakeAvailable(gtest)
|
||||
endif()
|
||||
|
||||
add_executable(httplib-test test.cc)
|
||||
target_compile_options(httplib-test PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/bigobj>")
|
||||
target_link_libraries(httplib-test PRIVATE httplib GTest::gtest_main)
|
||||
gtest_discover_tests(httplib-test)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www www
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www2 www2
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www3 www3
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_LIST_DIR}/ca-bundle.crt ca-bundle.crt
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_LIST_DIR}/image.jpg image.jpg
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
|
||||
if(HTTPLIB_IS_USING_OPENSSL)
|
||||
find_program(OPENSSL_COMMAND
|
||||
NAMES openssl
|
||||
PATHS ${OPENSSL_INCLUDE_DIR}/../bin
|
||||
REQUIRED
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key.pem
|
||||
OUTPUT_FILE cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -x509 -new -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE rootCA.key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -x509 -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.rootCA.conf -key rootCA.key.pem -days 1024
|
||||
OUTPUT_FILE rootCA.cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE client.key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client.key.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
|
||||
OUTPUT_FILE client.cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa -passout pass:test123! 2048
|
||||
OUTPUT_FILE key_encrypted.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key_encrypted.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key_encrypted.pem
|
||||
OUTPUT_FILE cert_encrypted.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
+55
-10
@@ -1,35 +1,80 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
|
||||
PREFIX = /usr/local
|
||||
#PREFIX = $(shell brew --prefix)
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
|
||||
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security
|
||||
endif
|
||||
endif
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
all : test
|
||||
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
|
||||
# By default, use standalone_fuzz_target_runner.
|
||||
# This runner does no fuzzing, but simply executes the inputs
|
||||
# provided via parameters.
|
||||
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||
# to link the fuzzer(s) against a real fuzzing engine.
|
||||
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||
|
||||
all : test test_split
|
||||
./test
|
||||
|
||||
proxy : test_proxy
|
||||
./test_proxy
|
||||
|
||||
test : test.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
|
||||
|
||||
# Note: The intention of test_split is to verify that it works to compile and
|
||||
# link the split httplib.h, so there is normally no need to execute it.
|
||||
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
|
||||
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
||||
|
||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test_proxy $(CXXFLAGS) test_proxy.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
|
||||
|
||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||
fuzz_test: server_fuzzer
|
||||
./server_fuzzer fuzzing/corpus/*
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
|
||||
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||
# feeds it to server_fuzzer.
|
||||
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) -c $<
|
||||
|
||||
httplib.cc : ../httplib.h
|
||||
python3 ../split.py -o .
|
||||
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
openssl genrsa 2048 > rootCA.key.pem
|
||||
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||
openssl genrsa 2048 > client.key.pem
|
||||
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
||||
openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
|
||||
openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
|
||||
#c_rehash .
|
||||
|
||||
clean:
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
||||
rm -f test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#CXX = clang++
|
||||
# Do not add default sanitizer flags here as OSS-fuzz adds its own sanitizer flags.
|
||||
CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I../.. -I. -Wall -Wextra -Wtype-limits -Wconversion
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
|
||||
# Using full path to libssl and libcrypto to avoid accidentally picking openssl libs brought in by msan.
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -I$(OPENSSL_DIR)/lib /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
# Runs all the tests and also fuzz tests against seed corpus.
|
||||
all : server_fuzzer
|
||||
./server_fuzzer corpus/*
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : server_fuzzer.cc ../../httplib.h
|
||||
# $(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
$(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
zip -q -r server_fuzzer_seed_corpus.zip corpus
|
||||
|
||||
clean:
|
||||
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
||||
@@ -0,0 +1 @@
|
||||
PUT /search/sample?a=12 HTTP/1.1
|
||||
@@ -0,0 +1,5 @@
|
||||
GET /hello.htm HTTP/1.1
|
||||
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
|
||||
Accept-Language: en-us
|
||||
Accept-Encoding: gzip, deflate
|
||||
Connection: Keep-Alive
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,91 @@
|
||||
#include <httplib.h>
|
||||
#include <memory>
|
||||
|
||||
class FuzzedStream : public httplib::Stream {
|
||||
public:
|
||||
FuzzedStream(const uint8_t *data, size_t size)
|
||||
: data_(data), size_(size), read_pos_(0) {}
|
||||
|
||||
ssize_t read(char *ptr, size_t size) override {
|
||||
if (size + read_pos_ > size_) { size = size_ - read_pos_; }
|
||||
memcpy(ptr, data_ + read_pos_, size);
|
||||
read_pos_ += size;
|
||||
return static_cast<ssize_t>(size);
|
||||
}
|
||||
|
||||
ssize_t write(const char *ptr, size_t size) override {
|
||||
response_.append(ptr, size);
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); }
|
||||
|
||||
ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
|
||||
|
||||
bool is_readable() const override { return true; }
|
||||
|
||||
bool is_writable() const override { return true; }
|
||||
|
||||
void get_remote_ip_and_port(std::string &ip, int &port) const override {
|
||||
ip = "127.0.0.1";
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
void get_local_ip_and_port(std::string &ip, int &port) const override {
|
||||
ip = "127.0.0.1";
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
socket_t socket() const override { return 0; }
|
||||
|
||||
private:
|
||||
const uint8_t *data_;
|
||||
size_t size_;
|
||||
size_t read_pos_;
|
||||
std::string response_;
|
||||
};
|
||||
|
||||
class FuzzableServer : public httplib::Server {
|
||||
public:
|
||||
void ProcessFuzzedRequest(FuzzedStream &stream) {
|
||||
bool connection_close = false;
|
||||
process_request(stream, /*last_connection=*/false, connection_close,
|
||||
nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
static FuzzableServer g_server;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) {
|
||||
g_server.Get(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Post(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Put(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Patch(R"(.*)",
|
||||
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Delete(
|
||||
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
g_server.Options(
|
||||
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||
res.set_content("response content", "text/plain");
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
FuzzedStream stream{data, size};
|
||||
g_server.ProcessFuzzedRequest(stream);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
# Sources: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
|
||||
|
||||
# misc
|
||||
"HTTP/1.1"
|
||||
|
||||
# verbs
|
||||
"CONNECT"
|
||||
"DELETE"
|
||||
"GET"
|
||||
"HEAD"
|
||||
"OPTIONS"
|
||||
"PATCH"
|
||||
"POST"
|
||||
"PUT"
|
||||
"TRACE"
|
||||
|
||||
|
||||
# Webdav/caldav verbs
|
||||
"ACL"
|
||||
"BASELINE-CONTROL"
|
||||
"BIND"
|
||||
"CHECKIN"
|
||||
"CHECKOUT"
|
||||
"COPY"
|
||||
"LABEL"
|
||||
"LINK"
|
||||
"LOCK"
|
||||
"MERGE"
|
||||
"MKACTIVITY"
|
||||
"MKCALENDAR"
|
||||
"MKCOL"
|
||||
"MKREDIRECTREF"
|
||||
"MKWORKSPACE"
|
||||
"MOVE"
|
||||
"ORDERPATCH"
|
||||
"PRI"
|
||||
"PROPFIND"
|
||||
"PROPPATCH"
|
||||
"REBIND"
|
||||
"REPORT"
|
||||
"SEARCH"
|
||||
"UNBIND"
|
||||
"UNCHECKOUT"
|
||||
"UNLINK"
|
||||
"UNLOCK"
|
||||
"UPDATE"
|
||||
"UPDATEREDIRECTREF"
|
||||
"VERSION-CONTROL"
|
||||
|
||||
|
||||
# Fields
|
||||
"A-IM"
|
||||
"Accept"
|
||||
"Accept-Charset"
|
||||
"Accept-Datetime"
|
||||
"Accept-Encoding"
|
||||
"Accept-Language"
|
||||
"Accept-Patch"
|
||||
"Accept-Ranges"
|
||||
"Access-Control-Allow-Credentials"
|
||||
"Access-Control-Allow-Headers"
|
||||
"Access-Control-Allow-Methods"
|
||||
"Access-Control-Allow-Origin"
|
||||
"Access-Control-Expose-Headers"
|
||||
"Access-Control-Max-Age"
|
||||
"Access-Control-Request-Headers"
|
||||
"Access-Control-Request-Method"
|
||||
"Age"
|
||||
"Allow"
|
||||
"Alt-Svc"
|
||||
"Authorization"
|
||||
"Cache-Control"
|
||||
"Connection"
|
||||
"Connection:"
|
||||
"Content-Disposition"
|
||||
"Content-Encoding"
|
||||
"Content-Language"
|
||||
"Content-Length"
|
||||
"Content-Location"
|
||||
"Content-MD5"
|
||||
"Content-Range"
|
||||
"Content-Security-Policy"
|
||||
"Content-Type"
|
||||
"Cookie"
|
||||
"DNT"
|
||||
"Date"
|
||||
"Delta-Base"
|
||||
"ETag"
|
||||
"Expect"
|
||||
"Expires"
|
||||
"Forwarded"
|
||||
"From"
|
||||
"Front-End-Https"
|
||||
"HTTP2-Settings"
|
||||
"Host"
|
||||
"IM"
|
||||
"If-Match"
|
||||
"If-Modified-Since"
|
||||
"If-None-Match"
|
||||
"If-Range"
|
||||
"If-Unmodified-Since"
|
||||
"Last-Modified"
|
||||
"Link"
|
||||
"Location"
|
||||
"Max-Forwards"
|
||||
"Origin"
|
||||
"P3P"
|
||||
"Pragma"
|
||||
"Proxy-Authenticate"
|
||||
"Proxy-Authorization"
|
||||
"Proxy-Connection"
|
||||
"Public-Key-Pins"
|
||||
"Range"
|
||||
"Referer"
|
||||
"Refresh"
|
||||
"Retry-After"
|
||||
"Save-Data"
|
||||
"Server"
|
||||
"Set-Cookie"
|
||||
"Status"
|
||||
"Strict-Transport-Security"
|
||||
"TE"
|
||||
"Timing-Allow-Origin"
|
||||
"Tk"
|
||||
"Trailer"
|
||||
"Transfer-Encoding"
|
||||
"Upgrade"
|
||||
"Upgrade-Insecure-Requests"
|
||||
"User-Agent"
|
||||
"Vary"
|
||||
"Via"
|
||||
"WWW-Authenticate"
|
||||
"Warning"
|
||||
"X-ATT-DeviceId"
|
||||
"X-Content-Duration"
|
||||
"X-Content-Security-Policy"
|
||||
"X-Content-Type-Options"
|
||||
"X-Correlation-ID"
|
||||
"X-Csrf-Token"
|
||||
"X-Forwarded-For"
|
||||
"X-Forwarded-Host"
|
||||
"X-Forwarded-Proto"
|
||||
"X-Frame-Options"
|
||||
"X-Http-Method-Override"
|
||||
"X-Powered-By"
|
||||
"X-Request-ID"
|
||||
"X-Requested-With"
|
||||
"X-UA-Compatible"
|
||||
"X-UIDH"
|
||||
"X-Wap-Profile"
|
||||
"X-WebKit-CSP"
|
||||
"X-XSS-Protection"
|
||||
|
||||
# Source: string and character literals in httplib.h
|
||||
" "
|
||||
"&"
|
||||
", "
|
||||
"-"
|
||||
"--"
|
||||
"."
|
||||
".."
|
||||
":"
|
||||
"="
|
||||
" = = "
|
||||
"0123456789abcdef"
|
||||
"%02X"
|
||||
"%0A"
|
||||
"\\x0a\\x0d"
|
||||
"%0D"
|
||||
"%20"
|
||||
"%27"
|
||||
"%2B"
|
||||
"%2C"
|
||||
"%3A"
|
||||
"%3B"
|
||||
"application/javascript"
|
||||
"application/json"
|
||||
"application/pdf"
|
||||
"application/xhtml+xml"
|
||||
"application/xml"
|
||||
"application/x-www-form-urlencoded"
|
||||
"Bad Request"
|
||||
"boundary="
|
||||
"bytes="
|
||||
"chunked"
|
||||
"close"
|
||||
"CONNECT"
|
||||
"css"
|
||||
"Forbidden"
|
||||
"Found"
|
||||
"gif"
|
||||
"gzip"
|
||||
"html"
|
||||
"ico"
|
||||
"image/gif"
|
||||
"image/jpg"
|
||||
"image/png"
|
||||
"image/svg+xml"
|
||||
"image/x-icon"
|
||||
"index.html"
|
||||
"Internal Server Error"
|
||||
"jpeg"
|
||||
"js"
|
||||
"json"
|
||||
"Location"
|
||||
"Moved Permanently"
|
||||
"multipart/form-data"
|
||||
"Not Found"
|
||||
"Not Modified"
|
||||
"OK"
|
||||
"pdf"
|
||||
"png"
|
||||
"Range"
|
||||
"REMOTE_ADDR"
|
||||
"See Other"
|
||||
"svg"
|
||||
"text/"
|
||||
"text/css"
|
||||
"text/html"
|
||||
"text/plain"
|
||||
"txt"
|
||||
"Unsupported Media Type"
|
||||
"xhtml"
|
||||
"xml"
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
// This runner does not do any fuzzing, but allows us to run the fuzz target
|
||||
// on the test corpus or on a single file,
|
||||
// e.g. the one that comes from a bug report.
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
// Forward declare the "fuzz target" interface.
|
||||
// We deliberately keep this interface simple and header-free.
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
|
||||
// It reads all files passed as parameters and feeds their contents
|
||||
// one by one into the fuzz target (LLVMFuzzerTestOneInput).
|
||||
int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::ifstream in(argv[i]);
|
||||
in.seekg(0, in.end);
|
||||
size_t length = static_cast<size_t>(in.tellg());
|
||||
in.seekg (0, in.beg);
|
||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||
std::vector<char> bytes(length);
|
||||
in.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
|
||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||
bytes.size());
|
||||
std::cout << "Execution successful" << std::endl;
|
||||
}
|
||||
std::cout << "Execution finished" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
+5790
-2420
File diff suppressed because it is too large
Load Diff
+6845
-14038
File diff suppressed because it is too large
Load Diff
@@ -27,13 +27,28 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstdio>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
std::cout << "Running main() from gtest_main.cc\n";
|
||||
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
|
||||
#if GTEST_OS_ESP8266
|
||||
extern "C" {
|
||||
#endif
|
||||
void setup() {
|
||||
testing::InitGoogleTest();
|
||||
}
|
||||
|
||||
void loop() { RUN_ALL_TESTS(); }
|
||||
|
||||
#if GTEST_OS_ESP8266
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
printf("Running main() from %s\n", __FILE__);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// The sole purpose of this file is to include httplib.h in a separate
|
||||
// compilation unit, thus verifying that inline keywords have not been forgotten
|
||||
// when linked together with test.cc.
|
||||
|
||||
#include <httplib.h>
|
||||
@@ -0,0 +1,112 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
gtest_dep = dependency('gtest', main: true)
|
||||
openssl = find_program('openssl')
|
||||
test_conf = files('test.conf')
|
||||
|
||||
key_pem = custom_target(
|
||||
'key_pem',
|
||||
output: 'key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
temp_req = custom_target(
|
||||
'temp_req',
|
||||
input: key_pem,
|
||||
output: 'temp_req',
|
||||
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
cert_pem = custom_target(
|
||||
'cert_pem',
|
||||
input: [temp_req, key_pem],
|
||||
output: 'cert.pem',
|
||||
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '3650', '-req', '-signkey', '@INPUT1@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
cert2_pem = custom_target(
|
||||
'cert2_pem',
|
||||
input: key_pem,
|
||||
output: 'cert2.pem',
|
||||
command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||
)
|
||||
|
||||
key_encrypted_pem = custom_target(
|
||||
'key_encrypted_pem',
|
||||
output: 'key_encrypted.pem',
|
||||
command: [openssl, 'genrsa', '-passout', 'pass:test123!', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
cert_encrypted_pem = custom_target(
|
||||
'cert_encrypted_pem',
|
||||
input: key_encrypted_pem,
|
||||
output: 'cert_encrypted.pem',
|
||||
command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||
)
|
||||
|
||||
rootca_key_pem = custom_target(
|
||||
'rootca_key_pem',
|
||||
output: 'rootCA.key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
rootca_cert_pem = custom_target(
|
||||
'rootca_cert_pem',
|
||||
input: rootca_key_pem,
|
||||
output: 'rootCA.cert.pem',
|
||||
command: [openssl, 'req', '-x509', '-new', '-batch', '-config', files('test.rootCA.conf'), '-key', '@INPUT@', '-days', '1024', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_key_pem = custom_target(
|
||||
'client_key_pem',
|
||||
output: 'client.key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
client_temp_req = custom_target(
|
||||
'client_temp_req',
|
||||
input: client_key_pem,
|
||||
output: 'client_temp_req',
|
||||
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_cert_pem = custom_target(
|
||||
'client_cert_pem',
|
||||
input: [client_temp_req, rootca_cert_pem, rootca_key_pem],
|
||||
output: 'client.cert.pem',
|
||||
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
# Copy test files to the build directory
|
||||
configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true)
|
||||
configure_file(input: 'image.jpg', output: 'image.jpg', copy: true)
|
||||
subdir(join_paths('www', 'dir'))
|
||||
subdir(join_paths('www2', 'dir'))
|
||||
subdir(join_paths('www3', 'dir'))
|
||||
|
||||
test(
|
||||
'main',
|
||||
executable(
|
||||
'main',
|
||||
'test.cc',
|
||||
dependencies: [
|
||||
cpp_httplib_dep,
|
||||
gtest_dep
|
||||
]
|
||||
),
|
||||
depends: [
|
||||
key_pem,
|
||||
cert_pem,
|
||||
cert2_pem,
|
||||
key_encrypted_pem,
|
||||
cert_encrypted_pem,
|
||||
rootca_key_pem,
|
||||
rootca_cert_pem,
|
||||
client_key_pem,
|
||||
client_cert_pem
|
||||
],
|
||||
workdir: meson.current_build_dir(),
|
||||
timeout: 300
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
docker-compose down --rmi all
|
||||
@@ -1 +0,0 @@
|
||||
docker-compose up -d
|
||||
+3509
-472
File diff suppressed because it is too large
Load Diff
@@ -16,3 +16,6 @@ emailAddress = test@email.address
|
||||
|
||||
[req_attributes]
|
||||
challengePassword = 1234
|
||||
|
||||
[SAN]
|
||||
subjectAltName=IP:127.0.0.1
|
||||
|
||||
+8
-2
@@ -22,7 +22,7 @@
|
||||
<ProjectGuid>{6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>test</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@@ -97,6 +97,7 @@
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -114,6 +115,8 @@
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -133,6 +136,7 @@
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -154,6 +158,8 @@
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -171,4 +177,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+37
-36
@@ -5,38 +5,40 @@
|
||||
using namespace std;
|
||||
using namespace httplib;
|
||||
|
||||
void ProxyTest(Client& cli, bool basic) {
|
||||
template <typename T>
|
||||
void ProxyTest(T& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(407, res->status);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(ProxyTest, SSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, SSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void RedirectProxyText(Client& cli, const char *path, bool basic) {
|
||||
template <typename T>
|
||||
void RedirectProxyText(T& cli, const char *path, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
@@ -52,29 +54,27 @@ void RedirectProxyText(Client& cli, const char *path, bool basic) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(RedirectTest, HTTPBinNoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
Client cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, HTTPBinNoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
Client cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", false);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
SSLClient cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
SSLClient cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", false);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
@@ -82,7 +82,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
RedirectProxyText(cli, "/", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeNoSSLDigest) {
|
||||
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
|
||||
Client cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", false);
|
||||
}
|
||||
@@ -100,7 +100,8 @@ TEST(RedirectTest, YouTubeSSLDigest) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void BaseAuthTestFromHTTPWatch(Client& cli) {
|
||||
template <typename T>
|
||||
void BaseAuthTestFromHTTPWatch(T& cli) {
|
||||
cli.set_proxy("localhost", 3128);
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
|
||||
@@ -157,7 +158,8 @@ TEST(BaseAuthTest, SSL) {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
void DigestAuthTestFromHTTPWatch(Client& cli) {
|
||||
template <typename T>
|
||||
void DigestAuthTestFromHTTPWatch(T& cli) {
|
||||
cli.set_proxy("localhost", 3129);
|
||||
cli.set_proxy_digest_auth("hello", "world");
|
||||
|
||||
@@ -191,7 +193,7 @@ void DigestAuthTestFromHTTPWatch(Client& cli) {
|
||||
}
|
||||
|
||||
// NOTE: Until httpbin.org fixes issue #46, the following test is commented
|
||||
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// out. Please see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// cli.set_digest_auth("bad", "world");
|
||||
// for (auto path : paths) {
|
||||
// auto res = cli.Get(path.c_str());
|
||||
@@ -214,7 +216,8 @@ TEST(DigestAuthTest, NoSSL) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void KeepAliveTest(Client& cli, bool basic) {
|
||||
template <typename T>
|
||||
void KeepAliveTest(T& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
@@ -230,20 +233,20 @@ void KeepAliveTest(Client& cli, bool basic) {
|
||||
#endif
|
||||
|
||||
{
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
{
|
||||
auto res = cli.Get("/redirect/2");
|
||||
auto res = cli.Get("/httpbin/redirect/2");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> paths = {
|
||||
"/digest-auth/auth/hello/world/MD5",
|
||||
"/digest-auth/auth/hello/world/SHA-256",
|
||||
"/digest-auth/auth/hello/world/SHA-512",
|
||||
"/digest-auth/auth-int/hello/world/MD5",
|
||||
"/httpbin/digest-auth/auth/hello/world/MD5",
|
||||
"/httpbin/digest-auth/auth/hello/world/SHA-256",
|
||||
"/httpbin/digest-auth/auth/hello/world/SHA-512",
|
||||
"/httpbin/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
for (auto path: paths) {
|
||||
@@ -254,34 +257,32 @@ void KeepAliveTest(Client& cli, bool basic) {
|
||||
}
|
||||
|
||||
{
|
||||
int count = 100;
|
||||
int count = 10;
|
||||
while (count--) {
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(KeepAliveTest, NoSSLWithBasic) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, NoSSLWithDigest) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.abcde', output: 'test.abcde', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
Reference in New Issue
Block a user