mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-06-12 09:37:15 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42f9f9107f | ||
|
|
7cd25fbd63 | ||
|
|
3dfb4ecac2 | ||
|
|
144114f316 | ||
|
|
0cc108d45e | ||
|
|
0743d78c9b | ||
|
|
e022b8b80b | ||
|
|
34282c79a9 | ||
|
|
f80b6bd980 | ||
|
|
5af7222217 | ||
|
|
ec00fe5d5b | ||
|
|
24bdb736f0 | ||
|
|
d0dc200633 | ||
|
|
919a51091f | ||
|
|
05e8b22989 | ||
|
|
00dcd6b004 | ||
|
|
a42c6b99d3 | ||
|
|
812cb5bc3d | ||
|
|
aea60feb85 | ||
|
|
b3a4045300 | ||
|
|
5fcd8f7795 | ||
|
|
d9fe3fa020 | ||
|
|
d8612ac02d | ||
|
|
83ee6007da | ||
|
|
3eaa769a2d | ||
|
|
b91540514d | ||
|
|
ab563ff52c | ||
|
|
8cad160c0a | ||
|
|
be7962f140 | ||
|
|
509b8570b0 | ||
|
|
630f3465a9 |
171
CMakeLists.txt
171
CMakeLists.txt
@@ -4,6 +4,9 @@
|
|||||||
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
||||||
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
||||||
* HTTPLIB_REQUIRE_ZLIB (default off)
|
* HTTPLIB_REQUIRE_ZLIB (default off)
|
||||||
|
* HTTPLIB_COMPILE (default off)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
After installation with Cmake, a find_package(httplib) is available.
|
After installation with Cmake, a find_package(httplib) is available.
|
||||||
This creates a httplib::httplib target (if found).
|
This creates a httplib::httplib target (if found).
|
||||||
@@ -27,18 +30,50 @@
|
|||||||
cmake ..
|
cmake ..
|
||||||
runas /user:Administrator "cmake --build . --config Release --target install"
|
runas /user:Administrator "cmake --build . --config Release --target install"
|
||||||
|
|
||||||
These three variables are available after you run find_package(httplib)
|
-------------------------------------------------------------------------------
|
||||||
* HTTPLIB_HEADER_PATH - this is the full path to the installed header.
|
|
||||||
|
These variables are available after you run find_package(httplib)
|
||||||
|
* HTTPLIB_HEADER_PATH - this is the full path to the installed header (e.g. /usr/include/httplib.h).
|
||||||
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
||||||
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
||||||
|
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
|
||||||
|
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
|
||||||
|
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
|
||||||
|
* HTTPLIB_VERSION - the project's version string.
|
||||||
|
* HTTPLIB_FOUND - a bool for if the target was found.
|
||||||
|
|
||||||
Want to use precompiled headers (Cmake feature since v3.16)?
|
Want to use precompiled headers (Cmake feature since v3.16)?
|
||||||
It's as simple as doing the following (before linking):
|
It's as simple as doing the following (before linking):
|
||||||
|
|
||||||
target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}")
|
target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}")
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FindPython3 requires Cmake v3.12
|
||||||
]]
|
]]
|
||||||
cmake_minimum_required(VERSION 3.7.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR)
|
||||||
project(httplib LANGUAGES CXX)
|
|
||||||
|
# Gets the latest tag as a string like "v0.6.6"
|
||||||
|
# Can silently fail if git isn't on the system
|
||||||
|
execute_process(COMMAND git describe --tags --abbrev=0
|
||||||
|
OUTPUT_VARIABLE _raw_version_string
|
||||||
|
ERROR_VARIABLE _git_tag_error
|
||||||
|
)
|
||||||
|
|
||||||
|
# execute_process can fail silenty, so check for an error
|
||||||
|
# if there was an error, just use the user agent as a version
|
||||||
|
if(_git_tag_error)
|
||||||
|
message(WARNING "cpp-httplib failed to find the latest git tag, falling back to using user agent as the version.")
|
||||||
|
# Get the user agent and use it as a version
|
||||||
|
# This gets the string with the user agent from the header.
|
||||||
|
# This is so the maintainer doesn't actually need to update this manually.
|
||||||
|
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
||||||
|
endif()
|
||||||
|
# Needed since git tags have "v" prefixing them.
|
||||||
|
# Also used if the fallback to user agent string is being used.
|
||||||
|
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||||
|
|
||||||
|
project(httplib VERSION ${_httplib_version} LANGUAGES CXX)
|
||||||
|
|
||||||
# Change as needed to set an OpenSSL minimum version.
|
# Change as needed to set an OpenSSL minimum version.
|
||||||
# This is used in the installed Cmake config file.
|
# This is used in the installed Cmake config file.
|
||||||
@@ -51,17 +86,23 @@ option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build.
|
|||||||
# Make these options so their automatic use can be specifically disabled (as needed)
|
# Make these options so their automatic use can be specifically disabled (as needed)
|
||||||
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
|
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
|
||||||
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable compression support." ON)
|
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable compression support." ON)
|
||||||
|
# Lets you compile the program as a regular library instead of header-only
|
||||||
# TODO: implement the option of option to correctly split, building, and export with the split.py script.
|
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
|
||||||
# option(HTTPLIB_SPLIT "Uses a Python script to split the header into a header & source file." OFF)
|
# Defaults to static library
|
||||||
|
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
|
||||||
|
if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
||||||
|
# Necessary for Windows if building shared libs
|
||||||
|
# See https://stackoverflow.com/a/40743080
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
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
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
||||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} REQUIRED)
|
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED)
|
||||||
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
||||||
# Look quietly since it's optional are optional
|
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET)
|
||||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} QUIET)
|
|
||||||
endif()
|
endif()
|
||||||
if(HTTPLIB_REQUIRE_ZLIB)
|
if(HTTPLIB_REQUIRE_ZLIB)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
@@ -73,16 +114,53 @@ endif()
|
|||||||
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
|
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} INTERFACE)
|
if(HTTPLIB_COMPILE)
|
||||||
|
# Put the split script into the build dir
|
||||||
|
configure_file(split.py "${CMAKE_CURRENT_BINARY_DIR}/split.py"
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
# Needs to be in the same dir as the python script
|
||||||
|
configure_file(httplib.h "${CMAKE_CURRENT_BINARY_DIR}/httplib.h"
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
# Used outside of this if-else
|
||||||
|
set(_INTERFACE_OR_PUBLIC PUBLIC)
|
||||||
|
# Brings in the Python3_EXECUTABLE path we can use.
|
||||||
|
find_package(Python3 REQUIRED)
|
||||||
|
# Actually split the file
|
||||||
|
# Keeps the output in the build dir to not pollute the main dir
|
||||||
|
execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/split.py"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
ERROR_VARIABLE _httplib_split_error
|
||||||
|
)
|
||||||
|
if(_httplib_split_error)
|
||||||
|
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# split.py puts output in "out"
|
||||||
|
set(_httplib_build_includedir "${CMAKE_CURRENT_BINARY_DIR}/out")
|
||||||
|
# This will automatically be either static or shared based on the value of BUILD_SHARED_LIBS
|
||||||
|
add_library(${PROJECT_NAME} "${_httplib_build_includedir}/httplib.cc")
|
||||||
|
target_sources(${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# This is for header-only.
|
||||||
|
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
set(_httplib_build_includedir "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
# Lets you address the target with httplib::httplib
|
# Lets you address the target with httplib::httplib
|
||||||
# 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
|
# Might be missing some, but this list is somewhat comprehensive
|
||||||
target_compile_features(${PROJECT_NAME} INTERFACE
|
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
cxx_std_11
|
cxx_std_11
|
||||||
cxx_nullptr
|
cxx_nullptr
|
||||||
cxx_noexcept
|
|
||||||
cxx_lambdas
|
cxx_lambdas
|
||||||
cxx_override
|
cxx_override
|
||||||
cxx_defaulted_functions
|
cxx_defaulted_functions
|
||||||
@@ -94,25 +172,42 @@ target_compile_features(${PROJECT_NAME} INTERFACE
|
|||||||
cxx_sizeof_member
|
cxx_sizeof_member
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
$<BUILD_INTERFACE:${_httplib_build_includedir}>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
|
||||||
# Always require threads
|
|
||||||
Threads::Threads
|
|
||||||
# Only link zlib & openssl if they're found
|
|
||||||
$<$<BOOL:${ZLIB_FOUND}>:ZLIB::ZLIB>
|
|
||||||
$<$<BOOL:${OPENSSL_FOUND}>:OpenSSL::SSL OpenSSL::Crypto>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Auto-define the optional support if those packages were found
|
# Always require threads
|
||||||
target_compile_definitions(${PROJECT_NAME} INTERFACE
|
target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
$<$<BOOL:${ZLIB_FOUND}>:CPPHTTPLIB_ZLIB_SUPPORT>
|
Threads::Threads
|
||||||
$<$<BOOL:${OPENSSL_FOUND}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# We check for the target when using IF_AVAILABLE since it's possible we didn't find it.
|
||||||
|
if(HTTPLIB_USE_OPENSSL_IF_AVAILABLE AND TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto OR HTTPLIB_REQUIRE_OPENSSL)
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
|
OpenSSL::SSL OpenSSL::Crypto
|
||||||
|
)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
|
CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
)
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
||||||
|
else()
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# We check for the target when using IF_AVAILABLE since it's possible we didn't find it.
|
||||||
|
if(HTTPLIB_USE_ZLIB_IF_AVAILABLE AND TARGET ZLIB::ZLIB OR HTTPLIB_REQUIRE_ZLIB)
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
|
ZLIB::ZLIB
|
||||||
|
)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
|
CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
|
)
|
||||||
|
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||||
|
else()
|
||||||
|
set(HTTPLIB_IS_USING_ZLIB FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Cmake's find_package search path is different based on the system
|
# Cmake's find_package search path is different based on the system
|
||||||
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
|
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
@@ -135,6 +230,23 @@ configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
|
|||||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(HTTPLIB_COMPILE)
|
||||||
|
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
# Example: if you find_package(httplib 0.5.4)
|
||||||
|
# then anything >= 0.5 and <= 1.0 is accepted
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
# Example: if you find_package(httplib 0.5.4)
|
||||||
|
# then anything >= 0.5 and <= 1.0 is accepted
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
# Tells Cmake that it's a header-only lib
|
||||||
|
# Mildly useful for end-users :)
|
||||||
|
ARCH_INDEPENDENT
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Creates the export httplibTargets.cmake
|
# Creates the export httplibTargets.cmake
|
||||||
# This is strictly what holds compilation requirements
|
# This is strictly what holds compilation requirements
|
||||||
# and linkage information (doesn't find deps though).
|
# and linkage information (doesn't find deps though).
|
||||||
@@ -144,10 +256,11 @@ install(TARGETS ${PROJECT_NAME}
|
|||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
99
README.md
99
README.md
@@ -203,6 +203,44 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 'Expect: 100-continue' handler
|
||||||
|
|
||||||
|
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Send a '417 Expectation Failed' response.
|
||||||
|
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||||
|
return 417;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Send a final status without reading the message body.
|
||||||
|
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||||
|
return res.status = 401;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keep-Alive connection
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.set_keep_alive_max_count(2); // Default is 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timeout
|
||||||
|
|
||||||
|
```c++
|
||||||
|
svr.set_read_timeout(5, 0); // 5 seconds
|
||||||
|
svr.set_write_timeout(5, 0); // 5 seconds
|
||||||
|
svr.set_idle_interval(0, 100000); // 100 milliseconds
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set maximum payload length for reading request body
|
||||||
|
|
||||||
|
```c++
|
||||||
|
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
|
||||||
|
```
|
||||||
|
|
||||||
### Server-Sent Events
|
### Server-Sent Events
|
||||||
|
|
||||||
Please check [here](https://github.com/yhirose/cpp-httplib/blob/master/example/sse.cc).
|
Please check [here](https://github.com/yhirose/cpp-httplib/blob/master/example/sse.cc).
|
||||||
@@ -240,24 +278,6 @@ svr.new_task_queue = [] {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 'Expect: 100-continue' handler
|
|
||||||
|
|
||||||
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// Send a '417 Expectation Failed' response.
|
|
||||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
|
||||||
return 417;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// Send a final status without reading the message body.
|
|
||||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
|
||||||
return res.status = 401;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Client Example
|
Client Example
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@@ -360,11 +380,14 @@ res = cli.Options("*");
|
|||||||
res = cli.Options("/resource/foo");
|
res = cli.Options("/resource/foo");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Connection Timeout
|
### Timeout
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
cli.set_connection_timeout(0, 300000); // 300 milliseconds
|
||||||
|
cli.set_read_timeout(5, 0); // 5 seconds
|
||||||
|
cli.set_write_timeout(5, 0); // 5 seconds
|
||||||
```
|
```
|
||||||
|
|
||||||
### Receive content with Content receiver
|
### Receive content with Content receiver
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -459,29 +482,15 @@ httplib::make_range_header({{0, 0}, {-1, 1}}) // 'Range: bytes=0-0, -1'
|
|||||||
### Keep-Alive connection
|
### Keep-Alive connection
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
cli.set_keep_alive_max_count(2); // Default is 5
|
httplib::Client cli("localhost", 1234);
|
||||||
|
|
||||||
std::vector<Request> requests;
|
cli.Get("/hello"); // with "Connection: close"
|
||||||
Get(requests, "/get-request1");
|
|
||||||
Get(requests, "/get-request2");
|
|
||||||
Post(requests, "/post-request1", "text", "text/plain");
|
|
||||||
Post(requests, "/post-request2", "text", "text/plain");
|
|
||||||
|
|
||||||
const size_t DATA_CHUNK_SIZE = 4;
|
cli.set_keep_alive(true);
|
||||||
std::string data("abcdefg");
|
cli.Get("/world");
|
||||||
Post(requests, "/post-request-with-content-provider",
|
|
||||||
data.size(),
|
|
||||||
[&](size_t offset, size_t length, DataSink &sink){
|
|
||||||
sink.write(&data[offset], std::min(length, DATA_CHUNK_SIZE));
|
|
||||||
},
|
|
||||||
"text/plain");
|
|
||||||
|
|
||||||
std::vector<Response> responses;
|
cli.set_keep_alive(false);
|
||||||
if (cli.send(requests, responses)) {
|
cli.Get("/last-request"); // with "Connection: close"
|
||||||
for (const auto& res: responses) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Redirect
|
### Redirect
|
||||||
@@ -536,13 +545,21 @@ The server applies gzip compression to the following MIME type contents:
|
|||||||
* application/xml
|
* application/xml
|
||||||
* application/xhtml+xml
|
* application/xhtml+xml
|
||||||
|
|
||||||
### Compress content on client
|
### Compress request body on client
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
cli.set_compress(true);
|
cli.set_compress(true);
|
||||||
res = cli.Post("/resource/foo", "...", "text/plain");
|
res = cli.Post("/resource/foo", "...", "text/plain");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Compress response body on client
|
||||||
|
|
||||||
|
```c++
|
||||||
|
cli.set_decompress(false);
|
||||||
|
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate"}});
|
||||||
|
res->body; // Compressed data
|
||||||
|
```
|
||||||
|
|
||||||
Split httplib.h into .h and .cc
|
Split httplib.h into .h and .cc
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,66 @@
|
|||||||
# Generates a macro to auto-configure everything
|
# Generates a macro to auto-configure everything
|
||||||
@PACKAGE_INIT@
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
# Setting these here so they're accessible after install.
|
||||||
|
# Might be useful for some users to check which settings were used.
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL @HTTPLIB_IS_USING_OPENSSL@)
|
||||||
|
set(HTTPLIB_IS_USING_ZLIB @HTTPLIB_IS_USING_ZLIB@)
|
||||||
|
set(HTTPLIB_IS_COMPILED @HTTPLIB_COMPILE@)
|
||||||
|
set(HTTPLIB_VERSION @PROJECT_VERSION@)
|
||||||
|
|
||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
|
|
||||||
# We add find_dependency calls here to not make the end-user have to call them.
|
# We add find_dependency calls here to not make the end-user have to call them.
|
||||||
find_dependency(Threads REQUIRED)
|
find_dependency(Threads REQUIRED)
|
||||||
if(@HTTPLIB_REQUIRE_OPENSSL@)
|
if(@HTTPLIB_IS_USING_OPENSSL@)
|
||||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
# OpenSSL COMPONENTS were added in Cmake v3.11
|
||||||
# Lets you check if these options were correctly enabled for your install
|
if(CMAKE_VERSION VERSION_LESS "3.11")
|
||||||
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
||||||
elseif(@HTTPLIB_USE_OPENSSL_IF_AVAILABLE@)
|
else()
|
||||||
# Look quietly since it's optional
|
# Once the COMPONENTS were added, they were made optional when not specified.
|
||||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ QUIET)
|
# Since we use both, we need to search for both.
|
||||||
# Lets you check if these options were correctly enabled for your install
|
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED)
|
||||||
set(HTTPLIB_IS_USING_OPENSSL @OPENSSL_FOUND@)
|
endif()
|
||||||
else()
|
|
||||||
set(HTTPLIB_IS_USING_OPENSSL FALSE)
|
|
||||||
endif()
|
endif()
|
||||||
if(@HTTPLIB_REQUIRE_ZLIB@)
|
if(@HTTPLIB_IS_USING_ZLIB@)
|
||||||
find_dependency(ZLIB REQUIRED)
|
find_dependency(ZLIB REQUIRED)
|
||||||
# Lets you check if these options were correctly enabled for your install
|
|
||||||
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
|
||||||
elseif(@HTTPLIB_USE_ZLIB_IF_AVAILABLE@)
|
|
||||||
# Look quietly since it's optional
|
|
||||||
find_dependency(ZLIB QUIET)
|
|
||||||
# Lets you check if these options were correctly enabled for your install
|
|
||||||
set(HTTPLIB_IS_USING_ZLIB @ZLIB_FOUND@)
|
|
||||||
else()
|
|
||||||
set(HTTPLIB_IS_USING_ZLIB FALSE)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Lets the end-user find the header path if needed
|
# Mildly useful for end-users
|
||||||
|
# Not really recommended to be used though
|
||||||
|
set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||||
|
# Lets the end-user find the header path with the header appended
|
||||||
# This is helpful if you're using Cmake's pre-compiled header feature
|
# This is helpful if you're using Cmake's pre-compiled header feature
|
||||||
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
||||||
|
|
||||||
# Brings in the target library
|
# Brings in the target library
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||||
|
|
||||||
|
# Ouputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||||
|
include(FindPackageMessage)
|
||||||
|
if(TARGET httplib::httplib)
|
||||||
|
set(HTTPLIB_FOUND TRUE)
|
||||||
|
|
||||||
|
# Since the compiled version has a lib, show that in the message
|
||||||
|
if(@HTTPLIB_COMPILE@)
|
||||||
|
# The list of configurations is most likely just 1 unless they installed a debug & release
|
||||||
|
get_target_property(_httplib_configs httplib::httplib "IMPORTED_CONFIGURATIONS")
|
||||||
|
# Need to loop since the "IMPORTED_LOCATION" property isn't want we want.
|
||||||
|
# Instead, we need to find the IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG which has the lib path.
|
||||||
|
foreach(_httplib_conf "${_httplib_configs}")
|
||||||
|
# Grab the path to the lib and sets it to HTTPLIB_LIBRARY
|
||||||
|
get_target_property(HTTPLIB_LIBRARY httplib::httplib "IMPORTED_LOCATION_${_httplib_conf}")
|
||||||
|
# Check if we found it
|
||||||
|
if(HTTPLIB_LIBRARY)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
unset(_httplib_configs)
|
||||||
|
unset(_httplib_conf)
|
||||||
|
|
||||||
|
find_package_message(httplib "Found httplib: ${HTTPLIB_LIBRARY} (found version \"${HTTPLIB_VERSION}\")" "[${HTTPLIB_LIBRARY}][${HTTPLIB_HEADER_PATH}]")
|
||||||
|
else()
|
||||||
|
find_package_message(httplib "Found httplib: ${HTTPLIB_HEADER_PATH} (found version \"${HTTPLIB_VERSION}\")" "[${HTTPLIB_HEADER_PATH}]")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|||||||
168
test/test.cc
168
test/test.cc
@@ -245,7 +245,7 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
auto res =
|
auto res =
|
||||||
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137");
|
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137");
|
||||||
@@ -268,7 +268,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
std::string body;
|
std::string body;
|
||||||
auto res =
|
auto res =
|
||||||
@@ -296,7 +296,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
std::string body;
|
std::string body;
|
||||||
auto res = cli.Get(
|
auto res = cli.Get(
|
||||||
@@ -328,7 +328,7 @@ TEST(RangeTest, FromHTTPBin) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(5);
|
cli.set_connection_timeout(5);
|
||||||
|
|
||||||
{
|
{
|
||||||
httplib::Headers headers;
|
httplib::Headers headers;
|
||||||
@@ -388,7 +388,7 @@ TEST(ConnectionErrorTest, InvalidHost) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
@@ -404,7 +404,7 @@ TEST(ConnectionErrorTest, InvalidPort) {
|
|||||||
auto port = 8080;
|
auto port = 8080;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
@@ -420,7 +420,7 @@ TEST(ConnectionErrorTest, Timeout) {
|
|||||||
auto port = 8080;
|
auto port = 8080;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(2);
|
cli.set_connection_timeout(2);
|
||||||
|
|
||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
@@ -436,7 +436,7 @@ TEST(CancelTest, NoCancel) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(5);
|
cli.set_connection_timeout(5);
|
||||||
|
|
||||||
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
|
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
@@ -456,7 +456,7 @@ TEST(CancelTest, WithCancelSmallPayload) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
|
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
|
||||||
cli.set_timeout_sec(5);
|
cli.set_connection_timeout(5);
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +470,7 @@ TEST(CancelTest, WithCancelLargePayload) {
|
|||||||
auto port = 80;
|
auto port = 80;
|
||||||
httplib::Client cli(host, port);
|
httplib::Client cli(host, port);
|
||||||
#endif
|
#endif
|
||||||
cli.set_timeout_sec(5);
|
cli.set_connection_timeout(5);
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
httplib::Headers headers;
|
httplib::Headers headers;
|
||||||
@@ -1136,6 +1136,10 @@ protected:
|
|||||||
EXPECT_EQ(req.get_param_value("key"), "value");
|
EXPECT_EQ(req.get_param_value("key"), "value");
|
||||||
EXPECT_EQ(req.body, "content");
|
EXPECT_EQ(req.body, "content");
|
||||||
})
|
})
|
||||||
|
.Get("/last-request",
|
||||||
|
[&](const Request & req, Response &/*res*/) {
|
||||||
|
EXPECT_EQ("close", req.get_header_value("Connection"));
|
||||||
|
})
|
||||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
.Get("/gzip",
|
.Get("/gzip",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
@@ -1766,14 +1770,24 @@ TEST_F(ServerTest, GetStreamedEndless) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, ClientStop) {
|
TEST_F(ServerTest, ClientStop) {
|
||||||
thread t = thread([&]() {
|
std::vector<std::thread> threads;
|
||||||
auto res = cli_.Get("/streamed-cancel",
|
for (auto i = 0; i < 3; i++) {
|
||||||
[&](const char *, uint64_t) { return true; });
|
threads.emplace_back(thread([&]() {
|
||||||
ASSERT_TRUE(res == nullptr);
|
auto res = cli_.Get("/streamed-cancel",
|
||||||
});
|
[&](const char *, uint64_t) { return true; });
|
||||||
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
cli_.stop();
|
|
||||||
t.join();
|
while (cli_.is_socket_open()) {
|
||||||
|
cli_.stop();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
for (auto &t : threads) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GetWithRange1) {
|
TEST_F(ServerTest, GetWithRange1) {
|
||||||
@@ -2117,42 +2131,48 @@ TEST_F(ServerTest, HTTP2Magic) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, KeepAlive) {
|
TEST_F(ServerTest, KeepAlive) {
|
||||||
cli_.set_keep_alive_max_count(4);
|
auto res = cli_.Get("/hi");
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("Hello World!", res->body);
|
||||||
|
|
||||||
std::vector<Request> requests;
|
res = cli_.Get("/hi");
|
||||||
Get(requests, "/hi");
|
ASSERT_TRUE(res != nullptr);
|
||||||
Get(requests, "/hi");
|
EXPECT_EQ(200, res->status);
|
||||||
Get(requests, "/hi");
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
Get(requests, "/not-exist");
|
EXPECT_EQ("Hello World!", res->body);
|
||||||
Post(requests, "/empty", "", "text/plain");
|
|
||||||
Post(
|
|
||||||
requests, "/empty", 0,
|
|
||||||
[&](size_t, size_t, httplib::DataSink &) { return true; }, "text/plain");
|
|
||||||
|
|
||||||
std::vector<Response> responses;
|
res = cli_.Get("/hi");
|
||||||
auto ret = cli_.send(requests, responses);
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("Hello World!", res->body);
|
||||||
|
|
||||||
ASSERT_TRUE(ret == true);
|
res = cli_.Get("/not-exist");
|
||||||
ASSERT_TRUE(requests.size() == responses.size());
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(404, res->status);
|
||||||
|
|
||||||
for (size_t i = 0; i < 3; i++) {
|
res = cli_.Post("/empty", "", "text/plain");
|
||||||
auto &res = responses[i];
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ(200, res.status);
|
EXPECT_EQ(200, res->status);
|
||||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
EXPECT_EQ("Hello World!", res.body);
|
EXPECT_EQ("empty", res->body);
|
||||||
}
|
EXPECT_EQ("close", res->get_header_value("Connection"));
|
||||||
|
|
||||||
{
|
res = cli_.Post(
|
||||||
auto &res = responses[3];
|
"/empty", 0, [&](size_t, size_t, httplib::DataSink &) { return true; },
|
||||||
EXPECT_EQ(404, res.status);
|
"text/plain");
|
||||||
}
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("empty", res->body);
|
||||||
|
|
||||||
for (size_t i = 4; i < 6; i++) {
|
cli_.set_keep_alive(false);
|
||||||
auto &res = responses[i];
|
res = cli_.Get("/last-request");
|
||||||
EXPECT_EQ(200, res.status);
|
ASSERT_TRUE(res != nullptr);
|
||||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
EXPECT_EQ(200, res->status);
|
||||||
EXPECT_EQ("empty", res.body);
|
EXPECT_EQ("close", res->get_header_value("Connection"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
@@ -2206,6 +2226,21 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
|||||||
EXPECT_EQ(200, res->status);
|
EXPECT_EQ(200, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GzipWithoutDecompressing) {
|
||||||
|
Headers headers;
|
||||||
|
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||||
|
|
||||||
|
cli_.set_decompress(false);
|
||||||
|
auto res = cli_.Get("/gzip", headers);
|
||||||
|
|
||||||
|
ASSERT_TRUE(res != nullptr);
|
||||||
|
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(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||||
Headers headers;
|
Headers headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
@@ -2279,15 +2314,14 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
|
|||||||
// Sends a raw request to a server listening at HOST:PORT.
|
// Sends a raw request to a server listening at HOST:PORT.
|
||||||
static bool send_request(time_t read_timeout_sec, const std::string &req,
|
static bool send_request(time_t read_timeout_sec, const std::string &req,
|
||||||
std::string *resp = nullptr) {
|
std::string *resp = nullptr) {
|
||||||
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
auto client_sock =
|
||||||
std::string());
|
detail::create_client_socket(HOST, PORT, nullptr,
|
||||||
|
/*timeout_sec=*/5, 0, std::string());
|
||||||
|
|
||||||
if (client_sock == INVALID_SOCKET) { return false; }
|
if (client_sock == INVALID_SOCKET) { return false; }
|
||||||
|
|
||||||
return detail::process_and_close_socket(
|
auto ret = detail::process_client_socket(
|
||||||
true, client_sock, 1, read_timeout_sec, 0, 0, 0,
|
client_sock, read_timeout_sec, 0, 0, 0, [&](Stream &strm) {
|
||||||
[&](Stream &strm, bool /*last_connection*/, bool &
|
|
||||||
/*connection_close*/) -> bool {
|
|
||||||
if (req.size() !=
|
if (req.size() !=
|
||||||
static_cast<size_t>(strm.write(req.data(), req.size()))) {
|
static_cast<size_t>(strm.write(req.data(), req.size()))) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2301,6 +2335,10 @@ static bool send_request(time_t read_timeout_sec, const std::string &req,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
detail::close_socket(client_sock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||||
@@ -2485,8 +2523,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Client client(HOST, PORT);
|
Client client(HOST, PORT);
|
||||||
const Headers headers = {{"Accept", "text/event-stream"},
|
const Headers headers = {{"Accept", "text/event-stream"}};
|
||||||
{"Connection", "Keep-Alive"}};
|
|
||||||
|
|
||||||
auto get_thread = std::thread([&client, &headers]() {
|
auto get_thread = std::thread([&client, &headers]() {
|
||||||
std::shared_ptr<Response> res = client.Get(
|
std::shared_ptr<Response> res = client.Get(
|
||||||
@@ -2712,19 +2749,24 @@ TEST(SSLClientTest, ServerNameIndication) {
|
|||||||
ASSERT_EQ(200, res->status);
|
ASSERT_EQ(200, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SSLClientTest, ServerCertificateVerification) {
|
TEST(SSLClientTest, ServerCertificateVerification1) {
|
||||||
SSLClient cli("google.com");
|
SSLClient cli("google.com");
|
||||||
|
|
||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(301, res->status);
|
ASSERT_EQ(301, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SSLClientTest, ServerCertificateVerification2) {
|
||||||
|
SSLClient cli("google.com");
|
||||||
cli.enable_server_certificate_verification(true);
|
cli.enable_server_certificate_verification(true);
|
||||||
res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SSLClientTest, ServerCertificateVerification3) {
|
||||||
|
SSLClient cli("google.com");
|
||||||
cli.set_ca_cert_path(CA_CERT_FILE);
|
cli.set_ca_cert_path(CA_CERT_FILE);
|
||||||
res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(301, res->status);
|
ASSERT_EQ(301, res->status);
|
||||||
}
|
}
|
||||||
@@ -2774,7 +2816,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
|||||||
|
|
||||||
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||||
auto res = cli.Get("/test");
|
auto res = cli.Get("/test");
|
||||||
cli.set_timeout_sec(30);
|
cli.set_connection_timeout(30);
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(200, res->status);
|
ASSERT_EQ(200, res->status);
|
||||||
|
|
||||||
@@ -2843,7 +2885,7 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
|||||||
|
|
||||||
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
||||||
auto res = cli.Get("/test");
|
auto res = cli.Get("/test");
|
||||||
cli.set_timeout_sec(30);
|
cli.set_connection_timeout(30);
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(200, res->status);
|
ASSERT_EQ(200, res->status);
|
||||||
|
|
||||||
@@ -2867,7 +2909,7 @@ TEST(SSLClientServerTest, ClientCertMissing) {
|
|||||||
|
|
||||||
httplib::SSLClient cli(HOST, PORT);
|
httplib::SSLClient cli(HOST, PORT);
|
||||||
auto res = cli.Get("/test");
|
auto res = cli.Get("/test");
|
||||||
cli.set_timeout_sec(30);
|
cli.set_connection_timeout(30);
|
||||||
ASSERT_TRUE(res == nullptr);
|
ASSERT_TRUE(res == nullptr);
|
||||||
|
|
||||||
svr.stop();
|
svr.stop();
|
||||||
@@ -2889,7 +2931,7 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
|||||||
|
|
||||||
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||||
auto res = cli.Get("/test");
|
auto res = cli.Get("/test");
|
||||||
cli.set_timeout_sec(30);
|
cli.set_connection_timeout(30);
|
||||||
ASSERT_TRUE(res != nullptr);
|
ASSERT_TRUE(res != nullptr);
|
||||||
ASSERT_EQ(200, res->status);
|
ASSERT_EQ(200, res->status);
|
||||||
|
|
||||||
|
|||||||
@@ -222,66 +222,45 @@ void KeepAliveTest(Client& cli, bool basic) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.set_keep_alive_max_count(4);
|
|
||||||
cli.set_follow_location(true);
|
cli.set_follow_location(true);
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
cli.set_digest_auth("hello", "world");
|
cli.set_digest_auth("hello", "world");
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<Request> requests;
|
{
|
||||||
|
auto res = cli.Get("/get");
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto res = cli.Get("/redirect/2");
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
Get(requests, "/get");
|
{
|
||||||
Get(requests, "/redirect/2");
|
std::vector<std::string> paths = {
|
||||||
|
"/digest-auth/auth/hello/world/MD5",
|
||||||
|
"/digest-auth/auth/hello/world/SHA-256",
|
||||||
|
"/digest-auth/auth/hello/world/SHA-512",
|
||||||
|
"/digest-auth/auth-int/hello/world/MD5",
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::string> paths = {
|
for (auto path: paths) {
|
||||||
"/digest-auth/auth/hello/world/MD5",
|
auto res = cli.Get(path.c_str());
|
||||||
"/digest-auth/auth/hello/world/SHA-256",
|
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||||
"/digest-auth/auth/hello/world/SHA-512",
|
EXPECT_EQ(200, res->status);
|
||||||
"/digest-auth/auth-int/hello/world/MD5",
|
}
|
||||||
};
|
|
||||||
|
|
||||||
for (auto path : paths) {
|
|
||||||
Get(requests, path.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int count = 100;
|
int count = 100;
|
||||||
while (count--) {
|
while (count--) {
|
||||||
Get(requests, "/get");
|
auto res = cli.Get("/get");
|
||||||
|
EXPECT_EQ(200, res->status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Response> responses;
|
|
||||||
auto ret = cli.send(requests, responses);
|
|
||||||
ASSERT_TRUE(ret == true);
|
|
||||||
ASSERT_TRUE(requests.size() == responses.size());
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
auto &res = responses[i++];
|
|
||||||
EXPECT_EQ(200, res.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto &res = responses[i++];
|
|
||||||
EXPECT_EQ(200, res.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
int count = static_cast<int>(paths.size());
|
|
||||||
while (count--) {
|
|
||||||
auto &res = responses[i++];
|
|
||||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res.body);
|
|
||||||
EXPECT_EQ(200, res.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; i < responses.size(); i++) {
|
|
||||||
auto &res = responses[i];
|
|
||||||
EXPECT_EQ(200, res.status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
TEST(KeepAliveTest, NoSSLWithBasic) {
|
TEST(KeepAliveTest, NoSSLWithBasic) {
|
||||||
Client cli("httpbin.org");
|
Client cli("httpbin.org");
|
||||||
KeepAliveTest(cli, true);
|
KeepAliveTest(cli, true);
|
||||||
@@ -292,7 +271,6 @@ TEST(KeepAliveTest, SSLWithBasic) {
|
|||||||
KeepAliveTest(cli, true);
|
KeepAliveTest(cli, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
||||||
TEST(KeepAliveTest, NoSSLWithDigest) {
|
TEST(KeepAliveTest, NoSSLWithDigest) {
|
||||||
Client cli("httpbin.org");
|
Client cli("httpbin.org");
|
||||||
KeepAliveTest(cli, false);
|
KeepAliveTest(cli, false);
|
||||||
|
|||||||
Reference in New Issue
Block a user