Compare commits

...

13 Commits

Author SHA1 Message Date
yhirose cf084e1db1 Fixed example build errors 2020-08-08 00:10:08 -04:00
KTGH e0e5898601 Overhaul FindBrotli to fix weird issues (#604)
Should get rid of the issue about not being able to create an ALIAS on
MinGW, as well as the "No REQUIRED_VARS" issue.

Fixes #603 (hopefully)
2020-08-06 07:08:29 -04:00
yhirose dfec2de5b5 Update README 2020-08-03 23:37:05 -04:00
yhirose 04002d57bd Added set_default_headers (Fix #600) 2020-08-03 22:05:37 -04:00
yhirose 38a7706c8b Removed old Keep-Alive functions 2020-08-03 22:04:40 -04:00
KTGH abaf875c42 Fix FindBrotli when no Brotli installed (#598)
Woops.

Ref https://github.com/yhirose/cpp-httplib/issues/582#issuecomment-667537002
2020-08-01 17:08:49 -04:00
PixlRainbow 5f76cb01c7 fix #592 -- add check for static-linked OpenSSL (#595) 2020-08-01 08:10:42 -04:00
yhirose ae54e833ea Code cleanup 2020-07-31 23:48:42 -04:00
yhirose 0dd3659de5 Updated README 2020-07-31 18:54:53 -04:00
KTGH 999f6abd2c Fix for Cmake on systems without Git (#594)
If you didn't have Git installed, execute_process never declared the
error variable, which led to it never parsing the header for a version.
So if Git wasn't installed, the version variable never got declared,
which caused errors.

This fixes that.
2020-07-31 13:46:12 -04:00
KTGH 48da75dd35 Fix FindBrotli for static libs (#593)
It wasn't linking them.
2020-07-31 13:45:21 -04:00
yhirose 4f84eeb298 Bearer Token auth support. Fix #484 2020-07-31 12:37:14 -04:00
yhirose a5b4cfadb9 Brotli suport on server. Fix #578 2020-07-31 10:23:57 -04:00
8 changed files with 584 additions and 389 deletions
+14 -10
View File
@@ -1,6 +1,6 @@
#[[
Build options:
* BUILD_SHARED_LIBS (default off) builds as a static library (if HTTPLIB_COMPILE is ON)
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
* HTTPLIB_REQUIRE_OPENSSL (default off)
@@ -60,17 +60,21 @@
]]
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
# 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
)
# On systems without Git installed, there were errors since execute_process seemed to not throw an error without it?
find_package(Git QUIET)
if(Git_FOUND)
# 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_EXECUTABLE} describe --tags --abbrev=0
OUTPUT_VARIABLE _raw_version_string
ERROR_VARIABLE _git_tag_error
)
endif()
# execute_process can fail silenty, so check for an error
# if there was an error, just use the user agent as a version
if(_git_tag_error)
message(WARNING "cpp-httplib failed to find the latest git tag, falling back to using user agent as the version.")
if(_git_tag_error OR NOT Git_FOUND)
message(WARNING "cpp-httplib failed to find the latest Git tag, falling back to using user agent as the version.")
# Get the user agent and use it as a version
# This gets the string with the user agent from the header.
# This is so the maintainer doesn't actually need to update this manually.
@@ -101,7 +105,7 @@ if(HTTPLIB_COMPILE)
endif()
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF)
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli compression support." ON)
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
# 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)
+13
View File
@@ -311,6 +311,13 @@ httplib::Headers headers = {
};
auto res = cli.Get("/hi", headers);
```
or
```c++
cli.set_default_headers({
{ "Accept-Encoding", "gzip, deflate" }
});
auto res = cli.Get("/hi");
```
### POST
@@ -447,6 +454,9 @@ cli.set_basic_auth("user", "pass");
// Digest Authentication
cli.set_digest_auth("user", "pass");
// Bearer Token Authentication
cli.set_bearer_token_auth("token");
```
NOTE: OpenSSL is required for Digest Authentication.
@@ -461,6 +471,9 @@ cli.set_proxy_basic_auth("user", "pass");
// Digest Authentication
cli.set_proxy_digest_auth("user", "pass");
// Bearer Token Authentication
cli.set_proxy_bearer_token_auth("pass");
```
NOTE: OpenSSL is required for Digest Authentication.
+128 -135
View File
@@ -5,181 +5,174 @@
# The targets will have the same names, but it will use the static libs.
#
# Valid find_package COMPONENTS names: "decoder", "encoder", and "common"
# Note that if you're requiring "decoder" or "encoder", then "common" will be automatically added as required.
#
# Defines the libraries (if found): Brotli::decoder, Brotli::encoder, Brotli::common
# and the includes path variable: Brotli_INCLUDE_DIR
#
# If it's failing to find the libraries, try setting BROTLI_ROOT_DIR to the folder containing your library & include dir.
function(brotli_err_msg _err_msg)
# If they asked for a specific version, warn/fail since we don't support it.
# TODO: if they start distributing the version somewhere, implement finding it.
# But currently there's a version header that doesn't seem to get installed.
if(Brotli_FIND_VERSION)
set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!")
# If the package is required, throw a fatal error
# Otherwise, if not running quietly, we throw a warning
if(Brotli_FIND_REQUIRED)
message(FATAL_ERROR "${_err_msg}")
message(FATAL_ERROR "${_brotli_version_error_msg}")
elseif(NOT Brotli_FIND_QUIETLY)
message(WARNING "${_err_msg}")
message(WARNING "${_brotli_version_error_msg}")
endif()
endfunction()
# If they asked for a specific version, warn/fail since we don't support it.
if(Brotli_FIND_VERSION)
brotli_err_msg("FindBrotli.cmake doesn't have version support!")
endif()
# Since both decoder & encoder require the common lib (I think), force its requirement..
# Since both decoder & encoder require the common lib, force its requirement..
# if the user is requiring either of those other libs.
if(Brotli_FIND_REQUIRED_decoder OR Brotli_FIND_REQUIRED_encoder)
set(Brotli_FIND_REQUIRED_common TRUE)
endif()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
# Credit to FindOpenSSL.cmake for this
if(BROTLI_USE_STATIC_LIBS)
set(_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
endif()
# Make PkgConfig optional, since some users (mainly Windows) don't have it.
# But it's a lot more clean than manually using find_library.
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
if(BROTLI_USE_STATIC_LIBS)
pkg_check_modules(Brotli_common_STATIC QUIET IMPORTED_TARGET libbrotlicommon)
pkg_check_modules(Brotli_decoder_STATIC QUIET IMPORTED_TARGET libbrotlidec)
pkg_check_modules(Brotli_encoder_STATIC QUIET IMPORTED_TARGET libbrotlienc)
else()
pkg_check_modules(Brotli_common QUIET IMPORTED_TARGET libbrotlicommon)
pkg_check_modules(Brotli_decoder QUIET IMPORTED_TARGET libbrotlidec)
pkg_check_modules(Brotli_encoder QUIET IMPORTED_TARGET libbrotlienc)
endif()
endif()
# Only used if the PkgConfig libraries aren't used.
find_path(Brotli_INCLUDE_DIR
NAMES "brotli/decode.h" "brotli/encode.h"
PATH_SUFFIXES "include" "includes"
NAMES
"brotli/decode.h"
"brotli/encode.h"
HINTS
${BROTLI_ROOT_DIR}
PATH_SUFFIXES
"include"
"includes"
DOC "The path to Brotli's include directory."
)
# Hides this var from the GUI
mark_as_advanced(Brotli_INCLUDE_DIR)
# Also check if Brotli_decoder was defined, as it can be passed by the end-user
if(NOT TARGET PkgConfig::Brotli_decoder AND NOT Brotli_decoder)
if(BROTLI_USE_STATIC_LIBS)
list(APPEND _brotli_decoder_lib_names
"brotlidec-static"
"libbrotlidec-static"
)
else()
list(APPEND _brotli_decoder_lib_names
"brotlidec"
"libbrotlidec"
)
endif()
find_library(Brotli_decoder
NAMES ${_brotli_decoder_lib_names}
PATH_SUFFIXES
"lib"
"lib64"
"libs"
"libs64"
"lib/x86_64-linux-gnu"
)
# Just used for PkgConfig stuff in the loop below
set(_brotli_stat_str "")
if(BROTLI_USE_STATIC_LIBS)
set(_brotli_stat_str "_STATIC")
endif()
# Also check if Brotli_encoder was defined, as it can be passed by the end-user
if(NOT TARGET PkgConfig::Brotli_encoder AND NOT Brotli_encoder)
if(BROTLI_USE_STATIC_LIBS)
list(APPEND _brotli_encoder_lib_names
"brotlienc-static"
"libbrotlienc-static"
)
else()
list(APPEND _brotli_encoder_lib_names
"brotlienc"
"libbrotlienc"
)
# Lets us know we are using the PkgConfig libraries
# Will be set false if any non-pkgconf vars are used
set(_brotli_using_pkgconf TRUE)
# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter)
foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
# Split the component name and literal library name from the listvar
list(GET _listvar 0 _component_name)
list(GET _listvar 1 _libname)
if(PKG_CONFIG_FOUND)
# These need to be GLOBAL for MinGW when making ALIAS libraries against them.
if(BROTLI_USE_STATIC_LIBS)
# Have to use _STATIC to tell PkgConfig to find the static libs.
pkg_check_modules(Brotli_${_component_name}_STATIC QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
else()
pkg_check_modules(Brotli_${_component_name} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
endif()
endif()
find_library(Brotli_encoder
NAMES ${_brotli_encoder_lib_names}
PATH_SUFFIXES
"lib"
"lib64"
"libs"
"libs64"
"lib/x86_64-linux-gnu"
)
endif()
# Also check if Brotli_common was defined, as it can be passed by the end-user
if(NOT TARGET PkgConfig::Brotli_common AND NOT Brotli_common)
if(BROTLI_USE_STATIC_LIBS)
list(APPEND _brotli_common_lib_names
"brotlicommon-static"
"libbrotlicommon-static"
)
else()
list(APPEND _brotli_common_lib_names
"brotlicommon"
"libbrotlicommon"
)
endif()
find_library(Brotli_common
NAMES ${_brotli_common_lib_names}
PATH_SUFFIXES
"lib"
"lib64"
"libs"
"libs64"
"lib/x86_64-linux-gnu"
)
endif()
# Check if the target was already found by Pkgconf
if(TARGET PkgConfig::Brotli_${_component_name} OR TARGET PkgConfig::Brotli_${_component_name}_STATIC)
# Can't use generators for ALIAS targets, so you get this jank
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
set(_brotli_req_vars "")
# Generic loop to either create all the aliases for the end-user, or throw errors/warnings.
# Note that the case here needs to match the case we used elsewhere in this file.
foreach(_target_name "common" "decoder" "encoder")
# The PkgConfig IMPORTED_TARGET has PkgConfig:: prefixed to it.
if(TARGET PkgConfig::Brotli_${_target_name})
add_library(Brotli::${_target_name} ALIAS PkgConfig::Brotli_${_target_name})
if(Brotli_FIND_REQUIRED_${_target_name})
# The PkgConfig version of the library has a slightly different path to its lib.
# Tells HANDLE_COMPONENTS we found the component
set(Brotli_${_component_name}_FOUND TRUE)
if(Brotli_FIND_REQUIRED_${_component_name})
# If the lib is required, we can add its literal path as a required var for FindPackageHandleStandardArgs
# Since it won't accept the PkgConfig targets
if(BROTLI_USE_STATIC_LIBS)
list(APPEND _brotli_req_vars "Brotli_${_target_name}_STATIC_LINK_LIBRARIES")
list(APPEND _brotli_req_vars "Brotli_${_component_name}_STATIC_LIBRARIES")
else()
list(APPEND _brotli_req_vars "Brotli_${_target_name}_LINK_LIBRARIES")
list(APPEND _brotli_req_vars "Brotli_${_component_name}_LINK_LIBRARIES")
endif()
endif()
# This will only trigger for libraries we found using find_library
elseif(Brotli_${_target_name})
add_library("Brotli::${_target_name}" UNKNOWN IMPORTED)
# Safety-check the includes dir
if(NOT Brotli_INCLUDE_DIR)
brotli_err_msg("Failed to find Brotli's includes directory. Try manually defining \"Brotli_INCLUDE_DIR\" to Brotli's header path on your system.")
endif()
# Attach the literal library and include dir to the IMPORTED target for the end-user
set_target_properties("Brotli::${_target_name}" PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Brotli_INCLUDE_DIR}"
IMPORTED_LOCATION "${Brotli_${_target_name}}"
# Skip searching for the libs with find_library since it was already found by Pkgconf
continue()
endif()
# Lets us know we aren't using the PkgConfig libraries
set(_brotli_using_pkgconf FALSE)
if(Brotli_FIND_REQUIRED_${_component_name})
# If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs
list(APPEND _brotli_req_vars "Brotli_${_component_name}")
endif()
if(BROTLI_USE_STATIC_LIBS)
list(APPEND _brotli_lib_names
"brotli${_libname}-static"
"libbrotli${_libname}-static"
)
# Attach the library from find_library to our required vars (if it's required)
if(Brotli_FIND_REQUIRED_${_target_name})
list(APPEND _brotli_req_vars "Brotli_${_target_name}")
endif()
# This will only happen if it's a required library but we didn't find it.
elseif(Brotli_FIND_REQUIRED_${_target_name})
# Only bother with an error/failure if they actually required the lib.
brotli_err_msg("Failed to find Brotli's ${_target_name} library. Try manually defining \"Brotli_${_target_name}\" to its path on your system.")
else()
list(APPEND _brotli_lib_names
"brotli${_libname}"
"libbrotli${_libname}"
)
endif()
find_library(Brotli_${_component_name}
NAMES ${_brotli_lib_names}
HINTS ${BROTLI_ROOT_DIR}
PATH_SUFFIXES
"lib"
"lib64"
"libs"
"libs64"
"lib/x86_64-linux-gnu"
)
# Hide the library variable from the Cmake GUI
mark_as_advanced(Brotli_${_component_name})
# Unset since otherwise it'll stick around for the next loop and break things
unset(_brotli_lib_names)
# Check if find_library found the library
if(Brotli_${_component_name})
# Tells HANDLE_COMPONENTS we found the component
set(Brotli_${_component_name}_FOUND TRUE)
add_library("Brotli::${_component_name}" UNKNOWN IMPORTED)
# Attach the literal library and include dir to the IMPORTED target for the end-user
set_target_properties("Brotli::${_component_name}" PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Brotli_INCLUDE_DIR}"
IMPORTED_LOCATION "${Brotli_${_component_name}}"
)
else()
# Tells HANDLE_COMPONENTS we found the component
set(Brotli_${_component_name}_FOUND FALSE)
endif()
endforeach()
include(FindPackageHandleStandardArgs)
# Sets Brotli_FOUND, and fails the find_package(Brotli) call if it was REQUIRED but missing libs.
find_package_handle_standard_args(Brotli
FOUND_VAR Brotli_FOUND
REQUIRED_VARS ${_brotli_req_vars}
FOUND_VAR
Brotli_FOUND
REQUIRED_VARS
Brotli_INCLUDE_DIR
${_brotli_required_targets}
HANDLE_COMPONENTS
)
if(Brotli_FOUND)
include(FindPackageMessage)
foreach(_lib_name ${_brotli_req_vars})
# TODO: remove this if/when The Cmake PkgConfig file fixes the non-quiet message about libbrotlicommon being found.
if(${_lib_name} MATCHES "common")
# This avoids a duplicate "Found Brotli: /usr/lib/libbrotlicommon.so" type message.
continue()
endif()
# Double-expand the var to get the actual path instead of the variable's name.
find_package_message(Brotli "Found Brotli: ${${_lib_name}}"
"[${${_lib_name}}][${Brotli_INCLUDE_DIR}]"
)
endforeach()
# Restore the original find library ordering
if(BROTLI_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_brotli_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
+1 -1
View File
@@ -1,6 +1,6 @@
#CXX = clang++
CXXFLAGS = -std=c++14 -I.. -Wall -Wextra -pthread
CXXFLAGS = -std=c++11 -I.. -Wall -Wextra -pthread
OPENSSL_DIR = /usr/local/opt/openssl
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
+1 -1
View File
@@ -17,7 +17,7 @@ int main(void) {
auto scheme_host_port = "http://localhost:8080";
#endif
auto res = httplib::Client2(scheme_host_port).Get("/hi");
auto res = httplib::Client(scheme_host_port).Get("/hi");
if (res) {
cout << res->status << endl;
+1 -1
View File
@@ -11,7 +11,7 @@
using namespace std;
int main(void) {
httplib::Client2("http://localhost:1234")
httplib::Client("http://localhost:1234")
.Get("/event1", [&](const char *data, size_t data_length) {
std::cout << string(data, data_length);
return true;
+272 -143
View File
@@ -199,7 +199,7 @@ using socket_t = int;
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#ifdef _WIN32
#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
#include <openssl/applink.c>
#endif
@@ -225,6 +225,7 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
#include <brotli/decode.h>
#include <brotli/encode.h>
#endif
/*
@@ -701,9 +702,16 @@ public:
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver);
std::shared_ptr<Response> Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
@@ -780,6 +788,8 @@ public:
void stop();
void set_default_headers(Headers headers);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
@@ -788,6 +798,7 @@ public:
void set_write_timeout(time_t sec, time_t usec = 0);
void set_basic_auth(const char *username, const char *password);
void set_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_digest_auth(const char *username, const char *password);
#endif
@@ -803,6 +814,7 @@ public:
void set_proxy(const char *host, int port);
void set_proxy_basic_auth(const char *username, const char *password);
void set_proxy_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_proxy_digest_auth(const char *username, const char *password);
#endif
@@ -835,6 +847,9 @@ protected:
mutable std::mutex socket_mutex_;
std::recursive_mutex request_mutex_;
// Default headers
Headers default_headers_;
// Settings
std::string client_cert_path_;
std::string client_key_path_;
@@ -848,6 +863,7 @@ protected:
std::string basic_auth_username_;
std::string basic_auth_password_;
std::string bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string digest_auth_username_;
std::string digest_auth_password_;
@@ -869,6 +885,7 @@ protected:
std::string proxy_basic_auth_username_;
std::string proxy_basic_auth_password_;
std::string proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string proxy_digest_auth_username_;
std::string proxy_digest_auth_password_;
@@ -886,6 +903,7 @@ protected:
write_timeout_usec_ = rhs.write_timeout_usec_;
basic_auth_username_ = rhs.basic_auth_username_;
basic_auth_password_ = rhs.basic_auth_password_;
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
digest_auth_username_ = rhs.digest_auth_username_;
digest_auth_password_ = rhs.digest_auth_password_;
@@ -901,6 +919,7 @@ protected:
proxy_port_ = rhs.proxy_port_;
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
@@ -960,6 +979,9 @@ public:
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver);
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver);
@@ -967,6 +989,10 @@ public:
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress);
std::shared_ptr<Response> Head(const char *path);
std::shared_ptr<Response> Head(const char *path, const Headers &headers);
@@ -1037,6 +1063,8 @@ public:
void stop();
void set_default_headers(Headers headers);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
@@ -1045,6 +1073,7 @@ public:
void set_write_timeout(time_t sec, time_t usec = 0);
void set_basic_auth(const char *username, const char *password);
void set_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_digest_auth(const char *username, const char *password);
#endif
@@ -1060,6 +1089,7 @@ public:
void set_proxy(const char *host, int port);
void set_proxy_basic_auth(const char *username, const char *password);
void set_proxy_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_proxy_digest_auth(const char *username, const char *password);
#endif
@@ -1088,51 +1118,6 @@ private:
#endif
}; // namespace httplib
inline void Get(std::vector<Request> &requests, const char *path,
const Headers &headers) {
Request req;
req.method = "GET";
req.path = path;
req.headers = headers;
requests.emplace_back(std::move(req));
}
inline void Get(std::vector<Request> &requests, const char *path) {
Get(requests, path, Headers());
}
inline void Post(std::vector<Request> &requests, const char *path,
const Headers &headers, const std::string &body,
const char *content_type) {
Request req;
req.method = "POST";
req.path = path;
req.headers = headers;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
req.body = body;
requests.emplace_back(std::move(req));
}
inline void Post(std::vector<Request> &requests, const char *path,
const std::string &body, const char *content_type) {
Post(requests, path, Headers(), body, content_type);
}
inline void Post(std::vector<Request> &requests, const char *path,
size_t content_length, ContentProvider content_provider,
const char *content_type) {
Request req;
req.method = "POST";
req.headers = Headers();
req.path = path;
req.content_length = content_length;
req.content_provider = content_provider;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
requests.emplace_back(std::move(req));
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class SSLServer : public Server {
public:
@@ -2157,8 +2142,39 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
return EncodingType::None;
}
class compressor {
public:
virtual ~compressor(){};
typedef std::function<bool(const char *data, size_t data_len)> Callback;
virtual bool compress(const char *data, size_t data_length, bool last,
Callback callback) = 0;
};
class decompressor {
public:
virtual ~decompressor() {}
virtual bool is_valid() const = 0;
typedef std::function<bool(const char *data, size_t data_len)> Callback;
virtual bool decompress(const char *data, size_t data_length,
Callback callback) = 0;
};
class nocompressor : public compressor {
public:
~nocompressor(){};
bool compress(const char *data, size_t data_length, bool /*last*/,
Callback callback) override {
if (!data_length) { return true; }
return callback(data, data_length);
}
};
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
class gzip_compressor {
class gzip_compressor : public compressor {
public:
gzip_compressor() {
std::memset(&strm_, 0, sizeof(strm_));
@@ -2172,8 +2188,8 @@ public:
~gzip_compressor() { deflateEnd(&strm_); }
template <typename T>
bool compress(const char *data, size_t data_length, bool last, T callback) {
bool compress(const char *data, size_t data_length, bool last,
Callback callback) override {
assert(is_valid_);
auto flush = last ? Z_FINISH : Z_NO_FLUSH;
@@ -2206,7 +2222,7 @@ private:
z_stream strm_;
};
class gzip_decompressor {
class gzip_decompressor : public decompressor {
public:
gzip_decompressor() {
std::memset(&strm_, 0, sizeof(strm_));
@@ -2223,10 +2239,10 @@ public:
~gzip_decompressor() { inflateEnd(&strm_); }
bool is_valid() const { return is_valid_; }
bool is_valid() const override { return is_valid_; }
template <typename T>
bool decompress(const char *data, size_t data_length, T callback) {
bool decompress(const char *data, size_t data_length,
Callback callback) override {
assert(is_valid_);
int ret = Z_OK;
@@ -2262,7 +2278,52 @@ private:
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
class brotli_decompressor {
class brotli_compressor : public compressor {
public:
brotli_compressor() {
state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
}
~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
bool compress(const char *data, size_t data_length, bool last,
Callback callback) override {
std::array<uint8_t, 16384> buff{};
auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
auto available_in = data_length;
auto next_in = reinterpret_cast<const uint8_t *>(data);
for (;;) {
if (last) {
if (BrotliEncoderIsFinished(state_)) { break; }
} else {
if (!available_in) { break; }
}
auto available_out = buff.size();
auto next_out = buff.data();
if (!BrotliEncoderCompressStream(state_, operation, &available_in,
&next_in, &available_out, &next_out,
nullptr)) {
return false;
}
auto output_bytes = buff.size() - available_out;
if (output_bytes) {
callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
}
}
return true;
}
private:
BrotliEncoderState *state_ = nullptr;
};
class brotli_decompressor : public decompressor {
public:
brotli_decompressor() {
decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
@@ -2274,13 +2335,14 @@ public:
if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
}
bool is_valid() const { return decoder_s; }
bool is_valid() const override { return decoder_s; }
template <typename T>
bool decompress(const char *data, size_t data_length, T callback) {
bool decompress(const char *data, size_t data_length,
Callback callback) override {
if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
decoder_r == BROTLI_DECODER_RESULT_ERROR)
decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return 0;
}
const uint8_t *next_in = (const uint8_t *)data;
size_t avail_in = data_length;
@@ -2491,32 +2553,29 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
bool decompress, U callback) {
if (decompress) {
std::string encoding = x.get_header_value("Content-Encoding");
std::shared_ptr<decompressor> decompressor;
if (encoding.find("gzip") != std::string::npos ||
encoding.find("deflate") != std::string::npos) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
gzip_decompressor decompressor;
if (decompressor.is_valid()) {
ContentReceiver out = [&](const char *buf, size_t n) {
return decompressor.decompress(
buf, n,
[&](const char *buf, size_t n) { return receiver(buf, n); });
};
return callback(out);
} else {
status = 500;
return false;
}
decompressor = std::make_shared<gzip_decompressor>();
#else
status = 415;
return false;
#endif
} else if (encoding.find("br") != std::string::npos) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
brotli_decompressor decompressor;
if (decompressor.is_valid()) {
decompressor = std::make_shared<brotli_decompressor>();
#else
status = 415;
return false;
#endif
}
if (decompressor) {
if (decompressor->is_valid()) {
ContentReceiver out = [&](const char *buf, size_t n) {
return decompressor.decompress(
return decompressor->decompress(
buf, n,
[&](const char *buf, size_t n) { return receiver(buf, n); });
};
@@ -2525,17 +2584,12 @@ bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
status = 500;
return false;
}
#else
status = 415;
return false;
#endif
}
}
ContentReceiver out = [&](const char *buf, size_t n) {
return receiver(buf, n);
};
return callback(out);
}
@@ -2628,10 +2682,10 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
return static_cast<ssize_t>(offset - begin_offset);
}
template <typename T>
template <typename T, typename U>
inline ssize_t write_content_chunked(Stream &strm,
ContentProvider content_provider,
T is_shutting_down, EncodingType type) {
T is_shutting_down, U &compressor) {
size_t offset = 0;
auto data_available = true;
ssize_t total_written_length = 0;
@@ -2639,10 +2693,6 @@ inline ssize_t write_content_chunked(Stream &strm,
auto ok = true;
DataSink data_sink;
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
detail::gzip_compressor compressor;
#endif
data_sink.write = [&](const char *d, size_t l) {
if (!ok) { return; }
@@ -2650,22 +2700,13 @@ inline ssize_t write_content_chunked(Stream &strm,
offset += l;
std::string payload;
if (type == EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (!compressor.compress(d, l, false,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
ok = false;
return;
}
#endif
} else if (type == EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
#endif
} else {
payload = std::string(d, l);
if (!compressor.compress(d, l, false,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
ok = false;
return;
}
if (!payload.empty()) {
@@ -2685,32 +2726,25 @@ inline ssize_t write_content_chunked(Stream &strm,
data_available = false;
if (type == EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
std::string payload;
if (!compressor.compress(nullptr, 0, true,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
std::string payload;
if (!compressor.compress(nullptr, 0, true,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
ok = false;
return;
}
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (write_data(strm, chunk.data(), chunk.size())) {
total_written_length += chunk.size();
} else {
ok = false;
return;
}
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (write_data(strm, chunk.data(), chunk.size())) {
total_written_length += chunk.size();
} else {
ok = false;
return;
}
}
#endif
} else if (type == EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
#endif
}
static const std::string done_marker("0\r\n\r\n");
@@ -3270,6 +3304,14 @@ make_basic_authentication_header(const std::string &username,
return std::make_pair(key, field);
}
inline std::pair<std::string, std::string>
make_bearer_token_authentication_header(const std::string &token,
bool is_proxy = false) {
auto field = "Bearer " + token;
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline std::pair<std::string, std::string> make_digest_authentication_header(
const Request &req, const std::map<std::string, std::string> &auth,
@@ -3918,25 +3960,33 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
}
if (type != detail::EncodingType::None) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
std::string compressed;
std::shared_ptr<detail::compressor> compressor;
if (type == detail::EncodingType::Gzip) {
detail::gzip_compressor compressor;
if (!compressor.compress(res.body.data(), res.body.size(), true,
[&](const char *data, size_t data_len) {
compressed.append(data, data_len);
return true;
})) {
return false;
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = std::make_shared<detail::gzip_compressor>();
res.set_header("Content-Encoding", "gzip");
#endif
} else if (type == detail::EncodingType::Brotli) {
// TODO:
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = std::make_shared<detail::brotli_compressor>();
res.set_header("Content-Encoding", "brotli");
#endif
}
res.body.swap(compressed);
#endif
if (compressor) {
std::string compressed;
if (!compressor->compress(res.body.data(), res.body.size(), true,
[&](const char *data, size_t data_len) {
compressed.append(data, data_len);
return true;
})) {
return false;
}
res.body.swap(compressed);
}
}
auto length = std::to_string(res.body.size());
@@ -3999,8 +4049,23 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
}
} else {
auto type = detail::encoding_type(req, res);
std::shared_ptr<detail::compressor> compressor;
if (type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = std::make_shared<detail::gzip_compressor>();
#endif
} else if (type == detail::EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = std::make_shared<detail::brotli_compressor>();
#endif
} else {
compressor = std::make_shared<detail::nocompressor>();
}
assert(compressor != nullptr);
if (detail::write_content_chunked(strm, res.content_provider_,
is_shutting_down, type) < 0) {
is_shutting_down, *compressor) < 0) {
return false;
}
}
@@ -4688,6 +4753,16 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
}
if (!bearer_token_auth_token_.empty()) {
headers.insert(make_bearer_token_authentication_header(
bearer_token_auth_token_, false));
}
if (!proxy_bearer_token_auth_token_.empty()) {
headers.insert(make_bearer_token_authentication_header(
proxy_bearer_token_auth_token_, true));
}
detail::write_headers(bstrm, req, headers);
// Flush buffer
@@ -4734,7 +4809,8 @@ inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
ContentProvider content_provider, const char *content_type) {
Request req;
req.method = method;
req.headers = headers;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.path = path;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
@@ -4874,7 +4950,8 @@ ClientImpl::Get(const char *path, const Headers &headers, Progress progress) {
Request req;
req.method = "GET";
req.path = path;
req.headers = headers;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.progress = std::move(progress);
auto res = std::make_shared<Response>();
@@ -4906,6 +4983,13 @@ ClientImpl::Get(const char *path, const Headers &headers,
std::move(progress));
}
inline std::shared_ptr<Response>
ClientImpl::Get(const char *path, ResponseHandler response_handler,
ContentReceiver content_receiver) {
return Get(path, Headers(), std::move(response_handler), content_receiver,
Progress());
}
inline std::shared_ptr<Response>
ClientImpl::Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
@@ -4914,6 +4998,13 @@ ClientImpl::Get(const char *path, const Headers &headers,
Progress());
}
inline std::shared_ptr<Response>
ClientImpl::Get(const char *path, ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return Get(path, Headers(), std::move(response_handler), content_receiver,
progress);
}
inline std::shared_ptr<Response>
ClientImpl::Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
@@ -4921,7 +5012,8 @@ ClientImpl::Get(const char *path, const Headers &headers,
Request req;
req.method = "GET";
req.path = path;
req.headers = headers;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.response_handler = std::move(response_handler);
req.content_receiver = std::move(content_receiver);
req.progress = std::move(progress);
@@ -4938,7 +5030,8 @@ inline std::shared_ptr<Response> ClientImpl::Head(const char *path,
const Headers &headers) {
Request req;
req.method = "HEAD";
req.headers = headers;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.path = path;
auto res = std::make_shared<Response>();
@@ -5117,7 +5210,8 @@ inline std::shared_ptr<Response> ClientImpl::Delete(const char *path,
const char *content_type) {
Request req;
req.method = "DELETE";
req.headers = headers;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.path = path;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
@@ -5136,8 +5230,9 @@ inline std::shared_ptr<Response> ClientImpl::Options(const char *path,
const Headers &headers) {
Request req;
req.method = "OPTIONS";
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.path = path;
req.headers = headers;
auto res = std::make_shared<Response>();
@@ -5180,6 +5275,10 @@ inline void ClientImpl::set_basic_auth(const char *username,
basic_auth_password_ = password;
}
inline void ClientImpl::set_bearer_token_auth(const char *token) {
bearer_token_auth_token_ = token;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void ClientImpl::set_digest_auth(const char *username,
const char *password) {
@@ -5192,6 +5291,10 @@ inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
inline void ClientImpl::set_default_headers(Headers headers) {
default_headers_ = std::move(headers);
}
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
@@ -5215,6 +5318,10 @@ inline void ClientImpl::set_proxy_basic_auth(const char *username,
proxy_basic_auth_password_ = password;
}
inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) {
proxy_bearer_token_auth_token_ = token;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void ClientImpl::set_proxy_digest_auth(const char *username,
const char *password) {
@@ -5939,12 +6046,24 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
Progress progress) {
return cli_->Get(path, headers, content_receiver, progress);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return cli_->Get(path, Headers(), response_handler, content_receiver);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return cli_->Get(path, headers, response_handler, content_receiver);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
return cli_->Get(path, Headers(), response_handler, content_receiver,
progress);
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ResponseHandler response_handler,
@@ -6095,6 +6214,10 @@ inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
inline void Client::stop() { cli_->stop(); }
inline void Client::set_default_headers(Headers headers) {
cli_->set_default_headers(std::move(headers));
}
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
inline void Client::set_socket_options(SocketOptions socket_options) {
cli_->set_socket_options(socket_options);
@@ -6113,6 +6236,9 @@ inline void Client::set_write_timeout(time_t sec, time_t usec) {
inline void Client::set_basic_auth(const char *username, const char *password) {
cli_->set_basic_auth(username, password);
}
inline void Client::set_bearer_token_auth(const char *token) {
cli_->set_bearer_token_auth(token);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void Client::set_digest_auth(const char *username,
const char *password) {
@@ -6140,6 +6266,9 @@ inline void Client::set_proxy_basic_auth(const char *username,
const char *password) {
cli_->set_proxy_basic_auth(username, password);
}
inline void Client::set_proxy_bearer_token_auth(const char *token) {
cli_->set_proxy_bearer_token_auth(token);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void Client::set_proxy_digest_auth(const char *username,
const char *password) {
+154 -98
View File
@@ -294,10 +294,10 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
@@ -306,7 +306,7 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
ASSERT_TRUE(res != nullptr);
std::string out;
httplib::detail::read_file("./image.jpg", out);
detail::read_file("./image.jpg", out);
EXPECT_EQ(200, res->status);
EXPECT_EQ(out, res->body);
@@ -317,10 +317,10 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
@@ -334,7 +334,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
ASSERT_TRUE(res != nullptr);
std::string out;
httplib::detail::read_file("./image.jpg", out);
detail::read_file("./image.jpg", out);
EXPECT_EQ(200, res->status);
EXPECT_EQ(out, body);
@@ -345,16 +345,16 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
std::string body;
auto res = cli.Get(
"/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", Headers(),
"/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
[&](const Response &response) {
EXPECT_EQ(200, response.status);
return true;
@@ -366,34 +366,53 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
ASSERT_TRUE(res != nullptr);
std::string out;
httplib::detail::read_file("./image.jpg", out);
detail::read_file("./image.jpg", out);
EXPECT_EQ(200, res->status);
EXPECT_EQ(out, body);
}
TEST(DefaultHeadersTest, FromHTTPBin) {
Client cli("httpbin.org");
cli.set_default_headers({make_range_header({{1, 10}})});
cli.set_connection_timeout(5);
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
{
auto res = cli.Get("/range/32");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("bcdefghijk", res->body);
EXPECT_EQ(206, res->status);
}
}
TEST(RangeTest, FromHTTPBin) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(5);
{
httplib::Headers headers;
auto res = cli.Get("/range/32", headers);
auto res = cli.Get("/range/32");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
EXPECT_EQ(200, res->status);
}
{
httplib::Headers headers = {httplib::make_range_header({{1, -1}})};
Headers headers = {make_range_header({{1, -1}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("bcdefghijklmnopqrstuvwxyzabcdef", res->body);
@@ -401,7 +420,7 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header({{1, 10}})};
Headers headers = {make_range_header({{1, 10}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("bcdefghijk", res->body);
@@ -409,7 +428,7 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header({{0, 31}})};
Headers headers = {make_range_header({{0, 31}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
@@ -417,7 +436,7 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header({{0, -1}})};
Headers headers = {make_range_header({{0, -1}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body);
@@ -425,7 +444,7 @@ TEST(RangeTest, FromHTTPBin) {
}
{
httplib::Headers headers = {httplib::make_range_header({{0, 32}})};
Headers headers = {make_range_header({{0, 32}})};
auto res = cli.Get("/range/32", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(416, res->status);
@@ -437,10 +456,10 @@ TEST(ConnectionErrorTest, InvalidHost) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
@@ -452,9 +471,9 @@ TEST(ConnectionErrorTest, InvalidHost2) {
auto host = "httpbin.org/";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
SSLClient cli(host);
#else
httplib::Client cli(host);
Client cli(host);
#endif
cli.set_connection_timeout(2);
@@ -467,10 +486,10 @@ TEST(ConnectionErrorTest, InvalidPort) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 44380;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 8080;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
@@ -483,10 +502,10 @@ TEST(ConnectionErrorTest, Timeout) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 44380;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 8080;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
@@ -499,10 +518,10 @@ TEST(CancelTest, NoCancel) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(5);
@@ -517,10 +536,10 @@ TEST(CancelTest, WithCancelSmallPayload) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
@@ -533,16 +552,15 @@ TEST(CancelTest, WithCancelLargePayload) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
cli.set_connection_timeout(5);
uint32_t count = 0;
httplib::Headers headers;
auto res = cli.Get("/range/65536", headers,
auto res = cli.Get("/range/65536",
[&count](uint64_t, uint64_t) { return (count++ == 0); });
ASSERT_TRUE(res == nullptr);
}
@@ -552,10 +570,10 @@ TEST(BaseAuthTest, FromHTTPWatch) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port);
Client cli(host, port);
#endif
{
@@ -565,9 +583,8 @@ TEST(BaseAuthTest, FromHTTPWatch) {
}
{
auto res =
cli.Get("/basic-auth/hello/world",
{httplib::make_basic_authentication_header("hello", "world")});
auto res = cli.Get("/basic-auth/hello/world",
{make_basic_authentication_header("hello", "world")});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
res->body);
@@ -602,7 +619,7 @@ TEST(BaseAuthTest, FromHTTPWatch) {
TEST(DigestAuthTest, FromHTTPWatch) {
auto host = "httpbin.org";
auto port = 443;
httplib::SSLClient cli(host, port);
SSLClient cli(host, port);
{
auto res = cli.Get("/digest-auth/auth/hello/world");
@@ -651,9 +668,9 @@ TEST(AbsoluteRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
SSLClient cli(host);
#else
httplib::Client cli(host);
Client cli(host);
#endif
cli.set_follow_location(true);
@@ -666,9 +683,9 @@ TEST(RedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
SSLClient cli(host);
#else
httplib::Client cli(host);
Client cli(host);
#endif
cli.set_follow_location(true);
@@ -681,9 +698,9 @@ TEST(RelativeRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
SSLClient cli(host);
#else
httplib::Client cli(host);
Client cli(host);
#endif
cli.set_follow_location(true);
@@ -696,9 +713,9 @@ TEST(TooManyRedirectTest, Redirect) {
auto host = "httpbin.org";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::SSLClient cli(host);
SSLClient cli(host);
#else
httplib::Client cli(host);
Client cli(host);
#endif
cli.set_follow_location(true);
@@ -709,7 +726,7 @@ TEST(TooManyRedirectTest, Redirect) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest, Redirect) {
httplib::Client cli("yahoo.com");
Client cli("yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
@@ -723,7 +740,7 @@ TEST(YahooRedirectTest, Redirect) {
#if 0
TEST(HttpsToHttpRedirectTest, Redirect) {
httplib::SSLClient cli("httpbin.org");
SSLClient cli("httpbin.org");
cli.set_follow_location(true);
auto res =
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
@@ -772,7 +789,7 @@ TEST(RedirectToDifferentPort, Redirect) {
}
TEST(UrlWithSpace, Redirect) {
httplib::SSLClient cli("edge.forgecdn.net");
SSLClient cli("edge.forgecdn.net");
cli.set_follow_location(true);
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
@@ -1227,22 +1244,22 @@ protected:
[&](const Request &req, Response & /*res*/) {
EXPECT_EQ("close", req.get_header_value("Connection"));
})
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
.Get("/gzip",
#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT)
.Get("/compress",
[&](const Request & /*req*/, Response &res) {
res.set_content(
"12345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901234567890",
"text/plain");
})
.Get("/nogzip",
.Get("/nocompress",
[&](const Request & /*req*/, Response &res) {
res.set_content(
"12345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901234567890",
"application/octet-stream");
})
.Post("/gzipmultipart",
.Post("/compress-multipart",
[&](const Request &req, Response & /*res*/) {
EXPECT_EQ(2u, req.files.size());
ASSERT_TRUE(!req.has_file("???"));
@@ -2091,7 +2108,7 @@ TEST_F(ServerTest, PutLargeFileWithGzip) {
TEST_F(ServerTest, PutContentWithDeflate) {
cli_.set_compress(false);
httplib::Headers headers;
Headers headers;
headers.emplace("Content-Encoding", "deflate");
// PUT in deflate format:
auto res = cli_.Put("/put", headers,
@@ -2103,7 +2120,7 @@ TEST_F(ServerTest, PutContentWithDeflate) {
}
TEST_F(ServerTest, GetStreamedChunkedWithGzip) {
httplib::Headers headers;
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
auto res = cli_.Get("/streamed-chunked", headers);
@@ -2113,7 +2130,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithGzip) {
}
TEST_F(ServerTest, GetStreamedChunkedWithGzip2) {
httplib::Headers headers;
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
auto res = cli_.Get("/streamed-chunked2", headers);
@@ -2123,6 +2140,28 @@ TEST_F(ServerTest, GetStreamedChunkedWithGzip2) {
}
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
TEST_F(ServerTest, GetStreamedChunkedWithBrotli) {
Headers headers;
headers.emplace("Accept-Encoding", "brotli");
auto res = cli_.Get("/streamed-chunked", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ(std::string("123456789"), res->body);
}
TEST_F(ServerTest, GetStreamedChunkedWithBrotli2) {
Headers headers;
headers.emplace("Accept-Encoding", "brotli");
auto res = cli_.Get("/streamed-chunked2", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ(std::string("123456789"), res->body);
}
#endif
TEST_F(ServerTest, Patch) {
auto res = cli_.Patch("/patch", "PATCH", "text/plain");
ASSERT_TRUE(res != nullptr);
@@ -2267,7 +2306,7 @@ TEST_F(ServerTest, KeepAlive) {
EXPECT_EQ("close", res->get_header_value("Connection"));
res = cli_.Post(
"/empty", 0, [&](size_t, size_t, httplib::DataSink &) { return true; },
"/empty", 0, [&](size_t, size_t, DataSink &) { return true; },
"text/plain");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
@@ -2285,7 +2324,7 @@ TEST_F(ServerTest, KeepAlive) {
TEST_F(ServerTest, Gzip) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
auto res = cli_.Get("/gzip", headers);
auto res = cli_.Get("/compress", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
@@ -2298,8 +2337,7 @@ TEST_F(ServerTest, Gzip) {
}
TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
Headers headers;
auto res = cli_.Get("/gzip", headers);
auto res = cli_.Get("/compress");
ASSERT_TRUE(res != nullptr);
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
@@ -2315,12 +2353,12 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
std::string body;
auto res =
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
auto res = cli_.Get("/compress", headers,
[&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
@@ -2337,7 +2375,7 @@ TEST_F(ServerTest, GzipWithoutDecompressing) {
headers.emplace("Accept-Encoding", "gzip, deflate");
cli_.set_decompress(false);
auto res = cli_.Get("/gzip", headers);
auto res = cli_.Get("/compress", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
@@ -2348,14 +2386,13 @@ TEST_F(ServerTest, GzipWithoutDecompressing) {
}
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
Headers headers;
std::string body;
auto res =
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
auto res = cli_.Get("/compress",
[&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
EXPECT_TRUE(res->get_header_value("Content-Encoding").empty());
@@ -2370,7 +2407,7 @@ TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
TEST_F(ServerTest, NoGzip) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
auto res = cli_.Get("/nogzip", headers);
auto res = cli_.Get("/nocompress", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(false, res->has_header("Content-Encoding"));
@@ -2386,12 +2423,12 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
Headers headers;
headers.emplace("Accept-Encoding", "gzip, deflate");
std::string body;
auto res =
cli_.Get("/nogzip", headers, [&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
auto res = cli_.Get("/nocompress", headers,
[&](const char *data, uint64_t data_length) {
EXPECT_EQ(data_length, 100);
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(false, res->has_header("Content-Encoding"));
@@ -2410,13 +2447,30 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
};
cli_.set_compress(true);
auto res = cli_.Post("/gzipmultipart", items);
auto res = cli_.Post("/compress-multipart", items);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
TEST_F(ServerTest, Brotli) {
Headers headers;
headers.emplace("Accept-Encoding", "br");
auto res = cli_.Get("/compress", headers);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ("brotli", res->get_header_value("Content-Encoding"));
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
EXPECT_EQ("19", res->get_header_value("Content-Length"));
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
"7890123456789012345678901234567890",
res->body);
EXPECT_EQ(200, res->status);
}
#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,
std::string *resp = nullptr) {
@@ -2964,7 +3018,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
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_FILE, CLIENT_PRIVATE_KEY_FILE);
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
cli.set_connection_timeout(30);
@@ -3035,7 +3089,7 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) {
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);
SSLClient cli(HOST, PORT, client_cert, client_private_key);
cli.enable_server_certificate_verification(false);
cli.set_connection_timeout(30);
@@ -3061,7 +3115,7 @@ TEST(SSLClientServerTest, ClientCertMissing) {
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
std::this_thread::sleep_for(std::chrono::milliseconds(1));
httplib::SSLClient cli(HOST, PORT);
SSLClient cli(HOST, PORT);
auto res = cli.Get("/test");
cli.set_connection_timeout(30);
ASSERT_TRUE(res == nullptr);
@@ -3083,7 +3137,7 @@ TEST(SSLClientServerTest, TrustDirOptional) {
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_FILE, CLIENT_PRIVATE_KEY_FILE);
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
cli.set_connection_timeout(30);
@@ -3104,24 +3158,24 @@ TEST(CleanupTest, WSACleanup) {
// #ifndef CPPHTTPLIB_OPENSSL_SUPPORT
// TEST(NoSSLSupport, SimpleInterface) {
// httplib::Client cli("https://yahoo.com");
// Client cli("https://yahoo.com");
// ASSERT_FALSE(cli.is_valid());
// }
// #endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(InvalidScheme, SimpleInterface) {
httplib::Client cli("scheme://yahoo.com");
Client cli("scheme://yahoo.com");
ASSERT_FALSE(cli.is_valid());
}
TEST(NoScheme, SimpleInterface) {
httplib::Client cli("yahoo.com:80");
Client cli("yahoo.com:80");
ASSERT_TRUE(cli.is_valid());
}
TEST(YahooRedirectTest2, SimpleInterface) {
httplib::Client cli("http://yahoo.com");
Client cli("http://yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
@@ -3134,7 +3188,7 @@ TEST(YahooRedirectTest2, SimpleInterface) {
}
TEST(YahooRedirectTest3, SimpleInterface) {
httplib::Client cli("https://yahoo.com");
Client cli("https://yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
@@ -3148,20 +3202,22 @@ TEST(YahooRedirectTest3, SimpleInterface) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
httplib::Client cli("https://cdnjs.cloudflare.com");
auto res = cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "brotli"}});
Client cli("https://cdnjs.cloudflare.com");
auto res = cli.Get("/ajax/libs/jquery/3.5.1/jquery.js",
{{"Accept-Encoding", "brotli"}});
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ(287630, res->body.size());
EXPECT_EQ("application/javascript; charset=utf-8", res->get_header_value("Content-Type"));
EXPECT_EQ("application/javascript; charset=utf-8",
res->get_header_value("Content-Type"));
}
#endif
#if 0
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
auto res =
httplib::Client("https://httpbin.org")
Client("https://httpbin.org")
.set_follow_location(true)
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");