mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-06-12 09:37:15 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9af1a4a08f | ||
|
|
0654e5dab4 | ||
|
|
62e036f253 | ||
|
|
f0adfb2e0c | ||
|
|
139c816c16 | ||
|
|
9505a76491 | ||
|
|
29fd136afd | ||
|
|
f5598237b2 | ||
|
|
01058659ab | ||
|
|
66f698fab6 | ||
|
|
b9a9df4d73 | ||
|
|
25aa3ca982 | ||
|
|
2d67211183 | ||
|
|
f4c5d94d74 | ||
|
|
63a96aeb20 | ||
|
|
bbb83d12c1 | ||
|
|
2d4b42b70b | ||
|
|
1919d08f71 | ||
|
|
824c02fcd3 | ||
|
|
2c0613f211 | ||
|
|
be45ff1ff1 | ||
|
|
803ebe1e20 | ||
|
|
ba685dbe48 | ||
|
|
49c4c2f9c1 | ||
|
|
58909f5917 | ||
|
|
5982b5c360 | ||
|
|
eb1fe5b191 | ||
|
|
5e01587ed6 | ||
|
|
5935d9fa59 | ||
|
|
5bb4c12c6b | ||
|
|
85637844c9 | ||
|
|
d043b18097 | ||
|
|
31bb13abd2 | ||
|
|
8728db7477 | ||
|
|
1c50ac3667 | ||
|
|
cf386f97fd | ||
|
|
b2203bb05a | ||
|
|
f5b806d995 | ||
|
|
3895210f19 | ||
|
|
d45250fd88 | ||
|
|
528cacdc0d | ||
|
|
ed1b6afa10 | ||
|
|
08fc7085e5 | ||
|
|
8333340e2c | ||
|
|
98a0887571 | ||
|
|
b0a189e50e | ||
|
|
776b3ffbf9 | ||
|
|
a061b97677 | ||
|
|
d359e3a5f7 | ||
|
|
5928e0af1a | ||
|
|
a5005789ff | ||
|
|
fae30af47d | ||
|
|
2feea0c9ab | ||
|
|
a2e4af54b7 | ||
|
|
d0b123be26 | ||
|
|
df138366e4 | ||
|
|
c49441ae64 | ||
|
|
e1506fa186 | ||
|
|
ad9fd3bd93 | ||
|
|
05e0253195 | ||
|
|
da26b517a3 | ||
|
|
2b7a968468 | ||
|
|
240cc85ccb | ||
|
|
129e2f00b8 | ||
|
|
da746c6e67 | ||
|
|
3451da940d | ||
|
|
38a6b3e69f | ||
|
|
d1037ee9fd | ||
|
|
2ece5f116b | ||
|
|
c2b6e4ac04 | ||
|
|
8674555b88 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ tags
|
||||
example/server
|
||||
example/client
|
||||
example/hello
|
||||
example/simplecli
|
||||
example/simplesvr
|
||||
example/benchmark
|
||||
example/redirect
|
||||
|
||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
||||
# Environment
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
# Compiler selection
|
||||
compiler:
|
||||
- clang
|
||||
|
||||
# Build/test steps
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}/test
|
||||
- make all
|
||||
163
CMakeLists.txt
163
CMakeLists.txt
@@ -1,29 +1,160 @@
|
||||
cmake_minimum_required(VERSION 3.7.0)
|
||||
project(httplib)
|
||||
#[[
|
||||
Build options:
|
||||
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
||||
* HTTPLIB_REQUIRE_ZLIB (default off)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
After installation with Cmake, a find_package(httplib) is available.
|
||||
This creates a httplib::httplib target (if found).
|
||||
It can be linked like so:
|
||||
|
||||
# Include
|
||||
target_link_libraries(your_exe httplib::httplib)
|
||||
|
||||
The following will build & install for later use.
|
||||
|
||||
Linux/macOS:
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
sudo cmake --build . --target install
|
||||
|
||||
Windows:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
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.
|
||||
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
||||
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
||||
|
||||
Want to use precompiled headers (Cmake feature since v3.16)?
|
||||
It's as simple as doing the following (before linking):
|
||||
|
||||
target_precompile_headers(httplib::httplib INTERFACE "${HTTPLIB_HEADER_PATH}")
|
||||
]]
|
||||
cmake_minimum_required(VERSION 3.7.0 FATAL_ERROR)
|
||||
project(httplib LANGUAGES CXX)
|
||||
|
||||
# Change as needed to set an OpenSSL minimum version.
|
||||
# This is used in the installed Cmake config file.
|
||||
set(_HTTPLIB_OPENSSL_MIN_VER "1.1.1")
|
||||
|
||||
# Allow for a build to require OpenSSL to pass, instead of just being optional
|
||||
option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF)
|
||||
option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
|
||||
# Allow for a build to casually enable OpenSSL/ZLIB support, but silenty continue if not found.
|
||||
# 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_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable compression support." ON)
|
||||
|
||||
# TODO: implement the option of option to correctly split, building, and export with the split.py script.
|
||||
# option(HTTPLIB_SPLIT "Uses a Python script to split the header into a header & source file." OFF)
|
||||
|
||||
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
||||
find_package(Threads REQUIRED)
|
||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} REQUIRED)
|
||||
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
||||
# Look quietly since it's optional are optional
|
||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} QUIET)
|
||||
endif()
|
||||
if(HTTPLIB_REQUIRE_ZLIB)
|
||||
find_package(ZLIB REQUIRED)
|
||||
elseif(HTTPLIB_USE_ZLIB_IF_AVAILABLE)
|
||||
find_package(ZLIB QUIET)
|
||||
endif()
|
||||
|
||||
# Used for default, common dirs that the end-user can change (if needed)
|
||||
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
|
||||
include(GNUInstallDirs)
|
||||
include(ExternalProject)
|
||||
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11)
|
||||
# Lets you address the target with httplib::httplib
|
||||
# Only useful if building in-tree, versus using it from an installation.
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Might be missing some, but this list is somewhat comprehensive
|
||||
target_compile_features(${PROJECT_NAME} INTERFACE
|
||||
cxx_std_11
|
||||
cxx_nullptr
|
||||
cxx_noexcept
|
||||
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
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT httplibConfig
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
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>
|
||||
)
|
||||
|
||||
install(EXPORT httplibConfig DESTINATION share/httplib/cmake)
|
||||
# Auto-define the optional support if those packages were found
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE
|
||||
$<$<BOOL:${ZLIB_FOUND}>:CPPHTTPLIB_ZLIB_SUPPORT>
|
||||
$<$<BOOL:${OPENSSL_FOUND}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
||||
)
|
||||
|
||||
export(TARGETS ${PROJECT_NAME} FILE httplibConfig.cmake)
|
||||
# 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
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}")
|
||||
else()
|
||||
# On Non-Windows, it should be /usr/lib/cmake/<name>/<name>Config.cmake
|
||||
# NOTE: This may or may not work for macOS...
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
#add_subdirectory(example)
|
||||
#add_subdirectory(test)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Configures the meta-file httplibConfig.cmake.in to replace variables with paths/values/etc.
|
||||
configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
|
||||
# Passes the includedir install path
|
||||
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
||||
# There aren't any components, so don't use the macro
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
# Creates the export httplibTargets.cmake
|
||||
# This is strictly what holds compilation requirements
|
||||
# and linkage information (doesn't find deps though).
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT httplibTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
install(FILES httplib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
|
||||
# NOTE: This path changes depending on if it's on Windows or Linux
|
||||
install(EXPORT httplibTargets
|
||||
# Puts the targets into the httplib namespace
|
||||
# So this makes httplib::httplib linkable after doing find_package(httplib)
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
|
||||
61
README.md
61
README.md
@@ -2,7 +2,6 @@ cpp-httplib
|
||||
===========
|
||||
|
||||
[](https://github.com/yhirose/cpp-httplib/actions)
|
||||
[](https://travis-ci.org/yhirose/cpp-httplib)
|
||||
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
||||
|
||||
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
@@ -172,16 +171,17 @@ svr.Post("/content_receiver",
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
const uint64_t DATA_CHUNK_SIZE = 4;
|
||||
const size_t DATA_CHUNK_SIZE = 4;
|
||||
|
||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
auto data = new std::string("abcdefg");
|
||||
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
||||
[data](size_t offset, size_t length, DataSink &sink) {
|
||||
const auto &d = *data;
|
||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
},
|
||||
[data] { delete data; });
|
||||
});
|
||||
@@ -192,11 +192,12 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_chunked_content_provider(
|
||||
[](uint64_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done();
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done();
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -291,7 +292,7 @@ auto res = cli.Get("/hi", headers);
|
||||
std::string body;
|
||||
|
||||
auto res = cli.Get("/large-data",
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -364,6 +365,35 @@ res = cli.Options("/resource/foo");
|
||||
```c++
|
||||
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
||||
```
|
||||
### Receive content with Content receiver
|
||||
|
||||
```cpp
|
||||
std::string body;
|
||||
auto res = cli.Get(
|
||||
"/stream", Headers(),
|
||||
[&](const Response &response) {
|
||||
EXPECT_EQ(200, response.status);
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
});
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
std::string body = ...;
|
||||
auto res = cli_.Post(
|
||||
"/stream", body.size(),
|
||||
[](size_t offset, size_t length, DataSink &sink) {
|
||||
sink.write(body.data() + offset, length);
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
"text/plain");
|
||||
```
|
||||
|
||||
### With Progress Callback
|
||||
|
||||
```cpp
|
||||
@@ -437,6 +467,15 @@ 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;
|
||||
std::string data("abcdefg");
|
||||
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;
|
||||
if (cli.send(requests, responses)) {
|
||||
for (const auto& res: responses) {
|
||||
@@ -486,7 +525,7 @@ cli.enable_server_certificate_verification(true);
|
||||
Zlib Support
|
||||
------------
|
||||
|
||||
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`.
|
||||
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. `libz` should be linked.
|
||||
|
||||
The server applies gzip compression to the following MIME type contents:
|
||||
|
||||
@@ -535,6 +574,8 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
Note: Cygwin on Windows is not supported.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
||||
19
appveyor.yml
19
appveyor.yml
@@ -1,9 +1,14 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2017
|
||||
build_script:
|
||||
- cmd: >-
|
||||
cd test
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
build_script:
|
||||
- cmd: >-
|
||||
cd test
|
||||
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Release;Platform=%PLATFORM%
|
||||
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build /p:Configuration=Debug;Platform=Win32
|
||||
test_script:
|
||||
- cmd: Debug\test.exe
|
||||
- cmd: x64\Release\test.exe
|
||||
|
||||
@@ -5,7 +5,7 @@ OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
all: server client hello simplesvr upload redirect sse benchmark
|
||||
all: server client hello simplecli simplesvr upload redirect sse benchmark
|
||||
|
||||
server : server.cc ../httplib.h Makefile
|
||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
@@ -16,6 +16,9 @@ client : client.cc ../httplib.h Makefile
|
||||
hello : hello.cc ../httplib.h Makefile
|
||||
$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
simplecli : simplecli.cc ../httplib.h Makefile
|
||||
$(CXX) -o simplecli $(CXXFLAGS) simplecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
simplesvr : simplesvr.cc ../httplib.h Makefile
|
||||
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
@@ -36,4 +39,4 @@ pem:
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplesvr upload redirect sse benchmark *.pem
|
||||
rm server client hello simplecli simplesvr upload redirect sse benchmark *.pem
|
||||
|
||||
@@ -15,5 +15,5 @@ int main(void) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
svr.listen("localhost", 8080);
|
||||
}
|
||||
|
||||
32
example/simplecli.cc
Normal file
32
example/simplecli.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// simplecli.cc
|
||||
//
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
|
||||
#define CA_CERT_FILE "./ca-bundle.crt"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto res = httplib::Client2("https://localhost:8080")
|
||||
.set_ca_cert_path(CA_CERT_FILE)
|
||||
// .enable_server_certificate_verification(true)
|
||||
.Get("/hi");
|
||||
#else
|
||||
auto res = httplib::Client2("http://localhost:8080").Get("/hi");
|
||||
#endif
|
||||
|
||||
if (res) {
|
||||
cout << res->status << endl;
|
||||
cout << res->get_header_value("Content-Type") << endl;
|
||||
cout << res->body << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -80,15 +80,19 @@ int main(void) {
|
||||
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event1..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||
ed.wait_event(&sink);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event2..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||
ed.wait_event(&sink);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
thread t([&] {
|
||||
|
||||
38
httplibConfig.cmake.in
Normal file
38
httplibConfig.cmake.in
Normal file
@@ -0,0 +1,38 @@
|
||||
# Generates a macro to auto-configure everything
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# We add find_dependency calls here to not make the end-user have to call them.
|
||||
find_dependency(Threads REQUIRED)
|
||||
if(@HTTPLIB_REQUIRE_OPENSSL@)
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
||||
# Lets you check if these options were correctly enabled for your install
|
||||
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
||||
elseif(@HTTPLIB_USE_OPENSSL_IF_AVAILABLE@)
|
||||
# Look quietly since it's optional
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ QUIET)
|
||||
# Lets you check if these options were correctly enabled for your install
|
||||
set(HTTPLIB_IS_USING_OPENSSL @OPENSSL_FOUND@)
|
||||
else()
|
||||
set(HTTPLIB_IS_USING_OPENSSL FALSE)
|
||||
endif()
|
||||
if(@HTTPLIB_REQUIRE_ZLIB@)
|
||||
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()
|
||||
|
||||
# Lets the end-user find the header path if needed
|
||||
# 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")
|
||||
|
||||
# Brings in the target library
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||
552
test/test.cc
552
test/test.cc
@@ -334,7 +334,7 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
httplib::Headers headers;
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
httplib::Headers headers = {httplib::make_range_header({{1, -1}})};
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "bcdefghijklmnopqrstuvwxyzabcdef");
|
||||
EXPECT_EQ("bcdefghijklmnopqrstuvwxyzabcdef", res->body);
|
||||
EXPECT_EQ(206, res->status);
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
httplib::Headers headers = {httplib::make_range_header({{1, 10}})};
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "bcdefghijk");
|
||||
EXPECT_EQ("bcdefghijk", res->body);
|
||||
EXPECT_EQ(206, res->status);
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
httplib::Headers headers = {httplib::make_range_header({{0, 31}})};
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
httplib::Headers headers = {httplib::make_range_header({{0, -1}})};
|
||||
auto res = cli.Get("/range/32", headers);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ TEST(CancelTest, NoCancel) {
|
||||
|
||||
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body, "abcdefghijklmnopqrstuvwxyzabcdef");
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -501,8 +501,8 @@ TEST(BaseAuthTest, FromHTTPWatch) {
|
||||
cli.Get("/basic-auth/hello/world",
|
||||
{httplib::make_basic_authentication_header("hello", "world")});
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body,
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -510,8 +510,8 @@ TEST(BaseAuthTest, FromHTTPWatch) {
|
||||
cli.set_basic_auth("hello", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body,
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -554,8 +554,8 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body,
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
@@ -563,15 +563,17 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("bad", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
// NOTE: Until httpbin.org fixes issue #46, the following test is commented
|
||||
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// cli.set_digest_auth("bad", "world");
|
||||
// for (auto path : paths) {
|
||||
// auto res = cli.Get(path.c_str());
|
||||
// ASSERT_TRUE(res != nullptr);
|
||||
// EXPECT_EQ(400, res->status);
|
||||
// }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -655,9 +657,82 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
auto res =
|
||||
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr8080;
|
||||
Server svr8081;
|
||||
|
||||
svr8080.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_redirect("http://localhost:8081/2");
|
||||
});
|
||||
|
||||
svr8081.Get("/2", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto thread8080 = std::thread([&]() { svr8080.listen("localhost", 8080); });
|
||||
|
||||
auto thread8081 = std::thread([&]() { svr8081.listen("localhost", 8081); });
|
||||
|
||||
while (!svr8080.is_running() || !svr8081.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", 8080);
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/1");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
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());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Server, BindDualStack) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto thread = std::thread([&]() { svr.listen("::", PORT); });
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli("127.0.0.1", PORT);
|
||||
|
||||
auto res = cli.Get("/1");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
{
|
||||
Client cli("::1", PORT);
|
||||
|
||||
auto res = cli.Get("/1");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(Server, BindAndListenSeparately) {
|
||||
Server svr;
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
@@ -732,9 +807,18 @@ protected:
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
res.set_content("slow", "text/plain");
|
||||
})
|
||||
.Post("/slowpost",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
res.set_content("slow", "text/plain");
|
||||
})
|
||||
.Get("/remote_addr",
|
||||
[&](const Request &req, Response &res) {
|
||||
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
|
||||
EXPECT_TRUE(req.has_header("REMOTE_PORT"));
|
||||
EXPECT_EQ(req.remote_addr, req.get_header_value("REMOTE_ADDR"));
|
||||
EXPECT_EQ(req.remote_port,
|
||||
std::stoi(req.get_header_value("REMOTE_PORT")));
|
||||
res.set_content(remote_addr.c_str(), "text/plain");
|
||||
})
|
||||
.Get("/endwith%",
|
||||
@@ -749,6 +833,13 @@ protected:
|
||||
})
|
||||
.Get("/", [&](const Request & /*req*/,
|
||||
Response &res) { res.set_redirect("/hi"); })
|
||||
.Post("/1", [](const Request & /*req*/,
|
||||
Response &res) { res.set_redirect("/2", 303); })
|
||||
.Get("/2",
|
||||
[](const Request & /*req*/, Response &res) {
|
||||
res.set_content("redirected.", "text/plain");
|
||||
res.status = 200;
|
||||
})
|
||||
.Post("/person",
|
||||
[&](const Request &req, Response &res) {
|
||||
if (req.has_param("name") && req.has_param("note")) {
|
||||
@@ -787,35 +878,38 @@ protected:
|
||||
.Get("/streamed-chunked",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_chunked_content_provider(
|
||||
[](uint64_t /*offset*/, DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
sink.write("123", 3);
|
||||
sink.write("456", 3);
|
||||
sink.write("789", 3);
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "123";
|
||||
sink.os << "456";
|
||||
sink.os << "789";
|
||||
sink.done();
|
||||
return true;
|
||||
});
|
||||
})
|
||||
.Get("/streamed-chunked2",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
auto i = new int(0);
|
||||
res.set_chunked_content_provider(
|
||||
[i](uint64_t /*offset*/, DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
[i](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
switch (*i) {
|
||||
case 0: sink.write("123", 3); break;
|
||||
case 1: sink.write("456", 3); break;
|
||||
case 2: sink.write("789", 3); break;
|
||||
case 0: sink.os << "123"; break;
|
||||
case 1: sink.os << "456"; break;
|
||||
case 2: sink.os << "789"; break;
|
||||
case 3: sink.done(); break;
|
||||
}
|
||||
(*i)++;
|
||||
return true;
|
||||
},
|
||||
[i] { delete i; });
|
||||
})
|
||||
.Get("/streamed",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content_provider(
|
||||
6, [](uint64_t offset, uint64_t /*length*/, DataSink &sink) {
|
||||
sink.write(offset < 3 ? "a" : "b", 1);
|
||||
6, [](size_t offset, size_t /*length*/, DataSink &sink) {
|
||||
sink.os << (offset < 3 ? "a" : "b");
|
||||
return true;
|
||||
});
|
||||
})
|
||||
.Get("/streamed-with-range",
|
||||
@@ -823,25 +917,26 @@ protected:
|
||||
auto data = new std::string("abcdefg");
|
||||
res.set_content_provider(
|
||||
data->size(),
|
||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
[data](size_t offset, size_t length, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
size_t DATA_CHUNK_SIZE = 4;
|
||||
const auto &d = *data;
|
||||
auto out_len =
|
||||
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
|
||||
sink.write(&d[static_cast<size_t>(offset)], out_len);
|
||||
return true;
|
||||
},
|
||||
[data] { delete data; });
|
||||
})
|
||||
.Get("/streamed-cancel",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content_provider(size_t(-1), [](uint64_t /*offset*/,
|
||||
uint64_t /*length*/,
|
||||
DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
std::string data = "data_chunk";
|
||||
sink.write(data.data(), data.size());
|
||||
});
|
||||
res.set_content_provider(
|
||||
size_t(-1),
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "data_chunk";
|
||||
return true;
|
||||
});
|
||||
})
|
||||
.Get("/with-range",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
@@ -864,13 +959,13 @@ protected:
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("text1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("text default", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("text2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("aωb", file.content);
|
||||
}
|
||||
|
||||
@@ -883,7 +978,7 @@ protected:
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("file3");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||
EXPECT_EQ(0u, file.content.size());
|
||||
}
|
||||
@@ -891,8 +986,24 @@ protected:
|
||||
.Post("/empty",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "");
|
||||
EXPECT_EQ("text/plain", req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||
res.set_content("empty", "text/plain");
|
||||
})
|
||||
.Post("/empty-no-content-type",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "");
|
||||
EXPECT_FALSE(req.has_header("Content-Type"));
|
||||
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||
res.set_content("empty-no-content-type", "text/plain");
|
||||
})
|
||||
.Put("/empty-no-content-type",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "");
|
||||
EXPECT_FALSE(req.has_header("Content-Type"));
|
||||
EXPECT_EQ("0", req.get_header_value("Content-Length"));
|
||||
res.set_content("empty-no-content-type", "text/plain");
|
||||
})
|
||||
.Put("/put",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "PUT");
|
||||
@@ -913,10 +1024,10 @@ protected:
|
||||
res.set_content("DELETE", "text/plain");
|
||||
})
|
||||
.Delete("/delete-body",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "content");
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "content");
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
.Options(R"(\*)",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
|
||||
@@ -963,13 +1074,13 @@ protected:
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "text1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("text default", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "text2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("aωb", file.content);
|
||||
}
|
||||
|
||||
@@ -982,7 +1093,7 @@ protected:
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "file3");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||
EXPECT_EQ(0u, file.content.size());
|
||||
}
|
||||
@@ -1047,13 +1158,13 @@ protected:
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("key1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("test", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("key2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_TRUE(file.filename.empty());
|
||||
EXPECT_EQ("--abcdefg123", file.content);
|
||||
}
|
||||
})
|
||||
@@ -1116,6 +1227,14 @@ TEST_F(ServerTest, GetMethod302) {
|
||||
EXPECT_EQ("/hi", res->get_header_value("Location"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethod302Redirect) {
|
||||
cli_.set_follow_location(true);
|
||||
auto res = cli_.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethod404) {
|
||||
auto res = cli_.Get("/invalid");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1127,7 +1246,7 @@ TEST_F(ServerTest, HeadMethod200) {
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("", res->body);
|
||||
EXPECT_TRUE(res->body.empty());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, HeadMethod200Static) {
|
||||
@@ -1136,14 +1255,14 @@ TEST_F(ServerTest, HeadMethod200Static) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length")));
|
||||
EXPECT_EQ("", res->body);
|
||||
EXPECT_TRUE(res->body.empty());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, HeadMethod404) {
|
||||
auto res = cli_.Head("/invalid");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
EXPECT_EQ("", res->body);
|
||||
EXPECT_TRUE(res->body.empty());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodPersonJohn) {
|
||||
@@ -1229,6 +1348,20 @@ TEST_F(ServerTest, PostEmptyContent) {
|
||||
ASSERT_EQ("empty", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostEmptyContentWithNoContentType) {
|
||||
auto res = cli_.Post("/empty-no-content-type");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("empty-no-content-type", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutEmptyContentWithNoContentType) {
|
||||
auto res = cli_.Put("/empty-no-content-type");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("empty-no-content-type", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodDir) {
|
||||
auto res = cli_.Get("/dir/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1315,6 +1448,21 @@ TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMethod303) {
|
||||
auto res = cli_.Post("/1", "body", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(303, res->status);
|
||||
EXPECT_EQ("/2", res->get_header_value("Location"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMethod303Redirect) {
|
||||
cli_.set_follow_location(true);
|
||||
auto res = cli_.Post("/1", "body", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("redirected.", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
||||
auto res = cli_.Get("/dir/test.abcde");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1617,6 +1765,17 @@ TEST_F(ServerTest, GetStreamedEndless) {
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, ClientStop) {
|
||||
thread t = thread([&]() {
|
||||
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));
|
||||
cli_.stop();
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetWithRange1) {
|
||||
auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}});
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1731,6 +1890,33 @@ TEST_F(ServerTest, SlowRequest) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, SlowPost) {
|
||||
char buffer[64 * 1024];
|
||||
memset(buffer, 0x42, sizeof(buffer));
|
||||
|
||||
auto res = cli_.Post(
|
||||
"/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
|
||||
cli_.set_write_timeout(0, 0);
|
||||
res = cli_.Post(
|
||||
"/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_FALSE(res != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, Put) {
|
||||
auto res = cli_.Put("/put", "PUT", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1742,8 +1928,9 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
sink.write("PUT", 3);
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
@@ -1752,14 +1939,26 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderAbort) {
|
||||
auto res = cli_.Post(
|
||||
"/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||
return false;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
ASSERT_TRUE(sink.is_writable());
|
||||
sink.write("PUT", 3);
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
@@ -1768,6 +1967,18 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Post(
|
||||
"/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||
return false;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
||||
@@ -1914,6 +2125,9 @@ TEST_F(ServerTest, KeepAlive) {
|
||||
Get(requests, "/hi");
|
||||
Get(requests, "/not-exist");
|
||||
Post(requests, "/empty", "", "text/plain");
|
||||
Post(
|
||||
requests, "/empty", 0,
|
||||
[&](size_t, size_t, httplib::DataSink &) { return true; }, "text/plain");
|
||||
|
||||
std::vector<Response> responses;
|
||||
auto ret = cli_.send(requests, responses);
|
||||
@@ -1933,8 +2147,8 @@ TEST_F(ServerTest, KeepAlive) {
|
||||
EXPECT_EQ(404, res.status);
|
||||
}
|
||||
|
||||
{
|
||||
auto &res = responses[4];
|
||||
for (size_t i = 4; i < 6; i++) {
|
||||
auto &res = responses[i];
|
||||
EXPECT_EQ(200, res.status);
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("empty", res.body);
|
||||
@@ -1962,7 +2176,7 @@ TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
|
||||
auto res = cli_.Get("/gzip", headers);
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||
@@ -2003,7 +2217,7 @@ TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||
@@ -2063,14 +2277,15 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
|
||||
#endif
|
||||
|
||||
// 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) {
|
||||
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
||||
std::string());
|
||||
|
||||
if (client_sock == INVALID_SOCKET) { return false; }
|
||||
|
||||
return detail::process_and_close_socket(
|
||||
true, client_sock, 1, read_timeout_sec, 0,
|
||||
true, client_sock, 1, read_timeout_sec, 0, 0, 0,
|
||||
[&](Stream &strm, bool /*last_connection*/, bool &
|
||||
/*connection_close*/) -> bool {
|
||||
if (req.size() !=
|
||||
@@ -2081,7 +2296,9 @@ static bool send_request(time_t read_timeout_sec, const std::string &req) {
|
||||
char buf[512];
|
||||
|
||||
detail::stream_line_reader line_reader(strm, buf, sizeof(buf));
|
||||
while (line_reader.getline()) {}
|
||||
while (line_reader.getline()) {
|
||||
if (resp) { *resp += line_reader.ptr(); }
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -2113,11 +2330,15 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||
}
|
||||
|
||||
// Sends a raw request and verifies that there isn't a crash or exception.
|
||||
static void test_raw_request(const std::string &req) {
|
||||
static void test_raw_request(const std::string &req,
|
||||
std::string *out = nullptr) {
|
||||
Server svr;
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
svr.Put("/put_hi", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
// Server read timeout must be longer than the client read timeout for the
|
||||
// bug to reproduce, probably to force the server to process a request
|
||||
@@ -2130,7 +2351,7 @@ static void test_raw_request(const std::string &req) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
|
||||
ASSERT_TRUE(send_request(client_read_timeout_sec, req, out));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_TRUE(listen_thread_ok);
|
||||
@@ -2142,8 +2363,8 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
test_raw_request(
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
" : "
|
||||
" "
|
||||
);
|
||||
" "
|
||||
" ");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
|
||||
@@ -2184,17 +2405,77 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
|
||||
"&&&%%%");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ExcessiveWhitespaceInUnparseableHeaderLine) {
|
||||
// Make sure this doesn't crash the server.
|
||||
// In a previous version of the header line regex, the "\r" rendered the line
|
||||
// unparseable and the regex engine repeatedly backtracked, trying to look for
|
||||
// a new position where the leading white space ended and the field value
|
||||
// began.
|
||||
// The crash occurs with libc++ but not libstdc++.
|
||||
test_raw_request("GET /hi HTTP/1.1\r\n"
|
||||
"a:" +
|
||||
std::string(2000, ' ') + '\r' + std::string(20, 'z') +
|
||||
"\r\n"
|
||||
"\r\n");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, InvalidFirstChunkLengthInRequest) {
|
||||
std::string out;
|
||||
|
||||
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"nothex\r\n",
|
||||
&out);
|
||||
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, InvalidSecondChunkLengthInRequest) {
|
||||
std::string out;
|
||||
|
||||
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"3\r\n"
|
||||
"xyz\r\n"
|
||||
"NaN\r\n",
|
||||
&out);
|
||||
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ChunkLengthTooHighInRequest) {
|
||||
std::string out;
|
||||
|
||||
test_raw_request("PUT /put_hi HTTP/1.1\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
// Length is too large for 64 bits.
|
||||
"1ffffffffffffffff\r\n"
|
||||
"xyz\r\n",
|
||||
&out);
|
||||
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, InvalidHeaderTextWithExtraCR) {
|
||||
test_raw_request("GET /hi HTTP/1.1\r\n"
|
||||
"Content-Type: text/plain\r\n\r");
|
||||
}
|
||||
|
||||
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/events", [](const Request & /*req*/, Response &res) {
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_header("Cache-Control", "no-cache");
|
||||
res.set_chunked_content_provider([](size_t offset, const DataSink &sink) {
|
||||
res.set_chunked_content_provider([](size_t offset, DataSink &sink) {
|
||||
char buffer[27];
|
||||
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
|
||||
sink.write(buffer, size);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2500,6 +2781,80 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
||||
X509 *server_cert;
|
||||
EVP_PKEY *server_private_key;
|
||||
X509_STORE *client_ca_cert_store;
|
||||
X509 *client_cert;
|
||||
EVP_PKEY *client_private_key;
|
||||
|
||||
FILE *f = fopen(SERVER_CERT_FILE, "r+");
|
||||
server_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(SERVER_PRIVATE_KEY_FILE, "r+");
|
||||
server_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CA_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
client_ca_cert_store = X509_STORE_new();
|
||||
X509_STORE_add_cert(client_ca_cert_store, client_cert);
|
||||
X509_free(client_cert);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_PRIVATE_KEY_FILE, "r+");
|
||||
client_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
SSLServer svr(server_cert, server_private_key, client_ca_cert_store);
|
||||
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));
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
X509_free(server_cert);
|
||||
EVP_PKEY_free(server_private_key);
|
||||
X509_free(client_cert);
|
||||
EVP_PKEY_free(client_private_key);
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, ClientCertMissing) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||
CLIENT_CA_CERT_DIR);
|
||||
@@ -2540,13 +2895,6 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
/* Cannot test this case as there is no external access to SSL object to check
|
||||
SSL_get_peer_certificate() == NULL TEST(SSLClientServerTest,
|
||||
ClientCAPathRequired) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
||||
nullptr, CLIENT_CA_CERT_DIR);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -2555,3 +2903,51 @@ TEST(CleanupTest, WSACleanup) {
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(InvalidScheme, SimpleInterface) {
|
||||
httplib::Client2 cli("scheme://yahoo.com");
|
||||
ASSERT_FALSE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(NoScheme, SimpleInterface) {
|
||||
httplib::Client2 cli("yahoo.com");
|
||||
ASSERT_FALSE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
httplib::Client2 cli("http://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(301, res->status);
|
||||
|
||||
cli.set_follow_location(true);
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest3, SimpleInterface) {
|
||||
httplib::Client2 cli("https://yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(301, res->status);
|
||||
|
||||
cli.set_follow_location(true);
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
auto res =
|
||||
httplib::Client2("https://httpbin.org")
|
||||
.set_follow_location(true)
|
||||
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
@@ -40,7 +40,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@@ -69,13 +69,13 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\lib\VC;C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
@@ -84,14 +84,14 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
@@ -108,7 +108,7 @@
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
@@ -118,12 +118,12 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
@@ -144,7 +144,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
@@ -160,7 +160,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -185,15 +185,17 @@ void DigestAuthTestFromHTTPWatch(Client& cli) {
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("bad", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
// NOTE: Until httpbin.org fixes issue #46, the following test is commented
|
||||
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// cli.set_digest_auth("bad", "world");
|
||||
// for (auto path : paths) {
|
||||
// auto res = cli.Get(path.c_str());
|
||||
// ASSERT_TRUE(res != nullptr);
|
||||
// EXPECT_EQ(401, res->status);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +268,7 @@ void KeepAliveTest(Client& cli, bool basic) {
|
||||
|
||||
|
||||
{
|
||||
int count = paths.size();
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user