mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-16 10:49:36 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
No files matched your search
@@ -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
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
run: cd test && make -j2
|
run: cd test && make -j2
|
||||||
- name: check fuzz test target
|
- name: check fuzz test target
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: cd test && make -f Makefile.fuzz_test
|
run: cd test && make fuzz_test
|
||||||
- name: setup msbuild on windows
|
- name: setup msbuild on windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
uses: microsoft/setup-msbuild@v1.0.2
|
||||||
@@ -39,55 +39,3 @@ jobs:
|
|||||||
cd test
|
cd test
|
||||||
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
||||||
x64\Release\test.exe
|
x64\Release\test.exe
|
||||||
|
|
||||||
|
|
||||||
meson-build:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
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
|
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install dependencies on Linux
|
|
||||||
if: matrix.os == 'ubuntu-latest'
|
|
||||||
run: sudo apt-get -qq update && sudo apt-get -qq install meson libssl-dev zlib1g-dev libbrotli-dev libgtest-dev
|
|
||||||
|
|
||||||
- name: Install dependencies on MacOS
|
|
||||||
if: matrix.os == 'macos-latest'
|
|
||||||
run: brew install meson openssl brotli googletest
|
|
||||||
|
|
||||||
- name: Setup MSVC on Windows
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
|
||||||
|
|
||||||
# It is necessary to remove MinGW and StrawberryPerl as they both provide
|
|
||||||
# GCC. This causes issues because CMake prefers to use MSVC, while Meson
|
|
||||||
# uses GCC, if found, causing linking errors.
|
|
||||||
- name: Install dependencies on Windows
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
run: |
|
|
||||||
choco uninstall mingw strawberryperl --yes --all-versions --remove-dependencies --skip-autouninstaller --no-color
|
|
||||||
Remove-Item -Path C:\Strawberry -Recurse
|
|
||||||
choco install pkgconfiglite --yes --skip-virus-check --no-color
|
|
||||||
pip install meson ninja
|
|
||||||
Invoke-WebRequest -Uri https://github.com/google/googletest/archive/refs/heads/master.zip -OutFile googletest-master.zip
|
|
||||||
Expand-Archive -Path googletest-master.zip
|
|
||||||
cd googletest-master\googletest-master
|
|
||||||
cmake -S . -B build -DINSTALL_GTEST=ON -DBUILD_GMOCK=OFF -Dgtest_hide_internal_symbols=ON -DCMAKE_INSTALL_PREFIX=C:/googletest
|
|
||||||
cmake --build build --config=Release
|
|
||||||
cmake --install build --config=Release
|
|
||||||
cd ..\..
|
|
||||||
|
|
||||||
- name: Build and test
|
|
||||||
run: |
|
|
||||||
meson setup build -Dcpp-httplib_test=true -Dpkg_config_path=C:\googletest\lib\pkgconfig -Db_vscrt=static_from_buildtype
|
|
||||||
meson test --no-stdsplit --print-errorlogs -C build
|
|
||||||
@@ -13,10 +13,12 @@ example/*.pem
|
|||||||
test/httplib.cc
|
test/httplib.cc
|
||||||
test/httplib.h
|
test/httplib.h
|
||||||
test/test
|
test/test
|
||||||
|
test/server_fuzzer
|
||||||
test/test_proxy
|
test/test_proxy
|
||||||
test/test_split
|
test/test_split
|
||||||
test/test.xcodeproj/xcuser*
|
test/test.xcodeproj/xcuser*
|
||||||
test/test.xcodeproj/*/xcuser*
|
test/test.xcodeproj/*/xcuser*
|
||||||
|
test/*.o
|
||||||
test/*.pem
|
test/*.pem
|
||||||
test/*.srl
|
test/*.srl
|
||||||
|
|
||||||
|
|||||||
+1
-17
@@ -60,27 +60,11 @@
|
|||||||
]]
|
]]
|
||||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||||
|
|
||||||
# On systems without Git installed, there were errors since execute_process seemed to not throw an error without it?
|
|
||||||
find_package(Git QUIET)
|
|
||||||
if(Git_FOUND)
|
|
||||||
# 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_EXECUTABLE} describe --tags --abbrev=0
|
|
||||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
OUTPUT_VARIABLE _raw_version_string
|
|
||||||
ERROR_VARIABLE _git_tag_error
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 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 OR NOT Git_FOUND)
|
|
||||||
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
|
# Get the user agent and use it as a version
|
||||||
# This gets the string with the user agent from the header.
|
# This gets the string with the user agent from the header.
|
||||||
# This is so the maintainer doesn't actually need to update this manually.
|
# 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]+\.?)+")
|
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
||||||
endif()
|
|
||||||
# Needed since git tags have "v" prefixing them.
|
# Needed since git tags have "v" prefixing them.
|
||||||
# Also used if the fallback to user agent string is being used.
|
# Also used if the fallback to user agent string is being used.
|
||||||
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
|
|||||||
### Pre routing handler
|
### Pre routing handler
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||||
if (req.path == "/hello") {
|
if (req.path == "/hello") {
|
||||||
res.set_content("world", "text/html");
|
res.set_content("world", "text/html");
|
||||||
return Server::HandlerResponse::Handled;
|
return Server::HandlerResponse::Handled;
|
||||||
@@ -303,7 +303,7 @@ Without content length:
|
|||||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
"text/plain", // Content type
|
"text/plain", // Content type
|
||||||
[&](size_t offset, size_t length, DataSink &sink) {
|
[&](size_t offset, DataSink &sink) {
|
||||||
if (/* there is still data */) {
|
if (/* there is still data */) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
// prepare data...
|
// prepare data...
|
||||||
@@ -746,6 +746,12 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
|||||||
res->body; // Compressed data
|
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
|
Split httplib.h into .h and .cc
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
+9
-6
@@ -1,14 +1,17 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
#CXX = clang++
|
||||||
CXXFLAGS = -std=c++11 -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
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
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
|
||||||
|
|
||||||
@@ -47,4 +50,4 @@ pem:
|
|||||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||||
|
|
||||||
clean:
|
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
|
||||||
+1
-1
@@ -15,5 +15,5 @@ int main(void) {
|
|||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.listen("localhost", 8080);
|
svr.listen("0.0.0.0", 8080);
|
||||||
}
|
}
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
for i in {1..10000}
|
||||||
|
do
|
||||||
|
echo "#### $i ####"
|
||||||
|
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
|
||||||
|
done
|
||||||
+15
-16
@@ -19,23 +19,13 @@ project(
|
|||||||
# Check just in case downstream decides to edit the source
|
# Check just in case downstream decides to edit the source
|
||||||
# and add a project version
|
# and add a project version
|
||||||
version = meson.project_version()
|
version = meson.project_version()
|
||||||
if version == 'undefined'
|
python3 = find_program('python3')
|
||||||
git = find_program('git', required: false)
|
|
||||||
if git.found()
|
|
||||||
result = run_command(git, 'describe', '--tags', '--abbrev=0')
|
|
||||||
if result.returncode() == 0
|
|
||||||
version = result.stdout().strip('v\n')
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
python = import('python').find_installation('python3')
|
|
||||||
# If version is still undefined it means that the git method failed
|
|
||||||
if version == 'undefined'
|
if version == 'undefined'
|
||||||
# Meson doesn't have regular expressions, but since it is implemented
|
# Meson doesn't have regular expressions, but since it is implemented
|
||||||
# in python we can be sure we can use it to parse the file manually
|
# in python we can be sure we can use it to parse the file manually
|
||||||
version = run_command(
|
version = run_command(
|
||||||
python, '-c', 'import re; raw_version = re.search("User\-Agent.*cpp\-httplib/([0-9]+\.?)+", open("httplib.h").read()).group(0); print(re.search("([0-9]+\\.?)+", raw_version).group(0))'
|
python3, '-c', 'import re; raw_version = re.search("User\-Agent.*cpp\-httplib/([0-9]+\.?)+", open("httplib.h").read()).group(0); print(re.search("([0-9]+\\.?)+", raw_version).group(0))',
|
||||||
|
check: true
|
||||||
).stdout().strip()
|
).stdout().strip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -79,7 +69,7 @@ if get_option('cpp-httplib_compile')
|
|||||||
'split',
|
'split',
|
||||||
input: 'httplib.h',
|
input: 'httplib.h',
|
||||||
output: ['httplib.cc', 'httplib.h'],
|
output: ['httplib.cc', 'httplib.h'],
|
||||||
command: [python, files('split.py'), '--out', meson.current_build_dir()],
|
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: [false, get_option('includedir')]
|
install_dir: [false, get_option('includedir')]
|
||||||
)
|
)
|
||||||
@@ -91,17 +81,26 @@ if get_option('cpp-httplib_compile')
|
|||||||
version: version,
|
version: version,
|
||||||
install: true
|
install: true
|
||||||
)
|
)
|
||||||
cpp_httplib_dep = declare_dependency(link_with: lib, sources: httplib_ch[1])
|
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||||
|
|
||||||
import('pkgconfig').generate(
|
import('pkgconfig').generate(
|
||||||
lib,
|
lib,
|
||||||
description: 'A C++ HTTP/HTTPS server and client library',
|
description: 'A C++ HTTP/HTTPS server and client library',
|
||||||
|
extra_cflags: args,
|
||||||
url: 'https://github.com/yhirose/cpp-httplib',
|
url: 'https://github.com/yhirose/cpp-httplib',
|
||||||
version: version
|
version: version
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
install_headers('httplib.h')
|
install_headers('httplib.h')
|
||||||
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'), dependencies: deps)
|
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
|
endif
|
||||||
|
|
||||||
if meson.version().version_compare('>=0.54.0')
|
if meson.version().version_compare('>=0.54.0')
|
||||||
|
|||||||
+1
-1
@@ -5,5 +5,5 @@
|
|||||||
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
|
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_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
|
||||||
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
|
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
|
||||||
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires Python 3)')
|
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file')
|
||||||
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
||||||
+33
-7
@@ -1,20 +1,30 @@
|
|||||||
#CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
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
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||||
|
|
||||||
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
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
|
all : test test_split
|
||||||
./test
|
./test
|
||||||
# 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.
|
|
||||||
|
|
||||||
proxy : test_proxy
|
proxy : test_proxy
|
||||||
./test_proxy
|
./test_proxy
|
||||||
@@ -22,12 +32,28 @@ proxy : test_proxy
|
|||||||
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
||||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
|
$(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
|
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
|
||||||
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
||||||
|
|
||||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
|
$(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
|
httplib.cc : ../httplib.h
|
||||||
python3 ../split.py -o .
|
python3 ../split.py -o .
|
||||||
|
|
||||||
@@ -42,4 +68,4 @@ cert.pem:
|
|||||||
#c_rehash .
|
#c_rehash .
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f test test_proxy pem *.0 *.1 *.srl httplib.h httplib.cc
|
rm -f test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
|
||||||
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
|
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
|
||||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
|
||||||
all 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) $(CXXFLAGS) -o $@ $< $(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) $(CXXFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
PUT { HTTP/1.0
|
||||||
|
Content-Type:multipart/form-databoundary=m
|
||||||
|
Range:bytes=-
|
||||||
|
|
||||||
|
--m
|
||||||
|
C
|
||||||
|
c
Line truncated
|
||||||
@@ -20,12 +20,12 @@ int main(int argc, char **argv) {
|
|||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
std::ifstream in(argv[i]);
|
std::ifstream in(argv[i]);
|
||||||
in.seekg(0, in.end);
|
in.seekg(0, in.end);
|
||||||
size_t length = in.tellg();
|
size_t length = static_cast<size_t>(in.tellg());
|
||||||
in.seekg (0, in.beg);
|
in.seekg (0, in.beg);
|
||||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||||
std::vector<char> bytes(length);
|
std::vector<char> bytes(length);
|
||||||
in.read(bytes.data(), bytes.size());
|
in.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
|
||||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||||
bytes.size());
|
bytes.size());
|
||||||
std::cout << "Execution successful" << std::endl;
|
std::cout << "Execution successful" << std::endl;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
docker-compose down --rmi all
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
docker-compose up -d
|
|
||||||
+221
-8
@@ -37,8 +37,12 @@ MultipartFormData &get_file_value(MultipartFormDataItems &files,
|
|||||||
auto it = std::find_if(
|
auto it = std::find_if(
|
||||||
files.begin(), files.end(),
|
files.begin(), files.end(),
|
||||||
[&](const MultipartFormData &file) { return file.name == key; });
|
[&](const MultipartFormData &file) { return file.name == key; });
|
||||||
|
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
|
||||||
|
return *it;
|
||||||
|
#else
|
||||||
if (it != files.end()) { return *it; }
|
if (it != files.end()) { return *it; }
|
||||||
throw std::runtime_error("invalid mulitpart form data name error");
|
throw std::runtime_error("invalid mulitpart form data name error");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ConstructorTest, MoveConstructible) {
|
TEST(ConstructorTest, MoveConstructible) {
|
||||||
@@ -54,6 +58,14 @@ TEST(StartupTest, WSAStartup) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(DecodeURLTest, PercentCharacter) {
|
||||||
|
EXPECT_EQ(
|
||||||
|
detail::decode_url(
|
||||||
|
R"(descrip=Gastos%20%C3%A1%C3%A9%C3%AD%C3%B3%C3%BA%C3%B1%C3%91%206)",
|
||||||
|
false),
|
||||||
|
R"(descrip=Gastos áéíóúñÑ 6)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) {
|
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) {
|
||||||
string unescapedCharacters = "-_.!~*'()";
|
string unescapedCharacters = "-_.!~*'()";
|
||||||
|
|
||||||
@@ -171,6 +183,12 @@ TEST(GetHeaderValueTest, RegularValue) {
|
|||||||
EXPECT_STREQ("text/html", val);
|
EXPECT_STREQ("text/html", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GetHeaderValueTest, RegularValueWithDifferentCase) {
|
||||||
|
Headers headers = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}};
|
||||||
|
auto val = detail::get_header_value(headers, "content-type", 0, "text/plain");
|
||||||
|
EXPECT_STREQ("text/html", val);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(GetHeaderValueTest, SetContent) {
|
TEST(GetHeaderValueTest, SetContent) {
|
||||||
Response res;
|
Response res;
|
||||||
|
|
||||||
@@ -383,6 +401,31 @@ TEST(ChunkedEncodingTest, FromHTTPWatch_Online) {
|
|||||||
EXPECT_EQ(out, res->body);
|
EXPECT_EQ(out, res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(HostnameToIPConversionTest, HTTPWatch_Online) {
|
||||||
|
auto host = "www.httpwatch.com";
|
||||||
|
|
||||||
|
auto ip = hosted_at(host);
|
||||||
|
EXPECT_EQ("191.236.16.12", ip);
|
||||||
|
|
||||||
|
std::vector<std::string> addrs;
|
||||||
|
hosted_at(host, addrs);
|
||||||
|
EXPECT_EQ(1u, addrs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 // It depends on each test environment...
|
||||||
|
TEST(HostnameToIPConversionTest, YouTube_Online) {
|
||||||
|
auto host = "www.youtube.com";
|
||||||
|
|
||||||
|
std::vector<std::string> addrs;
|
||||||
|
hosted_at(host, addrs);
|
||||||
|
|
||||||
|
EXPECT_EQ(20u, addrs.size());
|
||||||
|
|
||||||
|
auto it = std::find(addrs.begin(), addrs.end(), "2607:f8b0:4006:809::200e");
|
||||||
|
EXPECT_TRUE(it != addrs.end());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
|
TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
|
||||||
auto host = "www.httpwatch.com";
|
auto host = "www.httpwatch.com";
|
||||||
|
|
||||||
@@ -567,7 +610,7 @@ TEST(ConnectionErrorTest, InvalidPort) {
|
|||||||
EXPECT_EQ(Error::Connection, res.error());
|
EXPECT_EQ(Error::Connection, res.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ConnectionErrorTest, Timeout) {
|
TEST(ConnectionErrorTest, Timeout_Online) {
|
||||||
auto host = "google.com";
|
auto host = "google.com";
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
@@ -579,9 +622,14 @@ TEST(ConnectionErrorTest, Timeout) {
|
|||||||
#endif
|
#endif
|
||||||
cli.set_connection_timeout(std::chrono::seconds(2));
|
cli.set_connection_timeout(std::chrono::seconds(2));
|
||||||
|
|
||||||
|
// only probe one address type so that the error reason
|
||||||
|
// correlates to the timed-out IPv4, not the unsupported
|
||||||
|
// IPv6 connection attempt
|
||||||
|
cli.set_address_family(AF_INET);
|
||||||
|
|
||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(!res);
|
ASSERT_TRUE(!res);
|
||||||
EXPECT_TRUE(res.error() == Error::Connection);
|
EXPECT_EQ(Error::ConnectionTimeout, res.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CancelTest, NoCancel_Online) {
|
TEST(CancelTest, NoCancel_Online) {
|
||||||
@@ -736,6 +784,39 @@ TEST(DigestAuthTest, FromHTTPWatch_Online) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(SpecifyServerIPAddressTest, AnotherHostname_Online) {
|
||||||
|
auto host = "google.com";
|
||||||
|
auto another_host = "example.com";
|
||||||
|
auto wrong_ip = "0.0.0.0";
|
||||||
|
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
SSLClient cli(host);
|
||||||
|
#else
|
||||||
|
Client cli(host);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cli.set_hostname_addr_map({{another_host, wrong_ip}});
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
ASSERT_EQ(301, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpecifyServerIPAddressTest, RealHostname_Online) {
|
||||||
|
auto host = "google.com";
|
||||||
|
auto wrong_ip = "0.0.0.0";
|
||||||
|
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
SSLClient cli(host);
|
||||||
|
#else
|
||||||
|
Client cli(host);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cli.set_hostname_addr_map({{host, wrong_ip}});
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(!res);
|
||||||
|
EXPECT_EQ(Error::Connection, res.error());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(AbsoluteRedirectTest, Redirect_Online) {
|
TEST(AbsoluteRedirectTest, Redirect_Online) {
|
||||||
auto host = "nghttp2.org";
|
auto host = "nghttp2.org";
|
||||||
|
|
||||||
@@ -1148,14 +1229,16 @@ TEST(ErrorHandlerTest, ContentLength) {
|
|||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||||
TEST(ExceptionHandlerTest, ContentLength) {
|
TEST(ExceptionHandlerTest, ContentLength) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
svr.set_exception_handler(
|
svr.set_exception_handler([](const Request & /*req*/, Response &res,
|
||||||
[](const Request & /*req*/, Response &res, std::exception & /*e*/) {
|
std::exception &e) {
|
||||||
|
EXPECT_EQ("abc", std::string(e.what()));
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||||
"text/html"); // <= Content-Length still 13
|
"text/html"); // <= Content-Length still 13 at this point
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||||
@@ -1168,9 +1251,10 @@ TEST(ExceptionHandlerTest, ContentLength) {
|
|||||||
// Give GET time to get a few messages.
|
// Give GET time to get a few messages.
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
{
|
for (size_t i = 0; i < 10; i++) {
|
||||||
Client cli(HOST, PORT);
|
Client cli(HOST, PORT);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < 100; j++) {
|
||||||
auto res = cli.Get("/hi");
|
auto res = cli.Get("/hi");
|
||||||
ASSERT_TRUE(res);
|
ASSERT_TRUE(res);
|
||||||
EXPECT_EQ(500, res->status);
|
EXPECT_EQ(500, res->status);
|
||||||
@@ -1179,10 +1263,23 @@ TEST(ExceptionHandlerTest, ContentLength) {
|
|||||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cli.set_keep_alive(true);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < 100; j++) {
|
||||||
|
auto res = cli.Get("/hi");
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(500, res->status);
|
||||||
|
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
||||||
|
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svr.stop();
|
svr.stop();
|
||||||
thread.join();
|
thread.join();
|
||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(NoContentTest, ContentLength) {
|
TEST(NoContentTest, ContentLength) {
|
||||||
Server svr;
|
Server svr;
|
||||||
@@ -1571,6 +1668,11 @@ protected:
|
|||||||
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||||
res.set_content("empty-no-content-type", "text/plain");
|
res.set_content("empty-no-content-type", "text/plain");
|
||||||
})
|
})
|
||||||
|
.Post("/post-large",
|
||||||
|
[&](const Request &req, Response &res) {
|
||||||
|
EXPECT_EQ(req.body, LARGE_DATA);
|
||||||
|
res.set_content(req.body, "text/plain");
|
||||||
|
})
|
||||||
.Put("/empty-no-content-type",
|
.Put("/empty-no-content-type",
|
||||||
[&](const Request &req, Response &res) {
|
[&](const Request &req, Response &res) {
|
||||||
EXPECT_EQ(req.body, "");
|
EXPECT_EQ(req.body, "");
|
||||||
@@ -1976,6 +2078,13 @@ TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
|
|||||||
ASSERT_EQ("empty-no-content-type", res->body);
|
ASSERT_EQ("empty-no-content-type", res->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, PostLarge) {
|
||||||
|
auto res = cli_.Post("/post-large", LARGE_DATA, "text/plain");
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
ASSERT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ(LARGE_DATA, res->body);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
|
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
|
||||||
auto res = cli_.Put("/empty-no-content-type");
|
auto res = cli_.Put("/empty-no-content-type");
|
||||||
ASSERT_TRUE(res);
|
ASSERT_TRUE(res);
|
||||||
@@ -3321,7 +3430,7 @@ static bool send_request(time_t read_timeout_sec, const std::string &req,
|
|||||||
auto error = Error::Success;
|
auto error = Error::Success;
|
||||||
|
|
||||||
auto client_sock = detail::create_client_socket(
|
auto client_sock = detail::create_client_socket(
|
||||||
HOST, PORT, AF_UNSPEC, false, nullptr,
|
HOST, "", PORT, AF_UNSPEC, false, nullptr,
|
||||||
/*connection_timeout_sec=*/5, 0,
|
/*connection_timeout_sec=*/5, 0,
|
||||||
/*read_timeout_sec=*/5, 0,
|
/*read_timeout_sec=*/5, 0,
|
||||||
/*write_timeout_sec=*/5, 0, std::string(), error);
|
/*write_timeout_sec=*/5, 0, std::string(), error);
|
||||||
@@ -3580,7 +3689,8 @@ TEST(StreamingTest, NoContentLengthStreaming) {
|
|||||||
|
|
||||||
auto get_thread = std::thread([&client]() {
|
auto get_thread = std::thread([&client]() {
|
||||||
std::string s;
|
std::string s;
|
||||||
auto res = client.Get("/stream", [&s](const char *data, size_t len) -> bool {
|
auto res =
|
||||||
|
client.Get("/stream", [&s](const char *data, size_t len) -> bool {
|
||||||
s += std::string(data, len);
|
s += std::string(data, len);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -3642,6 +3752,7 @@ TEST(MountTest, Unmount) {
|
|||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||||
TEST(ExceptionTest, ThrowExceptionInHandler) {
|
TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
@@ -3670,6 +3781,7 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
|
|||||||
listen_thread.join();
|
listen_thread.join();
|
||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(KeepAliveTest, ReadTimeout) {
|
TEST(KeepAliveTest, ReadTimeout) {
|
||||||
Server svr;
|
Server svr;
|
||||||
@@ -3709,6 +3821,36 @@ TEST(KeepAliveTest, ReadTimeout) {
|
|||||||
ASSERT_FALSE(svr.is_running());
|
ASSERT_FALSE(svr.is_running());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(KeepAliveTest, Issue1041) {
|
||||||
|
const auto resourcePath = "/hi";
|
||||||
|
|
||||||
|
Server svr;
|
||||||
|
svr.set_keep_alive_timeout(3);
|
||||||
|
|
||||||
|
svr.Get(resourcePath, [](const httplib::Request &, httplib::Response &res) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto a2 = std::async(std::launch::async, [&svr] { svr.listen(HOST, PORT); });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
|
||||||
|
Client cli(HOST, PORT);
|
||||||
|
cli.set_keep_alive(true);
|
||||||
|
|
||||||
|
auto result = cli.Get(resourcePath);
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
EXPECT_EQ(200, result->status);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
|
|
||||||
|
result = cli.Get(resourcePath);
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
EXPECT_EQ(200, result->status);
|
||||||
|
|
||||||
|
svr.stop();
|
||||||
|
a2.wait();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ClientProblemDetectionTest, ContentProvider) {
|
TEST(ClientProblemDetectionTest, ContentProvider) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
@@ -4392,6 +4534,75 @@ TEST(SSLClientServerTest, SSLConnectTimeout) {
|
|||||||
svr.stop();
|
svr.stop();
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SSLClientServerTest, CustomizeServerSSLCtx) {
|
||||||
|
auto setup_ssl_ctx_callback = [](SSL_CTX &ssl_ctx) {
|
||||||
|
SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||||
|
SSL_CTX_set_options(&ssl_ctx,
|
||||||
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
|
SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_SSLv2);
|
||||||
|
SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_SSLv3);
|
||||||
|
SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_TLSv1);
|
||||||
|
SSL_CTX_set_options(&ssl_ctx, SSL_OP_NO_TLSv1_1);
|
||||||
|
auto ciphers = "ECDHE-RSA-AES128-SHA256:"
|
||||||
|
"ECDHE-DSS-AES128-SHA256:"
|
||||||
|
"ECDHE-RSA-AES256-SHA256:"
|
||||||
|
"ECDHE-DSS-AES256-SHA256:";
|
||||||
|
SSL_CTX_set_cipher_list(&ssl_ctx, ciphers);
|
||||||
|
if (SSL_CTX_use_certificate_chain_file(&ssl_ctx, SERVER_CERT_FILE) != 1 ||
|
||||||
|
SSL_CTX_use_PrivateKey_file(&ssl_ctx, SERVER_PRIVATE_KEY_FILE,
|
||||||
|
SSL_FILETYPE_PEM) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SSL_CTX_load_verify_locations(&ssl_ctx, CLIENT_CA_CERT_FILE,
|
||||||
|
CLIENT_CA_CERT_DIR);
|
||||||
|
SSL_CTX_set_verify(
|
||||||
|
&ssl_ctx,
|
||||||
|
SSL_VERIFY_PEER |
|
||||||
|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
|
||||||
|
nullptr);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
SSLServer svr(setup_ssl_ctx_callback);
|
||||||
|
ASSERT_TRUE(svr.is_valid());
|
||||||
|
|
||||||
|
svr.Get("/test", [&](const Request &req, Response &res) {
|
||||||
|
res.set_content("test", "text/plain");
|
||||||
|
svr.stop();
|
||||||
|
ASSERT_TRUE(true);
|
||||||
|
|
||||||
|
auto peer_cert = SSL_get_peer_certificate(req.ssl);
|
||||||
|
ASSERT_TRUE(peer_cert != nullptr);
|
||||||
|
|
||||||
|
auto subject_name = X509_get_subject_name(peer_cert);
|
||||||
|
ASSERT_TRUE(subject_name != nullptr);
|
||||||
|
|
||||||
|
std::string common_name;
|
||||||
|
{
|
||||||
|
char name[BUFSIZ];
|
||||||
|
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
||||||
|
name, sizeof(name));
|
||||||
|
common_name.assign(name, static_cast<size_t>(name_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ("Common Name", common_name);
|
||||||
|
|
||||||
|
X509_free(peer_cert);
|
||||||
|
});
|
||||||
|
|
||||||
|
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
|
||||||
|
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||||
|
cli.enable_server_certificate_verification(false);
|
||||||
|
cli.set_connection_timeout(30);
|
||||||
|
|
||||||
|
auto res = cli.Get("/test");
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
ASSERT_EQ(200, res->status);
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -4407,9 +4618,11 @@ TEST(NoSSLSupport, SimpleInterface) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||||
TEST(InvalidScheme, SimpleInterface) {
|
TEST(InvalidScheme, SimpleInterface) {
|
||||||
ASSERT_ANY_THROW(Client cli("scheme://yahoo.com"));
|
ASSERT_ANY_THROW(Client cli("scheme://yahoo.com"));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(NoScheme, SimpleInterface) {
|
TEST(NoScheme, SimpleInterface) {
|
||||||
Client cli("yahoo.com:80");
|
Client cli("yahoo.com:80");
|
||||||
|
|||||||
Reference in new issue
Block a user