mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06026bb47d | |||
| 226388ae27 | |||
| ea7548b4cc | |||
| c7486ead96 | |||
| 90a291214c | |||
| c111c42a86 | |||
| 6fb5b63018 | |||
| ec56dfa35e | |||
| 943cd51b67 | |||
| 301faa074c | |||
| dc0481e832 | |||
| 4f8fcdbaf7 | |||
| b80aa7fee3 | |||
| c384be02c9 | |||
| d17ac3bb40 | |||
| c7554ccac2 | |||
| 35ef1c7bae | |||
| d87d0672a8 | |||
| 3da42fd1e8 | |||
| 503aa61325 | |||
| e4c276d0c2 | |||
| e07f7691a8 | |||
| 623ab4a96e | |||
| e1efa337a2 | |||
| 549cdf2f7d | |||
| 3c522386e9 | |||
| c202aa9ce9 | |||
| e3e28c6231 | |||
| 4e05368086 | |||
| e1afe74fe2 | |||
| 461acb02f5 | |||
| 415edc237c | |||
| e20ecd2574 | |||
| ab477b5631 | |||
| 0823d5c7f2 | |||
| 1cc6930363 | |||
| 4297500928 | |||
| a58f042614 | |||
| 469c6bc2b6 | |||
| 887074efd2 | |||
| 9c2c15ca45 | |||
| 1b3b098329 | |||
| 6f7075e3aa |
@@ -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
|
||||
@@ -26,10 +26,10 @@ 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
|
||||
run: cd test && make fuzz_test
|
||||
- name: setup msbuild on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -256,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) {
|
||||
@@ -272,7 +273,6 @@ svr.Post("/content_receiver",
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -746,13 +746,29 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res->body; // Compressed data
|
||||
```
|
||||
|
||||
Use `poll` instead of `select`
|
||||
------------------------------
|
||||
|
||||
`select` system call is used as default since it's more widely supported. If you want to let cpp-httplib use `poll` instead, you can do so with `CPPHTTPLIB_USE_POLL`.
|
||||
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
```bash
|
||||
> python3 split.py
|
||||
> ls out
|
||||
httplib.h httplib.cc
|
||||
```console
|
||||
$ ./split.py -h
|
||||
usage: split.py [-h] [-e EXTENSION] [-o OUT]
|
||||
|
||||
This script splits httplib.h into .h and .cc parts.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-e EXTENSION, --extension EXTENSION
|
||||
extension of the implementation file (default: cc)
|
||||
-o OUT, --out OUT where to write the files (default: out)
|
||||
|
||||
$ ./split.py
|
||||
Wrote out/httplib.h and out/httplib.cc
|
||||
```
|
||||
|
||||
NOTE
|
||||
@@ -777,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
|
||||
|
||||
+8
-5
@@ -1,14 +1,17 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -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
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark
|
||||
|
||||
@@ -47,4 +50,4 @@ pem:
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplecli simplesvr upload redirect ssesvr sselci benchmark *.pem
|
||||
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark *.pem
|
||||
|
||||
+109
-2
@@ -1,7 +1,114 @@
|
||||
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(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||
|
||||
import('pkgconfig').generate(
|
||||
lib,
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
extra_cflags: args,
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
else
|
||||
install_headers('httplib.h')
|
||||
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.'))
|
||||
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))
|
||||
|
||||
+45
-9
@@ -1,25 +1,61 @@
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
CXX = clang++
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
all : test
|
||||
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
|
||||
# By default, use standalone_fuzz_target_runner.
|
||||
# This runner does no fuzzing, but simply executes the inputs
|
||||
# provided via parameters.
|
||||
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||
# to link the fuzzer(s) against a real fuzzing engine.
|
||||
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||
|
||||
all : test test_split
|
||||
./test
|
||||
|
||||
proxy : test_proxy
|
||||
./test_proxy
|
||||
|
||||
test : test.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
|
||||
|
||||
# Note: The intention of test_split is to verify that it works to compile and
|
||||
# link the split httplib.h, so there is normally no need to execute it.
|
||||
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
|
||||
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
||||
|
||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test_proxy $(CXXFLAGS) test_proxy.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
|
||||
|
||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||
fuzz_test: server_fuzzer
|
||||
./server_fuzzer fuzzing/corpus/*
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
|
||||
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||
# feeds it to server_fuzzer.
|
||||
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) -c $<
|
||||
|
||||
httplib.cc : ../httplib.h
|
||||
python3 ../split.py -o .
|
||||
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
@@ -32,4 +68,4 @@ cert.pem:
|
||||
#c_rehash .
|
||||
|
||||
clean:
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
||||
rm -f test test_split test_proxy server_fuzzer pem *.0 *.o *.1 *.srl httplib.h httplib.cc
|
||||
|
||||
@@ -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
|
||||
@@ -20,16 +20,16 @@ int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::ifstream in(argv[i]);
|
||||
in.seekg(0, in.end);
|
||||
size_t length = in.tellg();
|
||||
size_t length = static_cast<size_t>(in.tellg());
|
||||
in.seekg (0, in.beg);
|
||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||
std::vector<char> bytes(length);
|
||||
in.read(bytes.data(), bytes.size());
|
||||
in.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
|
||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||
bytes.size());
|
||||
std::cout << "Execution successful" << std::endl;
|
||||
}
|
||||
std::cout << "Execution finished" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
+200
-57
@@ -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"
|
||||
@@ -36,8 +37,17 @@ MultipartFormData &get_file_value(MultipartFormDataItems &files,
|
||||
auto it = std::find_if(
|
||||
files.begin(), files.end(),
|
||||
[&](const MultipartFormData &file) { return file.name == key; });
|
||||
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
return *it;
|
||||
#else
|
||||
if (it != files.end()) { return *it; }
|
||||
throw std::runtime_error("invalid mulitpart form data name error");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ConstructorTest, MoveConstructible) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<Client>::value);
|
||||
EXPECT_TRUE(std::is_nothrow_move_constructible<Client>::value);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -165,6 +175,12 @@ TEST(GetHeaderValueTest, RegularValue) {
|
||||
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) {
|
||||
Response res;
|
||||
|
||||
@@ -354,7 +370,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 +393,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 +421,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 +453,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
||||
EXPECT_EQ(out, body);
|
||||
}
|
||||
|
||||
TEST(RangeTest, FromHTTPBin) {
|
||||
TEST(RangeTest, FromHTTPBin_Online) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -578,7 +594,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 +612,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 +629,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 +648,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 +699,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 +746,40 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AbsoluteRedirectTest, Redirect) {
|
||||
TEST(SpecifyServerIPAddressTest, AnotherHostname) {
|
||||
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) {
|
||||
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) {
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -745,7 +794,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 +809,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 +824,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 +840,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 +854,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 +863,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 +876,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,7 +888,7 @@ TEST(HttpsToHttpRedirectTest3, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(UrlWithSpace, Redirect) {
|
||||
TEST(UrlWithSpace, Redirect_Online) {
|
||||
SSLClient cli("edge.forgecdn.net");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
@@ -852,42 +901,49 @@ TEST(UrlWithSpace, Redirect) {
|
||||
#endif
|
||||
|
||||
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) {
|
||||
Server svr1;
|
||||
svr1.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
|
||||
int svr1_port = 0;
|
||||
auto thread1 = std::thread([&]() {
|
||||
svr1_port = svr1.bind_to_any_port("localhost");
|
||||
svr1.listen_after_bind();
|
||||
});
|
||||
|
||||
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
|
||||
Server svr2;
|
||||
svr2.Get("/2", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("http://localhost:" + std::to_string(svr1_port) + "/1");
|
||||
});
|
||||
|
||||
while (!svr8080.is_running() || !svr8081.is_running()) {
|
||||
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", 8080);
|
||||
Client cli("localhost", svr2_port);
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/1");
|
||||
auto res = cli.Get("/2");
|
||||
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());
|
||||
svr1.stop();
|
||||
svr2.stop();
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
ASSERT_FALSE(svr1.is_running());
|
||||
ASSERT_FALSE(svr2.is_running());
|
||||
}
|
||||
|
||||
TEST(RedirectFromPageWithContent, Redirect) {
|
||||
@@ -964,8 +1020,15 @@ TEST(RedirectFromPageWithContentIP6, Redirect) {
|
||||
|
||||
auto th = std::thread([&]() { svr.listen("::1", 1234); });
|
||||
|
||||
while (!svr.is_running()) {
|
||||
// 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.
|
||||
@@ -1128,6 +1191,7 @@ TEST(ErrorHandlerTest, ContentLength) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
TEST(ExceptionHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
@@ -1163,6 +1227,7 @@ TEST(ExceptionHandlerTest, ContentLength) {
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(NoContentTest, ContentLength) {
|
||||
Server svr;
|
||||
@@ -1342,11 +1407,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;
|
||||
@@ -2616,6 +2683,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));
|
||||
@@ -2649,6 +2717,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");
|
||||
@@ -3297,7 +3366,7 @@ static bool send_request(time_t read_timeout_sec, const std::string &req,
|
||||
auto error = Error::Success;
|
||||
|
||||
auto client_sock = detail::create_client_socket(
|
||||
HOST, PORT, AF_UNSPEC, false, nullptr,
|
||||
HOST, "", PORT, AF_UNSPEC, false, nullptr,
|
||||
/*connection_timeout_sec=*/5, 0,
|
||||
/*read_timeout_sec=*/5, 0,
|
||||
/*write_timeout_sec=*/5, 0, std::string(), error);
|
||||
@@ -3555,10 +3624,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.
|
||||
@@ -3616,6 +3687,7 @@ TEST(MountTest, Unmount) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||
Server svr;
|
||||
|
||||
@@ -3644,6 +3716,7 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||
listen_thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(KeepAliveTest, ReadTimeout) {
|
||||
Server svr;
|
||||
@@ -3769,7 +3842,7 @@ TEST(GetWithParametersTest, GetWithParameters) {
|
||||
EXPECT_EQ("world3", req.get_param_value("hello3"));
|
||||
});
|
||||
|
||||
svr.Get(R"(/resources/([a-z0-9\\-]+))", [&](const Request& req, Response&) {
|
||||
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"));
|
||||
@@ -3852,7 +3925,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);
|
||||
@@ -4089,21 +4162,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");
|
||||
@@ -4112,7 +4185,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("/");
|
||||
@@ -4145,7 +4218,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);
|
||||
@@ -4366,6 +4439,75 @@ TEST(SSLClientServerTest, SSLConnectTimeout) {
|
||||
svr.stop();
|
||||
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
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -4381,16 +4523,18 @@ TEST(NoSSLSupport, SimpleInterface) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
TEST(InvalidScheme, SimpleInterface) {
|
||||
ASSERT_ANY_THROW(Client cli("scheme://yahoo.com"));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(NoScheme, SimpleInterface) {
|
||||
Client cli("yahoo.com:80");
|
||||
ASSERT_TRUE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(SendAPI, SimpleInterface) {
|
||||
TEST(SendAPI, SimpleInterface_Online) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
Request req;
|
||||
@@ -4403,7 +4547,7 @@ TEST(SendAPI, SimpleInterface) {
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
TEST(YahooRedirectTest2, SimpleInterface_Online) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
@@ -4417,7 +4561,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("/");
|
||||
@@ -4431,7 +4575,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("/");
|
||||
@@ -4456,7 +4600,7 @@ 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"}});
|
||||
@@ -4469,7 +4613,7 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(HttpsToHttpRedirectTest, SimpleInterface) {
|
||||
TEST(HttpsToHttpRedirectTest, SimpleInterface_Online) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
@@ -4480,7 +4624,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);
|
||||
|
||||
@@ -4493,7 +4637,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);
|
||||
|
||||
@@ -4520,7 +4664,6 @@ TEST(HttpToHttpsRedirectTest, CertFile) {
|
||||
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));
|
||||
|
||||
@@ -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