mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -26,7 +26,7 @@ jobs:
|
||||
run: brew install brotli
|
||||
- name: make
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: cd test && make
|
||||
run: cd test && make -j2
|
||||
- name: check fuzz test target
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cd test && make -f Makefile.fuzz_test
|
||||
@@ -39,3 +39,55 @@ jobs:
|
||||
cd test
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
||||
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
|
||||
|
||||
@@ -10,8 +10,11 @@ example/redirect
|
||||
example/sse*
|
||||
example/upload
|
||||
example/*.pem
|
||||
test/httplib.cc
|
||||
test/httplib.h
|
||||
test/test
|
||||
test/test_proxy
|
||||
test/test_split
|
||||
test/test.xcodeproj/xcuser*
|
||||
test/test.xcodeproj/*/xcuser*
|
||||
test/*.pem
|
||||
|
||||
@@ -53,6 +53,33 @@ res->body;
|
||||
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
|
||||
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
|
||||
|
||||
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.
|
||||
|
||||
```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
|
||||
|
||||
// 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
|
||||
------
|
||||
|
||||
@@ -229,6 +256,7 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
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) {
|
||||
@@ -245,7 +273,6 @@ svr.Post("/content_receiver",
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -719,39 +746,29 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res->body; // Compressed data
|
||||
```
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
Use `poll` instead of `select`
|
||||
------------------------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
`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`.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
|
||||
```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");
|
||||
|
||||
// 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.
|
||||
|
||||
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
|
||||
@@ -776,6 +793,8 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
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 and Cygwin on Windows are not supported.
|
||||
|
||||
License
|
||||
|
||||
+108
-2
@@ -1,7 +1,113 @@
|
||||
project('cpp-httplib', 'cpp', license: 'MIT')
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'))
|
||||
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'
|
||||
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'
|
||||
# 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
|
||||
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))'
|
||||
).stdout().strip()
|
||||
endif
|
||||
|
||||
message('cpp-httplib version ' + version)
|
||||
|
||||
deps = [dependency('threads')]
|
||||
args = []
|
||||
|
||||
openssl_dep = dependency('openssl', version: ['>=1.1.1', '<1.1.2'], required: get_option('cpp-httplib_openssl'))
|
||||
if openssl_dep.found()
|
||||
deps += openssl_dep
|
||||
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||
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')
|
||||
httplib_ch = custom_target(
|
||||
'split',
|
||||
input: 'httplib.h',
|
||||
output: ['httplib.cc', 'httplib.h'],
|
||||
command: [python, 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,
|
||||
install: true
|
||||
)
|
||||
cpp_httplib_dep = declare_dependency(link_with: lib, sources: httplib_ch[1])
|
||||
|
||||
import('pkgconfig').generate(
|
||||
lib,
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
else
|
||||
install_headers('httplib.h')
|
||||
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'), dependencies: deps)
|
||||
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,9 @@
|
||||
# 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_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires Python 3)')
|
||||
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))
|
||||
|
||||
+16
-6
@@ -1,5 +1,5 @@
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
@@ -9,17 +9,27 @@ 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
|
||||
|
||||
all : test
|
||||
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
|
||||
all : test test_split
|
||||
./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
|
||||
./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)
|
||||
|
||||
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)
|
||||
|
||||
httplib.cc : ../httplib.h
|
||||
python3 ../split.py -o .
|
||||
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
@@ -32,4 +42,4 @@ cert.pem:
|
||||
#c_rehash .
|
||||
|
||||
clean:
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl httplib.h httplib.cc
|
||||
|
||||
+5790
-2421
File diff suppressed because it is too large
Load Diff
+6845
-14039
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,97 @@
|
||||
# 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']
|
||||
)
|
||||
|
||||
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,
|
||||
rootca_key_pem,
|
||||
rootca_cert_pem,
|
||||
client_key_pem,
|
||||
client_cert_pem
|
||||
],
|
||||
workdir: meson.current_build_dir(),
|
||||
timeout: 300
|
||||
)
|
||||
+331
-128
@@ -8,6 +8,7 @@
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_CERT2_FILE "./cert2.pem"
|
||||
@@ -40,6 +41,11 @@ MultipartFormData &get_file_value(MultipartFormDataItems &files,
|
||||
throw std::runtime_error("invalid mulitpart form data name error");
|
||||
}
|
||||
|
||||
TEST(ConstructorTest, MoveConstructible) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<Client>::value);
|
||||
EXPECT_TRUE(std::is_nothrow_move_constructible<Client>::value);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(StartupTest, WSAStartup) {
|
||||
WSADATA wsaData;
|
||||
@@ -172,7 +178,7 @@ TEST(GetHeaderValueTest, SetContent) {
|
||||
EXPECT_EQ("text/html", res.get_header_value("Content-Type"));
|
||||
|
||||
res.set_content("text", "text/plain");
|
||||
EXPECT_EQ(1, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res.get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
}
|
||||
|
||||
@@ -354,7 +360,7 @@ TEST(BufferStreamTest, read) {
|
||||
EXPECT_EQ(0, strm.read(buf, 1));
|
||||
}
|
||||
|
||||
TEST(ChunkedEncodingTest, FromHTTPWatch) {
|
||||
TEST(ChunkedEncodingTest, FromHTTPWatch_Online) {
|
||||
auto host = "www.httpwatch.com";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -377,7 +383,7 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
|
||||
EXPECT_EQ(out, res->body);
|
||||
}
|
||||
|
||||
TEST(ChunkedEncodingTest, WithContentReceiver) {
|
||||
TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
|
||||
auto host = "www.httpwatch.com";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -405,7 +411,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
|
||||
EXPECT_EQ(out, body);
|
||||
}
|
||||
|
||||
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
||||
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver_Online) {
|
||||
auto host = "www.httpwatch.com";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -437,7 +443,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
||||
EXPECT_EQ(out, body);
|
||||
}
|
||||
|
||||
TEST(RangeTest, FromHTTPBin) {
|
||||
TEST(RangeTest, FromHTTPBin_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -542,7 +548,7 @@ TEST(ConnectionErrorTest, InvalidHostCheckResultErrorToString) {
|
||||
ASSERT_TRUE(!res);
|
||||
stringstream s;
|
||||
s << "error code: " << res.error();
|
||||
EXPECT_EQ("error code: 2", s.str());
|
||||
EXPECT_EQ("error code: Connection (2)", s.str());
|
||||
}
|
||||
|
||||
TEST(ConnectionErrorTest, InvalidPort) {
|
||||
@@ -578,7 +584,7 @@ TEST(ConnectionErrorTest, Timeout) {
|
||||
EXPECT_TRUE(res.error() == Error::Connection);
|
||||
}
|
||||
|
||||
TEST(CancelTest, NoCancel) {
|
||||
TEST(CancelTest, NoCancel_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -596,7 +602,7 @@ TEST(CancelTest, NoCancel) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(CancelTest, WithCancelSmallPayload) {
|
||||
TEST(CancelTest, WithCancelSmallPayload_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -613,7 +619,7 @@ TEST(CancelTest, WithCancelSmallPayload) {
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST(CancelTest, WithCancelLargePayload) {
|
||||
TEST(CancelTest, WithCancelLargePayload_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -632,7 +638,7 @@ TEST(CancelTest, WithCancelLargePayload) {
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST(BaseAuthTest, FromHTTPWatch) {
|
||||
TEST(BaseAuthTest, FromHTTPWatch_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -683,7 +689,7 @@ TEST(BaseAuthTest, FromHTTPWatch) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
TEST(DigestAuthTest, FromHTTPWatch_Online) {
|
||||
auto host = "httpbin.org";
|
||||
auto port = 443;
|
||||
SSLClient cli(host, port);
|
||||
@@ -730,7 +736,7 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AbsoluteRedirectTest, Redirect) {
|
||||
TEST(AbsoluteRedirectTest, Redirect_Online) {
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -745,7 +751,7 @@ TEST(AbsoluteRedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, Redirect) {
|
||||
TEST(RedirectTest, Redirect_Online) {
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -760,7 +766,7 @@ TEST(RedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RelativeRedirectTest, Redirect) {
|
||||
TEST(RelativeRedirectTest, Redirect_Online) {
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -775,7 +781,7 @@ TEST(RelativeRedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(TooManyRedirectTest, Redirect) {
|
||||
TEST(TooManyRedirectTest, Redirect_Online) {
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -791,7 +797,7 @@ TEST(TooManyRedirectTest, Redirect) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest, Redirect) {
|
||||
TEST(YahooRedirectTest, Redirect_Online) {
|
||||
Client cli("yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
@@ -805,7 +811,7 @@ TEST(YahooRedirectTest, Redirect) {
|
||||
EXPECT_EQ("https://yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
TEST(HttpsToHttpRedirectTest, Redirect_Online) {
|
||||
SSLClient cli("nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get(
|
||||
@@ -814,7 +820,7 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest2, Redirect) {
|
||||
TEST(HttpsToHttpRedirectTest2, Redirect_Online) {
|
||||
SSLClient cli("nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
@@ -827,7 +833,7 @@ TEST(HttpsToHttpRedirectTest2, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest3, Redirect) {
|
||||
TEST(HttpsToHttpRedirectTest3, Redirect_Online) {
|
||||
SSLClient cli("nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
@@ -839,53 +845,62 @@ TEST(HttpsToHttpRedirectTest3, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr8080;
|
||||
Server svr8081;
|
||||
|
||||
svr8080.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("http://localhost:8081/2");
|
||||
});
|
||||
|
||||
svr8081.Get("/2", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
|
||||
|
||||
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
|
||||
|
||||
while (!svr8080.is_running() || !svr8081.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", 8080);
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/1");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
|
||||
svr8080.stop();
|
||||
svr8081.stop();
|
||||
thread8080.join();
|
||||
thread8081.join();
|
||||
ASSERT_FALSE(svr8080.is_running());
|
||||
ASSERT_FALSE(svr8081.is_running());
|
||||
}
|
||||
|
||||
TEST(UrlWithSpace, Redirect) {
|
||||
TEST(UrlWithSpace, Redirect_Online) {
|
||||
SSLClient cli("edge.forgecdn.net");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(18527, res->get_header_value<uint64_t>("Content-Length"));
|
||||
EXPECT_EQ(18527U, res->get_header_value<uint64_t>("Content-Length"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr1;
|
||||
svr1.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
int svr1_port = 0;
|
||||
auto thread1 = std::thread([&]() {
|
||||
svr1_port = svr1.bind_to_any_port("localhost");
|
||||
svr1.listen_after_bind();
|
||||
});
|
||||
|
||||
Server svr2;
|
||||
svr2.Get("/2", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("http://localhost:" + std::to_string(svr1_port) + "/1");
|
||||
});
|
||||
|
||||
int svr2_port = 0;
|
||||
auto thread2 = std::thread([&]() {
|
||||
svr2_port = svr2.bind_to_any_port("localhost");
|
||||
svr2.listen_after_bind();
|
||||
});
|
||||
|
||||
while (!svr1.is_running() || !svr2.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", svr2_port);
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/2");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
|
||||
svr1.stop();
|
||||
svr2.stop();
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
ASSERT_FALSE(svr1.is_running());
|
||||
ASSERT_FALSE(svr2.is_running());
|
||||
}
|
||||
|
||||
TEST(RedirectFromPageWithContent, Redirect) {
|
||||
@@ -943,7 +958,72 @@ TEST(RedirectFromPageWithContent, Redirect) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#endif
|
||||
TEST(RedirectFromPageWithContentIP6, Redirect) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("___", "text/plain");
|
||||
// res.set_redirect("/2");
|
||||
res.set_redirect("http://[::1]:1234/2");
|
||||
});
|
||||
|
||||
svr.Get("/2", [&](const Request &req, Response &res) {
|
||||
auto host_header = req.headers.find("Host");
|
||||
ASSERT_TRUE(host_header != req.headers.end());
|
||||
EXPECT_EQ("[::1]:1234", host_header->second);
|
||||
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto th = std::thread([&]() { svr.listen("::1", 1234); });
|
||||
|
||||
// When IPV6 support isn't available svr.listen("::1", 1234) never
|
||||
// actually starts anything, so the condition !svr.is_running() will
|
||||
// always remain true, and the loop never stops.
|
||||
// This basically counts how many milliseconds have passed since the
|
||||
// call to svr.listen(), and if after 5 seconds nothing started yet
|
||||
// aborts the test.
|
||||
for (unsigned int milliseconds = 0; !svr.is_running(); milliseconds++) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
ASSERT_LT(milliseconds, 5000U);
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli("http://[::1]:1234");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", body);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli("http://[::1]:1234");
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(302, res->status);
|
||||
EXPECT_EQ("___", body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
th.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(PathUrlEncodeTest, PathUrlEncode) {
|
||||
Server svr;
|
||||
@@ -1166,9 +1246,9 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Routing Handler", res->body);
|
||||
EXPECT_EQ(1, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("PRE_ROUTING"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("POST_ROUTING"));
|
||||
}
|
||||
|
||||
@@ -1179,8 +1259,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!\n", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1190,8 +1270,8 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(404, res->status);
|
||||
EXPECT_EQ("Error", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0U, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
@@ -1250,31 +1330,31 @@ protected:
|
||||
.Get("/http_response_splitting",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("a", "1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a", "1\rSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\r\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\rb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_header("a\nb", "0");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("a"));
|
||||
|
||||
res.set_redirect("1\r\nSet-Cookie: a=1");
|
||||
EXPECT_EQ(0, res.headers.size());
|
||||
EXPECT_EQ(0U, res.headers.size());
|
||||
EXPECT_FALSE(res.has_header("Location"));
|
||||
})
|
||||
.Get("/slow",
|
||||
@@ -1282,11 +1362,13 @@ protected:
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
res.set_content("slow", "text/plain");
|
||||
})
|
||||
#if 0
|
||||
.Post("/slowpost",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
res.set_content("slow", "text/plain");
|
||||
})
|
||||
#endif
|
||||
.Get("/remote_addr",
|
||||
[&](const Request &req, Response &res) {
|
||||
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
|
||||
@@ -1592,7 +1674,7 @@ protected:
|
||||
} else {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
EXPECT_EQ(data_length, 7);
|
||||
EXPECT_EQ(7U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -1640,7 +1722,7 @@ protected:
|
||||
})
|
||||
.Post("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1648,7 +1730,7 @@ protected:
|
||||
})
|
||||
.Put("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1656,7 +1738,7 @@ protected:
|
||||
})
|
||||
.Patch("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1664,7 +1746,7 @@ protected:
|
||||
})
|
||||
.Delete("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ(4U, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
@@ -1744,7 +1826,7 @@ TEST_F(ServerTest, GetMethod200) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("OK", res->reason);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
@@ -1754,7 +1836,7 @@ TEST_F(ServerTest, GetMethod200withPercentEncoding) {
|
||||
EXPECT_EQ("HTTP/1.1", res->version);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ(1U, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
@@ -2032,25 +2114,25 @@ TEST_F(ServerTest, Binary) {
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, BinaryString) {
|
||||
@@ -2059,22 +2141,22 @@ TEST_F(ServerTest, BinaryString) {
|
||||
auto res = cli_.Post("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
ASSERT_EQ(4U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, EmptyRequest) {
|
||||
@@ -2390,7 +2472,7 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("269", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(269, res->body.size());
|
||||
EXPECT_EQ(269U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedEndless) {
|
||||
@@ -2477,7 +2559,7 @@ TEST_F(ServerTest, GetWithRangeMultipart) {
|
||||
EXPECT_EQ(206, res->status);
|
||||
EXPECT_EQ("269", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(false, res->has_header("Content-Range"));
|
||||
EXPECT_EQ(269, res->body.size());
|
||||
EXPECT_EQ(269U, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRangeMultipartOffsetGreaterThanContent) {
|
||||
@@ -2556,6 +2638,7 @@ TEST_F(ServerTest, SlowRequest) {
|
||||
std::thread([=]() { auto res = cli_.Get("/slow"); }));
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST_F(ServerTest, SlowPost) {
|
||||
char buffer[64 * 1024];
|
||||
memset(buffer, 0x42, sizeof(buffer));
|
||||
@@ -2589,6 +2672,7 @@ TEST_F(ServerTest, SlowPostFail) {
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Write, res.error());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(ServerTest, Put) {
|
||||
auto res = cli_.Put("/put", "PUT", "text/plain");
|
||||
@@ -2717,10 +2801,12 @@ TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip2) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
Client cli("https://localhost:1234");
|
||||
std::string s = std::string("https://") + HOST + ":" + std::to_string(PORT);
|
||||
Client cli(s.c_str());
|
||||
cli.enable_server_certificate_verification(false);
|
||||
#else
|
||||
Client cli("http://localhost:1234");
|
||||
std::string s = std::string("http://") + HOST + ":" + std::to_string(PORT);
|
||||
Client cli(s.c_str());
|
||||
#endif
|
||||
cli.set_compress(true);
|
||||
|
||||
@@ -2806,6 +2892,54 @@ TEST(GzipDecompressor, ChunkedDecompression) {
|
||||
}
|
||||
ASSERT_EQ(data, decompressed_data);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(GzipDecompressor, LargeRandomData) {
|
||||
|
||||
// prepare large random data that is difficult to be compressed and is
|
||||
// expected to have large size even when compressed
|
||||
std::random_device seed_gen;
|
||||
std::mt19937 random(seed_gen());
|
||||
constexpr auto large_size_byte = 4294967296UL; // 4GiB
|
||||
constexpr auto data_size = large_size_byte + 134217728UL; // + 128MiB
|
||||
std::vector<std::uint32_t> data(data_size / sizeof(std::uint32_t));
|
||||
std::generate(data.begin(), data.end(), [&]() { return random(); });
|
||||
|
||||
// compress data over 4GiB
|
||||
std::string compressed_data;
|
||||
compressed_data.reserve(large_size_byte + 536870912UL); // + 512MiB reserved
|
||||
httplib::detail::gzip_compressor compressor;
|
||||
auto result = compressor.compress(reinterpret_cast<const char *>(data.data()),
|
||||
data.size() * sizeof(std::uint32_t), true,
|
||||
[&](const char *data, size_t size) {
|
||||
compressed_data.insert(
|
||||
compressed_data.size(), data, size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// FIXME: compressed data size is expected to be greater than 4GiB,
|
||||
// but there is no guarantee
|
||||
// ASSERT_TRUE(compressed_data.size() >= large_size_byte);
|
||||
|
||||
// decompress data over 4GiB
|
||||
std::string decompressed_data;
|
||||
decompressed_data.reserve(data_size);
|
||||
httplib::detail::gzip_decompressor decompressor;
|
||||
result = decompressor.decompress(
|
||||
compressed_data.data(), compressed_data.size(),
|
||||
[&](const char *data, size_t size) {
|
||||
decompressed_data.insert(decompressed_data.size(), data, size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
// compare
|
||||
ASSERT_EQ(data_size, decompressed_data.size());
|
||||
ASSERT_TRUE(std::memcmp(data.data(), decompressed_data.data(), data_size) ==
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
@@ -3066,7 +3200,7 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/compress", headers,
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3092,14 +3226,14 @@ TEST_F(ServerTest, GzipWithoutDecompressing) {
|
||||
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("33", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ(33, res->body.size());
|
||||
EXPECT_EQ(33U, res->body.size());
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/compress", [&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3135,7 +3269,7 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
|
||||
std::string body;
|
||||
auto res = cli_.Get("/nocompress", headers,
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
EXPECT_EQ(100U, data_length);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -3231,14 +3365,14 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||
// Only space and horizontal tab are whitespace. Make sure other whitespace-
|
||||
// like characters are not treated the same - use vertical tab and escape.
|
||||
const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n"
|
||||
"foo: \t \v bar \e\t \r\n"
|
||||
"foo: \t \v bar \x1B\t \r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
ASSERT_TRUE(send_request(5, req));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_EQ(header_value, "\v bar \e");
|
||||
EXPECT_EQ(header_value, "\v bar \x1B");
|
||||
}
|
||||
|
||||
// Sends a raw request and verifies that there isn't a crash or exception.
|
||||
@@ -3390,7 +3524,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||
res.set_chunked_content_provider("text/event-stream", [](size_t offset,
|
||||
DataSink &sink) {
|
||||
char buffer[27];
|
||||
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
|
||||
auto size = static_cast<size_t>(sprintf(buffer, "data:%zd\n\n", offset));
|
||||
auto ret = sink.write(buffer, size);
|
||||
EXPECT_TRUE(ret);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
@@ -3445,10 +3579,12 @@ TEST(StreamingTest, NoContentLengthStreaming) {
|
||||
Client client(HOST, PORT);
|
||||
|
||||
auto get_thread = std::thread([&client]() {
|
||||
auto res = client.Get("/stream", [](const char *data, size_t len) -> bool {
|
||||
EXPECT_EQ("aaabbb", std::string(data, len));
|
||||
std::string s;
|
||||
auto res = client.Get("/stream", [&s](const char *data, size_t len) -> bool {
|
||||
s += std::string(data, len);
|
||||
return true;
|
||||
});
|
||||
EXPECT_EQ("aaabbb", s);
|
||||
});
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
@@ -3647,26 +3783,60 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
|
||||
TEST(GetWithParametersTest, GetWithParameters) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [&](const Request &req, Response &res) {
|
||||
auto text = req.get_param_value("hello");
|
||||
res.set_content(text, "text/plain");
|
||||
svr.Get("/", [&](const Request &req, Response &) {
|
||||
EXPECT_EQ("world", req.get_param_value("hello"));
|
||||
EXPECT_EQ("world2", req.get_param_value("hello2"));
|
||||
EXPECT_EQ("world3", req.get_param_value("hello3"));
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
svr.Get("/params", [&](const Request &req, Response &) {
|
||||
EXPECT_EQ("world", req.get_param_value("hello"));
|
||||
EXPECT_EQ("world2", req.get_param_value("hello2"));
|
||||
EXPECT_EQ("world3", req.get_param_value("hello3"));
|
||||
});
|
||||
|
||||
svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request &req, Response &) {
|
||||
EXPECT_EQ("resource-id", req.matches[1]);
|
||||
EXPECT_EQ("foo", req.get_param_value("param1"));
|
||||
EXPECT_EQ("bar", req.get_param_value("param2"));
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", PORT);
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
auto res = cli.Get("/", params, Headers{});
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
params.emplace("hello2", "world2");
|
||||
params.emplace("hello3", "world3");
|
||||
auto res = cli.Get("/", params, Headers{});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("world", res->body);
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/params?hello=world&hello2=world2&hello3=world3");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/resources/resource-id?param1=foo¶m2=bar");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
@@ -3708,7 +3878,7 @@ TEST(GetWithParametersTest, GetWithParameters2) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(ClientDefaultHeadersTest, DefaultHeaders) {
|
||||
TEST(ClientDefaultHeadersTest, DefaultHeaders_Online) {
|
||||
Client cli("httpbin.org");
|
||||
cli.set_default_headers({make_range_header({{1, 10}})});
|
||||
cli.set_connection_timeout(5);
|
||||
@@ -3945,21 +4115,21 @@ TEST(SSLClientTest, UpdateCAStore) {
|
||||
httplib_client.set_ca_cert_store(ca_store_2);
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerNameIndication) {
|
||||
TEST(SSLClientTest, ServerNameIndication_Online) {
|
||||
SSLClient cli("httpbin.org", 443);
|
||||
auto res = cli.Get("/get");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification1) {
|
||||
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
|
||||
SSLClient cli("google.com");
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(301, res->status);
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification2) {
|
||||
TEST(SSLClientTest, ServerCertificateVerification2_Online) {
|
||||
SSLClient cli("google.com");
|
||||
cli.enable_server_certificate_verification(true);
|
||||
cli.set_ca_cert_path("hello");
|
||||
@@ -3968,7 +4138,7 @@ TEST(SSLClientTest, ServerCertificateVerification2) {
|
||||
EXPECT_EQ(Error::SSLLoadingCerts, res.error());
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification3) {
|
||||
TEST(SSLClientTest, ServerCertificateVerification3_Online) {
|
||||
SSLClient cli("google.com");
|
||||
cli.set_ca_cert_path(CA_CERT_FILE);
|
||||
auto res = cli.Get("/");
|
||||
@@ -4001,7 +4171,7 @@ TEST(SSLClientTest, ServerCertificateVerification4) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientTest, WildcardHostNameMatch) {
|
||||
TEST(SSLClientTest, WildcardHostNameMatch_Online) {
|
||||
SSLClient cli("www.youtube.com");
|
||||
|
||||
cli.set_ca_cert_path(CA_CERT_FILE);
|
||||
@@ -4246,7 +4416,7 @@ TEST(NoScheme, SimpleInterface) {
|
||||
ASSERT_TRUE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(SendAPI, SimpleInterface) {
|
||||
TEST(SendAPI, SimpleInterface_Online) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
Request req;
|
||||
@@ -4259,7 +4429,7 @@ TEST(SendAPI, SimpleInterface) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
TEST(YahooRedirectTest2, SimpleInterface_Online) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
@@ -4273,7 +4443,7 @@ TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
EXPECT_EQ("https://yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest3, SimpleInterface) {
|
||||
TEST(YahooRedirectTest3, SimpleInterface_Online) {
|
||||
Client cli("https://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
@@ -4287,7 +4457,7 @@ TEST(YahooRedirectTest3, SimpleInterface) {
|
||||
EXPECT_EQ("https://www.yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest3, NewResultInterface) {
|
||||
TEST(YahooRedirectTest3, NewResultInterface_Online) {
|
||||
Client cli("https://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
@@ -4312,20 +4482,20 @@ TEST(YahooRedirectTest3, NewResultInterface) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
||||
TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
TEST(DecodeWithChunkedEncoding, BrotliEncoding_Online) {
|
||||
Client cli("https://cdnjs.cloudflare.com");
|
||||
auto res =
|
||||
cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "br"}});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(287630, res->body.size());
|
||||
EXPECT_EQ(287630U, res->body.size());
|
||||
EXPECT_EQ("application/javascript; charset=utf-8",
|
||||
res->get_header_value("Content-Type"));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(HttpsToHttpRedirectTest, SimpleInterface) {
|
||||
TEST(HttpsToHttpRedirectTest, SimpleInterface_Online) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
@@ -4336,7 +4506,7 @@ TEST(HttpsToHttpRedirectTest, SimpleInterface) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
TEST(HttpsToHttpRedirectTest2, SimpleInterface_Online) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
@@ -4349,7 +4519,7 @@ TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
|
||||
TEST(HttpsToHttpRedirectTest3, SimpleInterface_Online) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
@@ -4360,4 +4530,37 @@ TEST(HttpsToHttpRedirectTest3, SimpleInterface) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpToHttpsRedirectTest, CertFile) {
|
||||
Server svr;
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
svr.Get("/index", [&](const Request &, Response &res) {
|
||||
res.set_redirect("https://127.0.0.1:1235/index");
|
||||
svr.stop();
|
||||
});
|
||||
|
||||
SSLServer ssl_svr(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
ASSERT_TRUE(ssl_svr.is_valid());
|
||||
ssl_svr.Get("/index", [&](const Request &, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
ssl_svr.stop();
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen("127.0.0.1", PORT)); });
|
||||
thread t2 = thread([&]() { ASSERT_TRUE(ssl_svr.listen("127.0.0.1", 1235)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
Client cli("127.0.0.1", PORT);
|
||||
cli.set_ca_cert_path(SERVER_CERT2_FILE);
|
||||
cli.enable_server_certificate_verification(true);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_connection_timeout(30);
|
||||
|
||||
auto res = cli.Get("/index");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
t.join();
|
||||
t2.join();
|
||||
}
|
||||
#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