mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-23 11:54:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
219d13b718 | ||
|
|
df20c27696 | ||
|
|
a5a62768c0 | ||
|
|
4001637beb | ||
|
|
47044c05a8 | ||
|
|
a449d82723 | ||
|
|
fee8e97b4e | ||
|
|
72d9ed4056 | ||
|
|
1be1b3a86d | ||
|
|
9452c0a4b6 | ||
|
|
307b729549 | ||
|
|
696239d6e1 | ||
|
|
6929d90353 | ||
|
|
348d032029 | ||
|
|
d1d3fcdfd5 | ||
|
|
abf3a67dd0 | ||
|
|
d87abeecf0 | ||
|
|
4e28e4f741 | ||
|
|
80a55cedeb | ||
|
|
d05c343602 | ||
|
|
33f67386fe | ||
|
|
56d8168dc4 | ||
|
|
5d87cc0558 | ||
|
|
cb41947eb4 | ||
|
|
0857eba17b | ||
|
|
020b0db090 | ||
|
|
bf0760fde4 | ||
|
|
bb8e45383e | ||
|
|
a1df576e4f | ||
|
|
7fb0254794 | ||
|
|
c82d1e52cc | ||
|
|
846151b605 | ||
|
|
e44e31dd5b | ||
|
|
f7b9501662 | ||
|
|
e12fe4cbbb | ||
|
|
49d2e1f135 | ||
|
|
8191fd8e6c | ||
|
|
d73395e1dc | ||
|
|
64d001162b | ||
|
|
bb00a23116 | ||
|
|
63d6e9b91b | ||
|
|
66eed5681a | ||
|
|
8ecdb11979 | ||
|
|
894fcc8e02 | ||
|
|
7f43f0f3ff | ||
|
|
87e03dd1ce | ||
|
|
e5cacb465d | ||
|
|
ee8371f753 | ||
|
|
081723f983 | ||
|
|
b61f36579c | ||
|
|
33f53aa458 | ||
|
|
412ab5f063 | ||
|
|
11e02e901c | ||
|
|
65a8f4cf44 | ||
|
|
27d128bbb4 | ||
|
|
070f9bec58 | ||
|
|
f817032513 | ||
|
|
17abe221c0 | ||
|
|
4a7a81e039 | ||
|
|
37fd4eb643 | ||
|
|
865b0e4c03 | ||
|
|
b324921c1a | ||
|
|
63f72caf30 | ||
|
|
99ac17b90a | ||
|
|
4b0ed9ee88 | ||
|
|
20056f6cda | ||
|
|
3b35279b16 | ||
|
|
27deb44df5 | ||
|
|
24a3ef949b | ||
|
|
bc3e098964 | ||
|
|
c247dcdd7b | ||
|
|
793ae9855e | ||
|
|
9fa426d51b | ||
|
|
cec6288a99 | ||
|
|
9639578c2a | ||
|
|
743ecbd365 | ||
|
|
084c643973 | ||
|
|
824e7682e4 | ||
|
|
f9074684dd | ||
|
|
ddff782133 | ||
|
|
3051152103 | ||
|
|
06026bb47d | ||
|
|
226388ae27 | ||
|
|
ea7548b4cc | ||
|
|
c7486ead96 | ||
|
|
90a291214c | ||
|
|
c111c42a86 | ||
|
|
6fb5b63018 | ||
|
|
ec56dfa35e | ||
|
|
943cd51b67 | ||
|
|
301faa074c | ||
|
|
dc0481e832 | ||
|
|
4f8fcdbaf7 | ||
|
|
b80aa7fee3 | ||
|
|
c384be02c9 | ||
|
|
d17ac3bb40 | ||
|
|
c7554ccac2 | ||
|
|
35ef1c7bae | ||
|
|
d87d0672a8 | ||
|
|
3da42fd1e8 | ||
|
|
503aa61325 | ||
|
|
e4c276d0c2 | ||
|
|
e07f7691a8 | ||
|
|
623ab4a96e | ||
|
|
e1efa337a2 | ||
|
|
549cdf2f7d | ||
|
|
3c522386e9 | ||
|
|
c202aa9ce9 | ||
|
|
e3e28c6231 | ||
|
|
4e05368086 | ||
|
|
e1afe74fe2 | ||
|
|
461acb02f5 | ||
|
|
415edc237c | ||
|
|
e20ecd2574 | ||
|
|
ab477b5631 | ||
|
|
0823d5c7f2 | ||
|
|
1cc6930363 | ||
|
|
4297500928 | ||
|
|
a58f042614 | ||
|
|
469c6bc2b6 | ||
|
|
887074efd2 | ||
|
|
9c2c15ca45 | ||
|
|
1b3b098329 | ||
|
|
6f7075e3aa |
No files matched your search
@@ -0,0 +1,26 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cpp-httplib'
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cpp-httplib'
|
||||||
|
fuzz-seconds: 600
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
||||||
@@ -26,10 +26,10 @@ jobs:
|
|||||||
run: brew install brotli
|
run: brew install brotli
|
||||||
- name: make
|
- name: make
|
||||||
if: matrix.os != 'windows-latest'
|
if: matrix.os != 'windows-latest'
|
||||||
run: cd test && make
|
run: cd test && make -j2
|
||||||
- name: check fuzz test target
|
- name: check fuzz test target
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: cd test && make -f Makefile.fuzz_test
|
run: cd test && make fuzz_test
|
||||||
- name: setup msbuild on windows
|
- name: setup msbuild on windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
uses: microsoft/setup-msbuild@v1.0.2
|
||||||
|
|||||||
@@ -10,10 +10,15 @@ example/redirect
|
|||||||
example/sse*
|
example/sse*
|
||||||
example/upload
|
example/upload
|
||||||
example/*.pem
|
example/*.pem
|
||||||
|
test/httplib.cc
|
||||||
|
test/httplib.h
|
||||||
test/test
|
test/test
|
||||||
|
test/server_fuzzer
|
||||||
test/test_proxy
|
test/test_proxy
|
||||||
|
test/test_split
|
||||||
test/test.xcodeproj/xcuser*
|
test/test.xcodeproj/xcuser*
|
||||||
test/test.xcodeproj/*/xcuser*
|
test/test.xcodeproj/*/xcuser*
|
||||||
|
test/*.o
|
||||||
test/*.pem
|
test/*.pem
|
||||||
test/*.srl
|
test/*.srl
|
||||||
|
|
||||||
|
|||||||
+13
-35
@@ -60,27 +60,11 @@
|
|||||||
]]
|
]]
|
||||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||||
|
|
||||||
# On systems without Git installed, there were errors since execute_process seemed to not throw an error without it?
|
# Get the CPPHTTPLIB_VERSION value and use it as a version
|
||||||
find_package(Git QUIET)
|
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
|
||||||
if(Git_FOUND)
|
# This is so the maintainer doesn't actually need to update this manually.
|
||||||
# Gets the latest tag as a string like "v0.6.6"
|
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
|
||||||
# Can silently fail if git isn't on the system
|
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
|
|
||||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
OUTPUT_VARIABLE _raw_version_string
|
|
||||||
ERROR_VARIABLE _git_tag_error
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# execute_process can fail silenty, so check for an error
|
|
||||||
# if there was an error, just use the user agent as a version
|
|
||||||
if(_git_tag_error OR NOT Git_FOUND)
|
|
||||||
message(WARNING "cpp-httplib failed to find the latest Git tag, falling back to using user agent as the version.")
|
|
||||||
# Get the user agent and use it as a version
|
|
||||||
# This gets the string with the user agent from the header.
|
|
||||||
# This is so the maintainer doesn't actually need to update this manually.
|
|
||||||
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
|
||||||
endif()
|
|
||||||
# Needed since git tags have "v" prefixing them.
|
# Needed since git tags have "v" prefixing them.
|
||||||
# Also used if the fallback to user agent string is being used.
|
# Also used if the fallback to user agent string is being used.
|
||||||
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||||
@@ -116,6 +100,7 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
||||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||||
@@ -176,7 +161,7 @@ if(HTTPLIB_COMPILE)
|
|||||||
ERROR_VARIABLE _httplib_split_error
|
ERROR_VARIABLE _httplib_split_error
|
||||||
)
|
)
|
||||||
if(_httplib_split_error)
|
if(_httplib_split_error)
|
||||||
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
|
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# split.py puts output in "out"
|
# split.py puts output in "out"
|
||||||
@@ -188,6 +173,11 @@ if(HTTPLIB_COMPILE)
|
|||||||
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
||||||
)
|
)
|
||||||
|
set_target_properties(${PROJECT_NAME}
|
||||||
|
PROPERTIES
|
||||||
|
VERSION ${${PROJECT_NAME}_VERSION}
|
||||||
|
SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
# This is for header-only.
|
# This is for header-only.
|
||||||
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
||||||
@@ -198,19 +188,9 @@ endif()
|
|||||||
# Only useful if building in-tree, versus using it from an installation.
|
# Only useful if building in-tree, versus using it from an installation.
|
||||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||||
|
|
||||||
# Might be missing some, but this list is somewhat comprehensive
|
# Require C++11
|
||||||
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
cxx_std_11
|
cxx_std_11
|
||||||
cxx_nullptr
|
|
||||||
cxx_lambdas
|
|
||||||
cxx_override
|
|
||||||
cxx_defaulted_functions
|
|
||||||
cxx_attribute_deprecated
|
|
||||||
cxx_auto_type
|
|
||||||
cxx_decltype
|
|
||||||
cxx_deleted_functions
|
|
||||||
cxx_range_for
|
|
||||||
cxx_sizeof_member
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
@@ -285,11 +265,9 @@ endif()
|
|||||||
# and linkage information (doesn't find deps though).
|
# and linkage information (doesn't find deps though).
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${PROJECT_NAME}
|
||||||
EXPORT httplibTargets
|
EXPORT httplibTargets
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
|
|||||||
@@ -48,17 +48,12 @@ res->status;
|
|||||||
res->body;
|
res->body;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Try out the examples on Repl.it!
|
|
||||||
|
|
||||||
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
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||||
|
|
||||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
@@ -183,6 +178,15 @@ The followings are built-in mappings:
|
|||||||
| webm | video/webm | zip | application/zip |
|
| webm | video/webm | zip | application/zip |
|
||||||
| mp3 | audio/mp3 | wasm | application/wasm |
|
| mp3 | audio/mp3 | wasm | application/wasm |
|
||||||
|
|
||||||
|
### File request handler
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// The handler is called right before the response is sent to a client
|
||||||
|
svr.set_file_request_handler([](const Request &req, Response &res) {
|
||||||
|
...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
NOTE: These static file server methods are not thread-safe.
|
NOTE: These static file server methods are not thread-safe.
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
@@ -220,7 +224,7 @@ svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
|
|||||||
### Pre routing handler
|
### Pre routing handler
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||||
if (req.path == "/hello") {
|
if (req.path == "/hello") {
|
||||||
res.set_content("world", "text/html");
|
res.set_content("world", "text/html");
|
||||||
return Server::HandlerResponse::Handled;
|
return Server::HandlerResponse::Handled;
|
||||||
@@ -256,6 +260,7 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
|
|||||||
svr.Post("/content_receiver",
|
svr.Post("/content_receiver",
|
||||||
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
||||||
if (req.is_multipart_form_data()) {
|
if (req.is_multipart_form_data()) {
|
||||||
|
// NOTE: `content_reader` is blocking until every form data field is read
|
||||||
MultipartFormDataItems files;
|
MultipartFormDataItems files;
|
||||||
content_reader(
|
content_reader(
|
||||||
[&](const MultipartFormData &file) {
|
[&](const MultipartFormData &file) {
|
||||||
@@ -272,7 +277,6 @@ svr.Post("/content_receiver",
|
|||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
res.set_content(body, "text/plain");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -303,7 +307,7 @@ Without content length:
|
|||||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
"text/plain", // Content type
|
"text/plain", // Content type
|
||||||
[&](size_t offset, size_t length, DataSink &sink) {
|
[&](size_t offset, DataSink &sink) {
|
||||||
if (/* there is still data */) {
|
if (/* there is still data */) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
// prepare data...
|
// prepare data...
|
||||||
@@ -746,13 +750,29 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
|||||||
res->body; // Compressed data
|
res->body; // Compressed data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Use `poll` instead of `select`
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
`select` system call is used as default since it's more widely supported. If you want to let cpp-httplib use `poll` instead, you can do so with `CPPHTTPLIB_USE_POLL`.
|
||||||
|
|
||||||
|
|
||||||
Split httplib.h into .h and .cc
|
Split httplib.h into .h and .cc
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
> python3 split.py
|
$ ./split.py -h
|
||||||
> ls out
|
usage: split.py [-h] [-e EXTENSION] [-o OUT]
|
||||||
httplib.h httplib.cc
|
|
||||||
|
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
|
NOTE
|
||||||
@@ -777,6 +797,8 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
|||||||
#include <httplib.h>
|
#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.
|
Note: Windows 8 or lower and Cygwin on Windows are not supported.
|
||||||
|
|
||||||
License
|
License
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
FROM alpine as builder
|
||||||
|
WORKDIR /src/example
|
||||||
|
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
|
||||||
|
COPY ./httplib.h /src
|
||||||
|
COPY ./example/hello.cc /src/example
|
||||||
|
COPY ./example/Makefile /src/example
|
||||||
|
RUN make hello
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
RUN apk --no-cache add brotli libstdc++
|
||||||
|
COPY --from=builder /src/example/hello /bin/hello
|
||||||
|
CMD ["/bin/hello"]
|
||||||
+9
-6
@@ -1,14 +1,17 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
#CXX = clang++
|
||||||
CXXFLAGS = -std=c++11 -I.. -Wall -Wextra -pthread
|
CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl
|
PREFIX = /usr/local
|
||||||
|
#PREFIX = $(shell brew --prefix)
|
||||||
|
|
||||||
|
OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
|
||||||
|
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||||
|
|
||||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark
|
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark
|
||||||
|
|
||||||
@@ -47,4 +50,4 @@ pem:
|
|||||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm server client hello simplecli simplesvr upload redirect ssesvr sselci benchmark *.pem
|
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark *.pem
|
||||||
+1
-1
@@ -15,5 +15,5 @@ int main(void) {
|
|||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.listen("localhost", 8080);
|
svr.listen("0.0.0.0", 8080);
|
||||||
}
|
}
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
for i in {1..10000}
|
||||||
|
do
|
||||||
|
echo "#### $i ####"
|
||||||
|
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
|
||||||
|
done
|
||||||
+105
-2
@@ -1,7 +1,110 @@
|
|||||||
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'
|
||||||
|
cxx = meson.get_compiler('cpp')
|
||||||
|
version = cxx.get_define('CPPHTTPLIB_VERSION',
|
||||||
|
prefix: '#include <httplib.h>',
|
||||||
|
include_directories: include_directories('.')).strip('"')
|
||||||
|
assert(version != '', 'failed to get version from httplib.h')
|
||||||
|
endif
|
||||||
|
|
||||||
|
deps = [dependency('threads')]
|
||||||
|
args = []
|
||||||
|
|
||||||
|
openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cpp-httplib_openssl'))
|
||||||
|
if openssl_dep.found()
|
||||||
|
deps += openssl_dep
|
||||||
|
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||||
|
endif
|
||||||
|
|
||||||
|
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
||||||
|
if zlib_dep.found()
|
||||||
|
deps += zlib_dep
|
||||||
|
args += '-DCPPHTTPLIB_ZLIB_SUPPORT'
|
||||||
|
endif
|
||||||
|
|
||||||
|
brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))]
|
||||||
|
brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli'))
|
||||||
|
brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli'))
|
||||||
|
|
||||||
|
brotli_found_all = true
|
||||||
|
foreach brotli_dep : brotli_deps
|
||||||
|
if not brotli_dep.found()
|
||||||
|
brotli_found_all = false
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
if brotli_found_all
|
||||||
|
deps += brotli_deps
|
||||||
|
args += '-DCPPHTTPLIB_BROTLI_SUPPORT'
|
||||||
|
endif
|
||||||
|
|
||||||
|
cpp_httplib_dep = dependency('', required: false)
|
||||||
|
|
||||||
|
if get_option('cpp-httplib_compile')
|
||||||
|
python3 = find_program('python3')
|
||||||
|
|
||||||
|
httplib_ch = custom_target(
|
||||||
|
'split',
|
||||||
|
input: 'httplib.h',
|
||||||
|
output: ['httplib.cc', 'httplib.h'],
|
||||||
|
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
|
||||||
|
install: true,
|
||||||
|
install_dir: [false, get_option('includedir')]
|
||||||
|
)
|
||||||
|
lib = library(
|
||||||
|
'cpp-httplib',
|
||||||
|
sources: httplib_ch,
|
||||||
|
dependencies: deps,
|
||||||
|
cpp_args: args,
|
||||||
|
version: version,
|
||||||
|
install: true
|
||||||
|
)
|
||||||
|
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||||
|
|
||||||
|
import('pkgconfig').generate(
|
||||||
|
lib,
|
||||||
|
description: 'A C++ HTTP/HTTPS server and client library',
|
||||||
|
extra_cflags: args,
|
||||||
|
url: 'https://github.com/yhirose/cpp-httplib',
|
||||||
|
version: version
|
||||||
|
)
|
||||||
|
else
|
||||||
|
install_headers('httplib.h')
|
||||||
|
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.'))
|
||||||
|
|
||||||
|
import('pkgconfig').generate(
|
||||||
|
name: 'cpp-httplib',
|
||||||
|
description: 'A C++ HTTP/HTTPS server and client library',
|
||||||
|
install_dir: join_paths(get_option('datadir'), 'pkgconfig'),
|
||||||
|
url: 'https://github.com/yhirose/cpp-httplib',
|
||||||
|
version: version
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
if meson.version().version_compare('>=0.54.0')
|
if meson.version().version_compare('>=0.54.0')
|
||||||
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
|
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
|
||||||
endif
|
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 python3)')
|
||||||
|
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
||||||
@@ -1,32 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""This script splits httplib.h into .h and .cc parts."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
border = '// ----------------------------------------------------------------------------'
|
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:
|
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()
|
lines = f.readlines()
|
||||||
inImplementation = False
|
|
||||||
|
|
||||||
if PythonVersion < 3:
|
python_version = sys.version_info[0]
|
||||||
os.makedirs('out')
|
if python_version < 3:
|
||||||
|
os.makedirs(args.out)
|
||||||
else:
|
else:
|
||||||
os.makedirs('out', exist_ok=True)
|
os.makedirs(args.out, exist_ok=True)
|
||||||
|
|
||||||
with open('out/httplib.h', 'w') as fh:
|
in_implementation = False
|
||||||
with open('out/httplib.cc', 'w') as fc:
|
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('#include "httplib.h"\n')
|
||||||
fc.write('namespace httplib {\n')
|
fc.write('namespace httplib {\n')
|
||||||
for line in lines:
|
for line in lines:
|
||||||
isBorderLine = border in line
|
is_border_line = border in line
|
||||||
if isBorderLine:
|
if is_border_line:
|
||||||
inImplementation = not inImplementation
|
in_implementation = not in_implementation
|
||||||
else:
|
elif in_implementation:
|
||||||
if inImplementation:
|
|
||||||
fc.write(line.replace('inline ', ''))
|
fc.write(line.replace('inline ', ''))
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
fh.write(line)
|
fh.write(line)
|
||||||
pass
|
|
||||||
fc.write('} // namespace httplib\n')
|
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))
|
||||||
+47
-9
@@ -1,25 +1,61 @@
|
|||||||
#CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -g -std=c++11 -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
PREFIX = /usr/local
|
||||||
|
#PREFIX = $(shell brew --prefix)
|
||||||
|
|
||||||
|
OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
|
||||||
|
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||||
|
|
||||||
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
|
./test
|
||||||
|
|
||||||
proxy : test_proxy
|
proxy : test_proxy
|
||||||
./test_proxy
|
./test_proxy
|
||||||
|
|
||||||
test : test.cc ../httplib.h Makefile cert.pem
|
test : test.cc include_httplib.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
|
$(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
|
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:
|
cert.pem:
|
||||||
openssl genrsa 2048 > key.pem
|
openssl genrsa 2048 > key.pem
|
||||||
@@ -29,7 +65,9 @@ cert.pem:
|
|||||||
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||||
openssl genrsa 2048 > client.key.pem
|
openssl genrsa 2048 > client.key.pem
|
||||||
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
||||||
|
openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
|
||||||
|
openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
|
||||||
#c_rehash .
|
#c_rehash .
|
||||||
|
|
||||||
clean:
|
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
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
PUT { HTTP/1.0
|
||||||
|
Content-Type:multipart/form-databoundary=m
|
||||||
|
Range:bytes=-
|
||||||
|
|
||||||
|
--m
|
||||||
|
C
|
||||||
|
c
Line truncated
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
POST /fform%u008ano栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽栽Line truncated
|
||||||
|
Don | ||||||