Compare commits

..
49 Commits
Author SHA1 Message Date
yhirose 27c0e1186c Release v12.0.5 2023-05-30 16:09:42 +09:00
c54c71a3e5 Add HTTPLIB_INSTALL CMake option (#1575)
* Add HTTPLIB_INSTALL CMake option

* Proper formatting of HTTPLIB_INSTALL block

Co-authored-by: Jiwoo Park <jiwoo_90@naver.com>

---------

Co-authored-by: Niccolò Iardella <niccolo.iardella@doriansrl.it>
Co-authored-by: Jiwoo Park <jiwoo_90@naver.com>
2023-05-30 16:08:58 +09:00
yhirose 3409c00e6f Fixed warnings 2023-05-27 01:10:21 +09:00
yhirose f8ef5fab64 Release v0.12.4 2023-05-27 00:57:43 +09:00
yhirose 5b397d455d Fix more CRLF injection problems. 2023-05-22 22:56:16 +09:00
yhirose f977558a28 Release v0.12.3 2023-04-30 10:44:36 +09:00
Oleg Shparber c2e156e0e0 Fix leaked handle in create_socket (#1554)
Fixes resource leak problem detected by Coverity Scan.
2023-04-09 12:18:44 -04:00
yhirose 7aba2938d3 Fix #1548 2023-04-08 15:36:13 -04:00
yhirose d587548250 Fix #1545 2023-04-08 14:53:55 -04:00
Jiwoo Park 21f9c51556 Remove whitespace in CMake generator expression (#1552) 2023-04-07 15:40:52 -04:00
yhirose 985ceba525 Updated README 2023-04-04 07:17:59 -07:00
Sergey KazminandSergey Kazmin e62a4b02e5 fix (#1525)
Co-authored-by: Sergey Kazmin <sergey.kazmin@kaspersky.com>
2023-04-04 07:12:15 -07:00
Octavio Valle ff34749572 Initialize sockaddr_un to fix valgrind uninitialised byte message. (#1547) 2023-04-03 08:28:01 -07:00
Jiwoo Park e5804d4a50 Don't loading system certs from Keychain on iOS (#1546) 2023-04-01 06:26:30 -07:00
Jiwoo Park 3956a2b790 Fix ServerTest.ClientStop test case (#1542) 2023-03-30 09:50:51 -04:00
Jiwoo Park b33aa52dc2 Fix lifetime issues in test using detail::scope_exit() (#1535)
* Fix lifetime issues in test using detail::scope_exit()

* Remove checking joinable threads
2023-03-28 00:01:34 -04:00
yhirose 76230db97f Simplified scope_exit 2023-03-25 21:52:39 -04:00
yhirose a66a013ed7 Release v0.12.2 2023-03-25 21:47:14 -04:00
yhirose f4b02dfdc1 Fix #1533 2023-03-25 21:13:07 -04:00
yhirose 4cf218643e Code format 2023-03-25 21:12:40 -04:00
yhirose 8f96b69a25 Update test.yaml to use microsoft/setup-msbuild@v1.1 2023-03-23 18:53:27 -04:00
Johannes Flügel d262033ded Prevent overflow in hash function str2tag_core() (#1529)
* str2tag_core(): prevent overflow

* Update httplib.h

works for all sizes of unsigned int and if there exists a #define for max
2023-03-22 14:16:32 -04:00
yhirose 5745eabe69 Updated test.yaml to use ctions/checkout@v3 2023-03-21 18:54:54 -04:00
yhirose 5f18642271 Close #1531 2023-03-20 11:47:06 -04:00
yhirose 88a9278872 Fix #1486 2023-03-11 17:04:08 -05:00
yhirose 9bb3ca8169 Fix #1459 (#1523) 2023-03-10 22:21:42 -05:00
yhirose f2f4728489 Release v0.12.1 2023-03-10 17:53:19 -05:00
yhirose d1b616286f Add note for macOS regarding system certs 2023-03-10 17:48:19 -05:00
Mathieu GaillardandMathieu Gaillard df74526f91 Fix multipart Content-Type headers with both boundary and charset parameters (#1516)
* Fix multipart Content-Type headers with both boundary and charset parameters

* Improve code readability

* Add missing forward declaration

---------

Co-authored-by: Mathieu Gaillard <gaillard@adobe.com>
2023-03-08 23:57:17 -05:00
Joel Rosdahl 9f7ae0737a Fix typos (#1517) 2023-03-08 17:03:20 -05:00
yhirose 1ebb8412c5 Use SSL_ERROR_ZERO_RETURN to check if the SSL peer is closed. 2023-03-07 08:15:16 -05:00
Petr MenšíkandJiwoo Park 7b69999c37 Reuse gtest on system (#1493)
* Reuse gtest on the system

Try to use gtest on the system if found. Avoid using recent CMake
features.

* Set CMP0135 only when using FetchContent

* Add /bigobj option for MSVC

* Support also cmake between 3.14 and 3.20

Older versions provided only GTest::Main target, not currently used
gtest_main. Provide backward compatibility also to old cmake versions.

* Remove redundant variable checking

---------

Co-authored-by: Jiwoo Park <jiwoo_90@naver.com>
2023-03-05 21:35:35 -05:00
yhirose c7e959a948 Fix #1481 2023-03-04 18:14:00 -05:00
yhirose ba5884e779 Fix #1481 (#1513) 2023-03-03 23:45:19 -05:00
yhirose cdaa5c48db Code cleanup 2023-03-03 22:41:57 -05:00
Alexandre Bouvier bab5c0e907 cmake: fix find_dependency (#1509) 2023-03-01 08:38:58 -05:00
Alexandre Bouvier 016838fd10 cmake: support components (#1504) 2023-02-25 09:56:56 -05:00
yhirose 75053bf855 Fix #1498 (#1501)
* Fix #1498

* Fixed build error
2023-02-22 13:19:36 -05:00
yhirose ae3a6dd2a9 Fixed an issue with example/Makefile on MacOS 2023-02-21 22:00:10 -05:00
Sergey KazminandSergey Kazmin 6d963fbe8d Support loading system certs from Keychein on MacOS (#1474)
* Support loading system certs from Keychein on MacOS

* review improvements: add deps to meson.build and improve conditional expressions in cmake

* fix tabs

* fix tabs

* review improvements

* fix after review

* additionally load root certs from the system root keychain

* cmake fix

* fix

* small refactoring

* small refactoring

---------

Co-authored-by: Sergey Kazmin <sergey.kazmin@kaspersky.com>
2023-02-17 12:06:55 -05:00
jingTian-z 88f6245c84 feat: Add Request::get_file_multi_value func. (#1495)
Support to get multiple values of a key.

perf: Rename function names, variable names etc.
2023-02-16 21:51:06 -05:00
yhirose 0e7d2f9f93 Resolve #1482 2023-02-14 11:40:47 -05:00
yhirose 4e6ded1f36 Release v0.12.0 2023-02-07 10:27:40 -05:00
yhirose d663588491 Removed is_writable() from DataSink (Resolve #1478, too) (#1483) 2023-02-04 13:53:42 -05:00
yhirose 439caf5b79 Fix #1479 2023-02-01 19:11:11 -05:00
yhirose c4ba43ca6f Removed incorrect comment 2023-01-24 09:07:42 -05:00
Jiwoo Park 20cba2ecd9 Support CTest (#1468)
* Add test/CMakeLists.txt to enable testing with CTest

Add HTTPLIB_TEST option
Downlaod GoogleTest source using FetchContent module
Generate cert files to test httplib with OpenSSL

* Generate cert2.pem with a new certificate request

* Use the latest GoogleTest library
2023-01-21 03:43:22 -05:00
yhirose 0ff2e16d69 Issue 52666: cpp-httplib:server_fuzzer: Timeout in server_fuzzer 2023-01-21 01:09:19 -05:00
Ray Beck 51607ec752 add to_human_string (#1467)
* add to_human_string

* replace to_string with to_human_string

* fix test
2023-01-19 07:01:34 -05:00
15 changed files with 1217 additions and 425 deletions

No files matched your search

+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
git config --global core.autocrlf false git config --global core.autocrlf false
git config --global core.eol lf git config --global core.eol lf
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: install brotli library on ubuntu - name: install brotli library on ubuntu
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: sudo apt update && sudo apt-get install -y libbrotli-dev run: sudo apt update && sudo apt-get install -y libbrotli-dev
@@ -32,7 +32,7 @@ jobs:
run: cd test && make fuzz_test run: cd test && make fuzz_test
- name: setup msbuild on windows - name: setup msbuild on windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.0.2 uses: microsoft/setup-msbuild@v1.1
- name: make-windows - name: make-windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: | run: |
+25 -6
View File
@@ -6,15 +6,18 @@
* HTTPLIB_REQUIRE_OPENSSL (default off) * HTTPLIB_REQUIRE_OPENSSL (default off)
* HTTPLIB_REQUIRE_ZLIB (default off) * HTTPLIB_REQUIRE_ZLIB (default off)
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on) * HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
* HTTPLIB_REQUIRE_BROTLI (default off) * HTTPLIB_REQUIRE_BROTLI (default off)
* HTTPLIB_COMPILE (default off) * HTTPLIB_COMPILE (default off)
* HTTPLIB_INSTALL (default on)
* HTTPLIB_TEST (default off)
* BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed). * BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed).
* OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed). * OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed).
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
After installation with Cmake, a find_package(httplib) is available. After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli) is available.
This creates a httplib::httplib target (if found). This creates a httplib::httplib target (if found and if listed components are supported).
It can be linked like so: It can be linked like so:
target_link_libraries(your_exe httplib::httplib) target_link_libraries(your_exe httplib::httplib)
@@ -42,6 +45,7 @@
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled. * HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled. * HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
* HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled. * HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled.
* HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled.
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only. * HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include). * HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so). * HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
@@ -78,19 +82,22 @@ set(_HTTPLIB_OPENSSL_MIN_VER "1.1.1")
# Allow for a build to require OpenSSL to pass, instead of just being optional # 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_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) 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. # Allow for a build to casually enable OpenSSL/ZLIB support, but silently continue if not found.
# Make these options so their automatic use can be specifically disabled (as needed) # Make these options so their automatic use can be specifically disabled (as needed)
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON) option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON) option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON)
# Lets you compile the program as a regular library instead of header-only # Lets you compile the program as a regular library instead of header-only
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF) option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
# Lets you disable the installation (useful when fetched from another CMake project)
option(HTTPLIB_INSTALL "Enables the installation target" ON)
# Just setting this variable here for people building in-tree # Just setting this variable here for people building in-tree
if(HTTPLIB_COMPILE) if(HTTPLIB_COMPILE)
set(HTTPLIB_IS_COMPILED TRUE) set(HTTPLIB_IS_COMPILED TRUE)
endif() endif()
option(HTTPLIB_TEST "Enables testing and builds tests" OFF)
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF) 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 decompression support." ON) option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON)
# Defaults to static library # 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) 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) if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
@@ -136,6 +143,10 @@ if(Brotli_FOUND)
set(HTTPLIB_IS_USING_BROTLI TRUE) set(HTTPLIB_IS_USING_BROTLI TRUE)
endif() endif()
if(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN TRUE)
endif()
# Used for default, common dirs that the end-user can change (if needed) # Used for default, common dirs that the end-user can change (if needed)
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR # like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
include(GNUInstallDirs) include(GNUInstallDirs)
@@ -205,6 +216,8 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<PLATFORM_ID:Windows>:ws2_32> $<$<PLATFORM_ID:Windows>:ws2_32>
$<$<PLATFORM_ID:Windows>:crypt32> $<$<PLATFORM_ID:Windows>:crypt32>
$<$<PLATFORM_ID:Windows>:cryptui> $<$<PLATFORM_ID:Windows>:cryptui>
# Needed for API from MacOS Security framework
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}>>:-framework CoreFoundation -framework Security>"
# Can't put multiple targets in a single generator expression or it bugs out. # Can't put multiple targets in a single generator expression or it bugs out.
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common> $<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder> $<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
@@ -219,6 +232,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT> $<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT> $<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT> $<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
) )
# CMake configuration files installation directory # CMake configuration files installation directory
@@ -232,8 +246,6 @@ configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}" INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
# Passes the includedir install path # Passes the includedir install path
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
# There aren't any components, so don't use the macro
NO_CHECK_REQUIRED_COMPONENTS_MACRO
) )
if(HTTPLIB_COMPILE) if(HTTPLIB_COMPILE)
@@ -253,6 +265,7 @@ else()
) )
endif() endif()
if(HTTPLIB_INSTALL)
# Creates the export httplibTargets.cmake # Creates the export httplibTargets.cmake
# This is strictly what holds compilation requirements # This is strictly what holds compilation requirements
# and linkage information (doesn't find deps though). # and linkage information (doesn't find deps though).
@@ -278,3 +291,9 @@ install(EXPORT httplibTargets
NAMESPACE ${PROJECT_NAME}:: NAMESPACE ${PROJECT_NAME}::
DESTINATION ${_TARGET_INSTALL_CMAKEDIR} DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
) )
endif()
if(HTTPLIB_TEST)
include(CTest)
add_subdirectory(test)
endif()
+31 -4
View File
@@ -55,6 +55,8 @@ SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcry
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0. NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
NOTE for macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
```c++ ```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT #define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h" #include "path/to/httplib.h"
@@ -74,7 +76,7 @@ cli.set_ca_cert_path("./ca-bundle.crt");
cli.enable_server_certificate_verification(false); cli.enable_server_certificate_verification(false);
``` ```
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself. NOTE: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
Server Server
------ ------
@@ -346,6 +348,27 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
}); });
``` ```
With trailer:
```cpp
svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_header("Trailer", "Dummy1, Dummy2");
res.set_chunked_content_provider(
"text/plain",
[](size_t offset, DataSink &sink) {
sink.write("123", 3);
sink.write("345", 3);
sink.write("789", 3);
sink.done_with_trailer({
{"Dummy1", "DummyVal1"},
{"Dummy2", "DummyVal2"}
});
return true;
}
);
});
```
### 'Expect: 100-continue' handler ### 'Expect: 100-continue' handler
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header. By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
@@ -493,6 +516,10 @@ auto res = cli.Get("/hi", headers);
``` ```
or or
```c++ ```c++
auto res = cli.Get("/hi", {{"Accept-Encoding", "gzip, deflate"}});
```
or
```c++
cli.set_default_headers({ cli.set_default_headers({
{ "Accept-Encoding", "gzip, deflate" } { "Accept-Encoding", "gzip, deflate" }
}); });
@@ -808,14 +835,14 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
#include <httplib.h> #include <httplib.h>
``` ```
Note: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance. NOTE: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
Note: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin on Windows are not supported. NOTE: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin on Windows are not supported.
License License
------- -------
MIT license (© 2022 Yuji Hirose) MIT license (© 2023 Yuji Hirose)
Special Thanks To Special Thanks To
----------------- -----------------
+8 -1
View File
@@ -8,12 +8,19 @@ OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3 #OPENSSL_DIR = $(PREFIX)/opt/openssl@3
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
ifneq ($(OS), Windows_NT)
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
endif
endif
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = $(PREFIX)/opt/brotli BROTLI_DIR = $(PREFIX)/opt/brotli
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark issue
server : server.cc ../httplib.h Makefile server : server.cc ../httplib.h Makefile
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
+1 -1
View File
@@ -19,7 +19,7 @@ public:
unique_lock<mutex> lk(m_); unique_lock<mutex> lk(m_);
int id = id_; int id = id_;
cv_.wait(lk, [&] { return cid_ == id; }); cv_.wait(lk, [&] { return cid_ == id; });
if (sink->is_writable()) { sink->write(message_.data(), message_.size()); } sink->write(message_.data(), message_.size());
} }
void send_event(const string &message) { void send_event(const string &message) {
+360 -117
View File
@@ -1,14 +1,14 @@
// //
// httplib.h // httplib.h
// //
// Copyright (c) 2022 Yuji Hirose. All rights reserved. // Copyright (c) 2023 Yuji Hirose. All rights reserved.
// MIT License // MIT License
// //
#ifndef CPPHTTPLIB_HTTPLIB_H #ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.11.4" #define CPPHTTPLIB_VERSION "0.12.5"
/* /*
* Configuration * Configuration
@@ -239,6 +239,12 @@ using socket_t = int;
#pragma comment(lib, "crypt32.lib") #pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib") #pragma comment(lib, "cryptui.lib")
#endif #endif
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_OSX
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#endif // TARGET_OS_OSX
#endif // _WIN32 #endif // _WIN32
#include <openssl/err.h> #include <openssl/err.h>
@@ -308,6 +314,34 @@ struct ci {
} }
}; };
// This is based on
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
struct scope_exit {
explicit scope_exit(std::function<void(void)> &&f)
: exit_function(std::move(f)), execute_on_destruction{true} {}
scope_exit(scope_exit &&rhs)
: exit_function(std::move(rhs.exit_function)),
execute_on_destruction{rhs.execute_on_destruction} {
rhs.release();
}
~scope_exit() {
if (execute_on_destruction) { this->exit_function(); }
}
void release() { this->execute_on_destruction = false; }
private:
scope_exit(const scope_exit &) = delete;
void operator=(const scope_exit &) = delete;
scope_exit &operator=(scope_exit &&) = delete;
std::function<void(void)> exit_function;
bool execute_on_destruction;
};
} // namespace detail } // namespace detail
using Headers = std::multimap<std::string, std::string, detail::ci>; using Headers = std::multimap<std::string, std::string, detail::ci>;
@@ -340,7 +374,7 @@ public:
std::function<bool(const char *data, size_t data_len)> write; std::function<bool(const char *data, size_t data_len)> write;
std::function<void()> done; std::function<void()> done;
std::function<bool()> is_writable; std::function<void(const Headers &trailer)> done_with_trailer;
std::ostream os; std::ostream os;
private: private:
@@ -455,6 +489,7 @@ struct Request {
bool has_file(const std::string &key) const; bool has_file(const std::string &key) const;
MultipartFormData get_file_value(const std::string &key) const; MultipartFormData get_file_value(const std::string &key) const;
std::vector<MultipartFormData> get_file_values(const std::string &key) const;
// private members... // private members...
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
@@ -708,6 +743,7 @@ public:
bool listen(const std::string &host, int port, int socket_flags = 0); bool listen(const std::string &host, int port, int socket_flags = 0);
bool is_running() const; bool is_running() const;
void wait_until_ready() const;
void stop(); void stop();
std::function<TaskQueue *(void)> new_task_queue; std::function<TaskQueue *(void)> new_task_queue;
@@ -717,7 +753,7 @@ protected:
bool &connection_closed, bool &connection_closed,
const std::function<void(Request &)> &setup_request); const std::function<void(Request &)> &setup_request);
std::atomic<socket_t> svr_sock_; std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
@@ -769,7 +805,7 @@ private:
ContentReceiver multipart_receiver); ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, Request &req, Response &res, bool read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader mulitpart_header, MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver); ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock); virtual bool process_and_close_socket(socket_t sock);
@@ -781,7 +817,8 @@ private:
}; };
std::vector<MountPointEntry> base_dirs_; std::vector<MountPointEntry> base_dirs_;
std::atomic<bool> is_running_; std::atomic<bool> is_running_{false};
std::atomic<bool> done_{false};
std::map<std::string, std::string> file_extension_and_mimetype_map_; std::map<std::string, std::string> file_extension_and_mimetype_map_;
Handler file_request_handler_; Handler file_request_handler_;
Handlers get_handlers_; Handlers get_handlers_;
@@ -823,6 +860,9 @@ enum class Error {
UnsupportedMultipartBoundaryChars, UnsupportedMultipartBoundaryChars,
Compression, Compression,
ConnectionTimeout, ConnectionTimeout,
// For internal use only
SSLPeerCouldBeClosed_,
}; };
std::string to_string(const Error error); std::string to_string(const Error error);
@@ -1095,8 +1135,6 @@ protected:
bool is_open() const { return sock != INVALID_SOCKET; } bool is_open() const { return sock != INVALID_SOCKET; }
}; };
Result send_(Request &&req);
virtual bool create_and_connect_socket(Socket &socket, Error &error); virtual bool create_and_connect_socket(Socket &socket, Error &error);
// All of: // All of:
@@ -1118,7 +1156,7 @@ protected:
void copy_settings(const ClientImpl &rhs); void copy_settings(const ClientImpl &rhs);
// Socket endoint information // Socket endpoint information
const std::string host_; const std::string host_;
const int port_; const int port_;
const std::string host_and_port_; const std::string host_and_port_;
@@ -1197,6 +1235,9 @@ protected:
Logger logger_; Logger logger_;
private: private:
bool send_(Request &req, Response &res, Error &error);
Result send_(Request &&req);
socket_t create_client_socket(Error &error) const; socket_t create_client_socket(Error &error) const;
bool read_response_line(Stream &strm, const Request &req, Response &res); bool read_response_line(Stream &strm, const Request &req, Response &res);
bool write_request(Stream &strm, Request &req, bool close_connection, bool write_request(Stream &strm, Request &req, bool close_connection,
@@ -1665,20 +1706,20 @@ Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
inline std::string to_string(const Error error) { inline std::string to_string(const Error error) {
switch (error) { switch (error) {
case Error::Success: return "Success"; case Error::Success: return "Success (no error)";
case Error::Connection: return "Connection"; case Error::Connection: return "Could not establish connection";
case Error::BindIPAddress: return "BindIPAddress"; case Error::BindIPAddress: return "Failed to bind IP address";
case Error::Read: return "Read"; case Error::Read: return "Failed to read connection";
case Error::Write: return "Write"; case Error::Write: return "Failed to write connection";
case Error::ExceedRedirectCount: return "ExceedRedirectCount"; case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
case Error::Canceled: return "Canceled"; case Error::Canceled: return "Connection handling canceled";
case Error::SSLConnection: return "SSLConnection"; case Error::SSLConnection: return "SSL connection failed";
case Error::SSLLoadingCerts: return "SSLLoadingCerts"; case Error::SSLLoadingCerts: return "SSL certificate loading failed";
case Error::SSLServerVerification: return "SSLServerVerification"; case Error::SSLServerVerification: return "SSL server verification failed";
case Error::UnsupportedMultipartBoundaryChars: case Error::UnsupportedMultipartBoundaryChars:
return "UnsupportedMultipartBoundaryChars"; return "Unsupported HTTP multipart boundary characters";
case Error::Compression: return "Compression"; case Error::Compression: return "Compression failed";
case Error::ConnectionTimeout: return "ConnectionTimeout"; case Error::ConnectionTimeout: return "Connection timed out";
case Error::Unknown: return "Unknown"; case Error::Unknown: return "Unknown";
default: break; default: break;
} }
@@ -1788,6 +1829,9 @@ std::string params_to_query_str(const Params &params);
void parse_query_text(const std::string &s, Params &params); void parse_query_text(const std::string &s, Params &params);
bool parse_multipart_boundary(const std::string &content_type,
std::string &boundary);
bool parse_range_header(const std::string &s, Ranges &ranges); bool parse_range_header(const std::string &s, Ranges &ranges);
int close_socket(socket_t sock); int close_socket(socket_t sock);
@@ -2626,7 +2670,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (sock != INVALID_SOCKET) { if (sock != INVALID_SOCKET) {
sockaddr_un addr; sockaddr_un addr{};
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
std::copy(host.begin(), host.end(), addr.sun_path); std::copy(host.begin(), host.end(), addr.sun_path);
@@ -2684,7 +2728,10 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
if (sock == INVALID_SOCKET) { continue; } if (sock == INVALID_SOCKET) { continue; }
#ifndef _WIN32 #ifndef _WIN32
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
close_socket(sock);
continue;
}
#endif #endif
if (tcp_nodelay) { if (tcp_nodelay) {
@@ -2939,9 +2986,14 @@ inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
inline constexpr unsigned int str2tag_core(const char *s, size_t l, inline constexpr unsigned int str2tag_core(const char *s, size_t l,
unsigned int h) { unsigned int h) {
return (l == 0) ? h return (l == 0)
: str2tag_core(s + 1, l - 1, ? h
(h * 33) ^ static_cast<unsigned char>(*s)); : str2tag_core(
s + 1, l - 1,
// Unsets the 6 high bits of h, therefore no overflow happens
(((std::numeric_limits<unsigned int>::max)() >> 6) &
h * 33) ^
static_cast<unsigned char>(*s));
} }
inline unsigned int str2tag(const std::string &s) { inline unsigned int str2tag(const std::string &s) {
@@ -3354,6 +3406,14 @@ inline const char *get_header_value(const Headers &headers,
return def; return def;
} }
inline bool compare_case_ignore(const std::string &a, const std::string &b) {
if (a.size() != b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) {
if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
}
return true;
}
template <typename T> template <typename T>
inline bool parse_header(const char *beg, const char *end, T fn) { inline bool parse_header(const char *beg, const char *end, T fn) {
// Skip trailing spaces and tabs. // Skip trailing spaces and tabs.
@@ -3377,7 +3437,11 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
} }
if (p < end) { if (p < end) {
fn(std::string(beg, key_end), decode_url(std::string(p, end), false)); auto key = std::string(beg, key_end);
auto val = compare_case_ignore(key, "Location")
? std::string(p, end)
: decode_url(std::string(p, end), false);
fn(std::move(key), std::move(val));
return true; return true;
} }
@@ -3475,7 +3539,8 @@ inline bool read_content_without_length(Stream &strm,
return true; return true;
} }
inline bool read_content_chunked(Stream &strm, template <typename T>
inline bool read_content_chunked(Stream &strm, T &x,
ContentReceiverWithProgress out) { ContentReceiverWithProgress out) {
const auto bufsiz = 16; const auto bufsiz = 16;
char buf[bufsiz]; char buf[bufsiz];
@@ -3501,15 +3566,29 @@ inline bool read_content_chunked(Stream &strm,
if (!line_reader.getline()) { return false; } if (!line_reader.getline()) { return false; }
if (strcmp(line_reader.ptr(), "\r\n")) { break; } if (strcmp(line_reader.ptr(), "\r\n")) { return false; }
if (!line_reader.getline()) { return false; } if (!line_reader.getline()) { return false; }
} }
if (chunk_len == 0) { assert(chunk_len == 0);
// Reader terminator after chunks
if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) // Trailer
return false; if (!line_reader.getline()) { return false; }
while (strcmp(line_reader.ptr(), "\r\n")) {
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
// Exclude line terminator
constexpr auto line_terminator_len = 2;
auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
parse_header(line_reader.ptr(), end,
[&](std::string &&key, std::string &&val) {
x.headers.emplace(std::move(key), std::move(val));
});
if (!line_reader.getline()) { return false; }
} }
return true; return true;
@@ -3579,7 +3658,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
auto exceed_payload_max_length = false; auto exceed_payload_max_length = false;
if (is_chunked_transfer_encoding(x.headers)) { if (is_chunked_transfer_encoding(x.headers)) {
ret = read_content_chunked(strm, out); ret = read_content_chunked(strm, x, out);
} else if (!has_header(x.headers, "Content-Length")) { } else if (!has_header(x.headers, "Content-Length")) {
ret = read_content_without_length(strm, out); ret = read_content_without_length(strm, out);
} else { } else {
@@ -3632,7 +3711,7 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
data_sink.write = [&](const char *d, size_t l) -> bool { data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) { if (ok) {
if (write_data(strm, d, l)) { if (strm.is_writable() && write_data(strm, d, l)) {
offset += l; offset += l;
} else { } else {
ok = false; ok = false;
@@ -3641,14 +3720,14 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
return ok; return ok;
}; };
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (offset < end_offset && !is_shutting_down()) { while (offset < end_offset && !is_shutting_down()) {
if (!content_provider(offset, end_offset - offset, data_sink)) { if (!strm.is_writable()) {
error = Error::Write;
return false;
} else if (!content_provider(offset, end_offset - offset, data_sink)) {
error = Error::Canceled; error = Error::Canceled;
return false; return false;
} } else if (!ok) {
if (!ok) {
error = Error::Write; error = Error::Write;
return false; return false;
} }
@@ -3680,18 +3759,21 @@ write_content_without_length(Stream &strm,
data_sink.write = [&](const char *d, size_t l) -> bool { data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) { if (ok) {
offset += l; offset += l;
if (!write_data(strm, d, l)) { ok = false; } if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }
} }
return ok; return ok;
}; };
data_sink.done = [&](void) { data_available = false; }; data_sink.done = [&](void) { data_available = false; };
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (data_available && !is_shutting_down()) { while (data_available && !is_shutting_down()) {
if (!content_provider(offset, 0, data_sink)) { return false; } if (!strm.is_writable()) {
if (!ok) { return false; } return false;
} else if (!content_provider(offset, 0, data_sink)) {
return false;
} else if (!ok) {
return false;
}
} }
return true; return true;
} }
@@ -3720,7 +3802,10 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
// Emit chunked response header and footer for each chunk // Emit chunked response header and footer for each chunk
auto chunk = auto chunk =
from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; } if (!strm.is_writable() ||
!write_data(strm, chunk.data(), chunk.size())) {
ok = false;
}
} }
} else { } else {
ok = false; ok = false;
@@ -3729,7 +3814,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
return ok; return ok;
}; };
data_sink.done = [&](void) { auto done_with_trailer = [&](const Headers *trailer) {
if (!ok) { return; } if (!ok) { return; }
data_available = false; data_available = false;
@@ -3747,26 +3832,46 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
if (!payload.empty()) { if (!payload.empty()) {
// Emit chunked response header and footer for each chunk // Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (!write_data(strm, chunk.data(), chunk.size())) { if (!strm.is_writable() ||
!write_data(strm, chunk.data(), chunk.size())) {
ok = false; ok = false;
return; return;
} }
} }
static const std::string done_marker("0\r\n\r\n"); static const std::string done_marker("0\r\n");
if (!write_data(strm, done_marker.data(), done_marker.size())) { if (!write_data(strm, done_marker.data(), done_marker.size())) {
ok = false; ok = false;
} }
// Trailer
if (trailer) {
for (const auto &kv : *trailer) {
std::string field_line = kv.first + ": " + kv.second + "\r\n";
if (!write_data(strm, field_line.data(), field_line.size())) {
ok = false;
}
}
}
static const std::string crlf("\r\n");
if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
}; };
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; data_sink.done = [&](void) { done_with_trailer(nullptr); };
data_sink.done_with_trailer = [&](const Headers &trailer) {
done_with_trailer(&trailer);
};
while (data_available && !is_shutting_down()) { while (data_available && !is_shutting_down()) {
if (!content_provider(offset, 0, data_sink)) { if (!strm.is_writable()) {
error = Error::Write;
return false;
} else if (!content_provider(offset, 0, data_sink)) {
error = Error::Canceled; error = Error::Canceled;
return false; return false;
} } else if (!ok) {
if (!ok) {
error = Error::Write; error = Error::Write;
return false; return false;
} }
@@ -3847,9 +3952,12 @@ inline void parse_query_text(const std::string &s, Params &params) {
inline bool parse_multipart_boundary(const std::string &content_type, inline bool parse_multipart_boundary(const std::string &content_type,
std::string &boundary) { std::string &boundary) {
auto pos = content_type.find("boundary="); auto boundary_keyword = "boundary=";
auto pos = content_type.find(boundary_keyword);
if (pos == std::string::npos) { return false; } if (pos == std::string::npos) { return false; }
boundary = content_type.substr(pos + 9); auto end = content_type.find(';', pos);
auto beg = pos + strlen(boundary_keyword);
boundary = content_type.substr(beg, end - beg);
if (boundary.length() >= 2 && boundary.front() == '"' && if (boundary.length() >= 2 && boundary.front() == '"' &&
boundary.back() == '"') { boundary.back() == '"') {
boundary = boundary.substr(1, boundary.size() - 2); boundary = boundary.substr(1, boundary.size() - 2);
@@ -3960,6 +4068,9 @@ public:
if (std::regex_match(header, m, re_content_disposition)) { if (std::regex_match(header, m, re_content_disposition)) {
file_.name = m[1]; file_.name = m[1];
file_.filename = m[2]; file_.filename = m[2];
} else {
is_valid_ = false;
return false;
} }
} }
buf_erase(pos + crlf_.size()); buf_erase(pos + crlf_.size());
@@ -4379,15 +4490,15 @@ inline std::string SHA_512(const std::string &s) {
} }
#endif #endif
#ifdef _WIN32
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
// NOTE: This code came up with the following stackoverflow post: // NOTE: This code came up with the following stackoverflow post:
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
inline bool load_system_certs_on_windows(X509_STORE *store) { inline bool load_system_certs_on_windows(X509_STORE *store) {
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
if (!hStore) { return false; } if (!hStore) { return false; }
auto result = false;
PCCERT_CONTEXT pContext = NULL; PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr) { nullptr) {
@@ -4398,16 +4509,109 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
if (x509) { if (x509) {
X509_STORE_add_cert(store, x509); X509_STORE_add_cert(store, x509);
X509_free(x509); X509_free(x509);
result = true;
} }
} }
CertFreeCertificateContext(pContext); CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0); CertCloseStore(hStore, 0);
return result;
}
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if TARGET_OS_OSX
template <typename T>
using CFObjectPtr =
std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
inline void cf_object_ptr_deleter(CFTypeRef obj) {
if (obj) { CFRelease(obj); }
}
inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
kCFBooleanTrue};
CFObjectPtr<CFDictionaryRef> query(
CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks),
cf_object_ptr_deleter);
if (!query) { return false; }
CFTypeRef security_items = nullptr;
if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
CFArrayGetTypeID() != CFGetTypeID(security_items)) {
return false;
}
certs.reset(reinterpret_cast<CFArrayRef>(security_items));
return true; return true;
} }
#endif
inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
CFArrayRef root_security_items = nullptr;
if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
return false;
}
certs.reset(root_security_items);
return true;
}
inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
auto result = false;
for (int i = 0; i < CFArrayGetCount(certs); ++i) {
const auto cert = reinterpret_cast<const __SecCertificate *>(
CFArrayGetValueAtIndex(certs, i));
if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
CFDataRef cert_data = nullptr;
if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
errSecSuccess) {
continue;
}
CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
auto encoded_cert = static_cast<const unsigned char *>(
CFDataGetBytePtr(cert_data_ptr.get()));
auto x509 =
d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
if (x509) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
result = true;
}
}
return result;
}
inline bool load_system_certs_on_macos(X509_STORE *store) {
auto result = false;
CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
if (retrieve_certs_from_keychain(certs) && certs) {
result = add_certs_to_x509_store(certs.get(), store);
}
if (retrieve_root_certs_from_keychain(certs) && certs) {
result = add_certs_to_x509_store(certs.get(), store) || result;
}
return result;
}
#endif // TARGET_OS_OSX
#endif // _WIN32
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
class WSInit { class WSInit {
public: public:
WSInit() { WSInit() {
@@ -4682,6 +4886,16 @@ inline MultipartFormData Request::get_file_value(const std::string &key) const {
return MultipartFormData(); return MultipartFormData();
} }
inline std::vector<MultipartFormData>
Request::get_file_values(const std::string &key) const {
std::vector<MultipartFormData> values;
auto rng = files.equal_range(key);
for (auto it = rng.first; it != rng.second; it++) {
values.push_back(it->second);
}
return values;
}
// Response implementation // Response implementation
inline bool Response::has_header(const std::string &key) const { inline bool Response::has_header(const std::string &key) const {
return headers.find(key) != headers.end(); return headers.find(key) != headers.end();
@@ -4732,10 +4946,9 @@ inline void Response::set_content(const std::string &s,
inline void Response::set_content_provider( inline void Response::set_content_provider(
size_t in_length, const std::string &content_type, ContentProvider provider, size_t in_length, const std::string &content_type, ContentProvider provider,
ContentProviderResourceReleaser resource_releaser) { ContentProviderResourceReleaser resource_releaser) {
assert(in_length > 0);
set_header("Content-Type", content_type); set_header("Content-Type", content_type);
content_length_ = in_length; content_length_ = in_length;
content_provider_ = std::move(provider); if (in_length > 0) { content_provider_ = std::move(provider); }
content_provider_resource_releaser_ = resource_releaser; content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ = false; is_chunked_content_provider_ = false;
} }
@@ -4912,8 +5125,7 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
// HTTP server implementation // HTTP server implementation
inline Server::Server() inline Server::Server()
: new_task_queue( : new_task_queue(
[] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }), [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
svr_sock_(INVALID_SOCKET), is_running_(false) {
#ifndef _WIN32 #ifndef _WIN32
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#endif #endif
@@ -5126,15 +5338,25 @@ inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
return bind_internal(host, 0, socket_flags); return bind_internal(host, 0, socket_flags);
} }
inline bool Server::listen_after_bind() { return listen_internal(); } inline bool Server::listen_after_bind() {
auto se = detail::scope_exit([&]() { done_ = true; });
return listen_internal();
}
inline bool Server::listen(const std::string &host, int port, inline bool Server::listen(const std::string &host, int port,
int socket_flags) { int socket_flags) {
auto se = detail::scope_exit([&]() { done_ = true; });
return bind_to_port(host, port, socket_flags) && listen_internal(); return bind_to_port(host, port, socket_flags) && listen_internal();
} }
inline bool Server::is_running() const { return is_running_; } inline bool Server::is_running() const { return is_running_; }
inline void Server::wait_until_ready() const {
while (!is_running() && !done_) {
std::this_thread::sleep_for(std::chrono::milliseconds{1});
}
}
inline void Server::stop() { inline void Server::stop() {
if (is_running_) { if (is_running_) {
assert(svr_sock_ != INVALID_SOCKET); assert(svr_sock_ != INVALID_SOCKET);
@@ -5397,7 +5619,7 @@ inline bool Server::read_content_with_content_receiver(
inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader mulitpart_header, MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) { ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser; detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiverWithProgress out; ContentReceiverWithProgress out;
@@ -5417,14 +5639,14 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
while (pos < n) { while (pos < n) {
auto read_size = (std::min)<size_t>(1, n - pos); auto read_size = (std::min)<size_t>(1, n - pos);
auto ret = multipart_form_data_parser.parse( auto ret = multipart_form_data_parser.parse(
buf + pos, read_size, multipart_receiver, mulitpart_header); buf + pos, read_size, multipart_receiver, multipart_header);
if (!ret) { return false; } if (!ret) { return false; }
pos += read_size; pos += read_size;
} }
return true; return true;
*/ */
return multipart_form_data_parser.parse(buf, n, multipart_receiver, return multipart_form_data_parser.parse(buf, n, multipart_receiver,
mulitpart_header); multipart_header);
}; };
} else { } else {
out = [receiver](const char *buf, size_t n, uint64_t /*off*/, out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
@@ -5525,6 +5747,7 @@ inline int Server::bind_internal(const std::string &host, int port,
inline bool Server::listen_internal() { inline bool Server::listen_internal() {
auto ret = true; auto ret = true;
is_running_ = true; is_running_ = true;
auto se = detail::scope_exit([&]() { is_running_ = false; });
{ {
std::unique_ptr<TaskQueue> task_queue(new_task_queue()); std::unique_ptr<TaskQueue> task_queue(new_task_queue());
@@ -5590,17 +5813,12 @@ inline bool Server::listen_internal() {
#endif #endif
} }
#if __cplusplus > 201703L task_queue->enqueue([this, sock]() { process_and_close_socket(sock); });
task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
#else
task_queue->enqueue([=]() { process_and_close_socket(sock); });
#endif
} }
task_queue->shutdown(); task_queue->shutdown();
} }
is_running_ = false;
return ret; return ret;
} }
@@ -5707,7 +5925,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
res.headers.erase(it); res.headers.erase(it);
} }
res.headers.emplace("Content-Type", res.set_header("Content-Type",
"multipart/byteranges; boundary=" + boundary); "multipart/byteranges; boundary=" + boundary);
} }
@@ -6141,7 +6359,15 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
inline bool ClientImpl::send(Request &req, Response &res, Error &error) { inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_); std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
auto ret = send_(req, res, error);
if (error == Error::SSLPeerCouldBeClosed_) {
assert(!ret);
ret = send_(req, res, error);
}
return ret;
}
inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
{ {
std::lock_guard<std::mutex> guard(socket_mutex_); std::lock_guard<std::mutex> guard(socket_mutex_);
@@ -6172,7 +6398,7 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
if (is_ssl()) { if (is_ssl()) {
auto &scli = static_cast<SSLClient &>(*this); auto &scli = static_cast<SSLClient &>(*this);
if (!proxy_host_.empty() && proxy_port_ != -1) { if (!proxy_host_.empty() && proxy_port_ != -1) {
bool success = false; auto success = false;
if (!scli.connect_with_proxy(socket_, res, success, error)) { if (!scli.connect_with_proxy(socket_, res, success, error)) {
return success; return success;
} }
@@ -6199,13 +6425,11 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
} }
} }
auto ret = false;
auto close_connection = !keep_alive_; auto close_connection = !keep_alive_;
auto ret = process_socket(socket_, [&](Stream &strm) {
return handle_request(strm, req, res, close_connection, error);
});
auto se = detail::scope_exit([&]() {
// Briefly lock mutex in order to mark that a request is no longer ongoing // Briefly lock mutex in order to mark that a request is no longer ongoing
{
std::lock_guard<std::mutex> guard(socket_mutex_); std::lock_guard<std::mutex> guard(socket_mutex_);
socket_requests_in_flight_ -= 1; socket_requests_in_flight_ -= 1;
if (socket_requests_in_flight_ <= 0) { if (socket_requests_in_flight_ <= 0) {
@@ -6219,7 +6443,11 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
shutdown_socket(socket_); shutdown_socket(socket_);
close_socket(socket_); close_socket(socket_);
} }
} });
ret = process_socket(socket_, [&](Stream &strm) {
return handle_request(strm, req, res, close_connection, error);
});
if (!ret) { if (!ret) {
if (error == Error::Success) { error = Error::Unknown; } if (error == Error::Success) { error = Error::Unknown; }
@@ -6307,11 +6535,11 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
return false; return false;
} }
auto location = detail::decode_url(res.get_header_value("location"), true); auto location = res.get_header_value("location");
if (location.empty()) { return false; } if (location.empty()) { return false; }
const static std::regex re( const static std::regex re(
R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
std::smatch m; std::smatch m;
if (!std::regex_match(location, m, re)) { return false; } if (!std::regex_match(location, m, re)) { return false; }
@@ -6323,6 +6551,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
if (next_host.empty()) { next_host = m[3].str(); } if (next_host.empty()) { next_host = m[3].str(); }
auto port_str = m[4].str(); auto port_str = m[4].str();
auto next_path = m[5].str(); auto next_path = m[5].str();
auto next_query = m[6].str();
auto next_port = port_; auto next_port = port_;
if (!port_str.empty()) { if (!port_str.empty()) {
@@ -6335,22 +6564,24 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
if (next_host.empty()) { next_host = host_; } if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; } if (next_path.empty()) { next_path = "/"; }
auto path = detail::decode_url(next_path, true) + next_query;
if (next_scheme == scheme && next_host == host_ && next_port == port_) { if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, next_path, location, error); return detail::redirect(*this, req, res, path, location, error);
} else { } else {
if (next_scheme == "https") { if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port); SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this); cli.copy_settings(*this);
if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
return detail::redirect(cli, req, res, next_path, location, error); return detail::redirect(cli, req, res, path, location, error);
#else #else
return false; return false;
#endif #endif
} else { } else {
ClientImpl cli(next_host.c_str(), next_port); ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*this); cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path, location, error); return detail::redirect(cli, req, res, path, location, error);
} }
} }
} }
@@ -6361,7 +6592,7 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
auto is_shutting_down = []() { return false; }; auto is_shutting_down = []() { return false; };
if (req.is_chunked_content_provider_) { if (req.is_chunked_content_provider_) {
// TODO: Brotli suport // TODO: Brotli support
std::unique_ptr<detail::compressor> compressor; std::unique_ptr<detail::compressor> compressor;
#ifdef CPPHTTPLIB_ZLIB_SUPPORT #ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) { if (compress_) {
@@ -6378,39 +6609,39 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
return detail::write_content(strm, req.content_provider_, 0, return detail::write_content(strm, req.content_provider_, 0,
req.content_length_, is_shutting_down, error); req.content_length_, is_shutting_down, error);
} }
} // namespace httplib }
inline bool ClientImpl::write_request(Stream &strm, Request &req, inline bool ClientImpl::write_request(Stream &strm, Request &req,
bool close_connection, Error &error) { bool close_connection, Error &error) {
// Prepare additional headers // Prepare additional headers
if (close_connection) { if (close_connection) {
if (!req.has_header("Connection")) { if (!req.has_header("Connection")) {
req.headers.emplace("Connection", "close"); req.set_header("Connection", "close");
} }
} }
if (!req.has_header("Host")) { if (!req.has_header("Host")) {
if (is_ssl()) { if (is_ssl()) {
if (port_ == 443) { if (port_ == 443) {
req.headers.emplace("Host", host_); req.set_header("Host", host_);
} else { } else {
req.headers.emplace("Host", host_and_port_); req.set_header("Host", host_and_port_);
} }
} else { } else {
if (port_ == 80) { if (port_ == 80) {
req.headers.emplace("Host", host_); req.set_header("Host", host_);
} else { } else {
req.headers.emplace("Host", host_and_port_); req.set_header("Host", host_and_port_);
} }
} }
} }
if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); } if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
if (!req.has_header("User-Agent")) { if (!req.has_header("User-Agent")) {
auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION; auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
req.headers.emplace("User-Agent", agent); req.set_header("User-Agent", agent);
} }
#endif #endif
@@ -6419,23 +6650,23 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
if (!req.is_chunked_content_provider_) { if (!req.is_chunked_content_provider_) {
if (!req.has_header("Content-Length")) { if (!req.has_header("Content-Length")) {
auto length = std::to_string(req.content_length_); auto length = std::to_string(req.content_length_);
req.headers.emplace("Content-Length", length); req.set_header("Content-Length", length);
} }
} }
} else { } else {
if (req.method == "POST" || req.method == "PUT" || if (req.method == "POST" || req.method == "PUT" ||
req.method == "PATCH") { req.method == "PATCH") {
req.headers.emplace("Content-Length", "0"); req.set_header("Content-Length", "0");
} }
} }
} else { } else {
if (!req.has_header("Content-Type")) { if (!req.has_header("Content-Type")) {
req.headers.emplace("Content-Type", "text/plain"); req.set_header("Content-Type", "text/plain");
} }
if (!req.has_header("Content-Length")) { if (!req.has_header("Content-Length")) {
auto length = std::to_string(req.body.size()); auto length = std::to_string(req.body.size());
req.headers.emplace("Content-Length", length); req.set_header("Content-Length", length);
} }
} }
@@ -6503,12 +6734,10 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
ContentProvider content_provider, ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length, ContentProviderWithoutLength content_provider_without_length,
const std::string &content_type, Error &error) { const std::string &content_type, Error &error) {
if (!content_type.empty()) { if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
req.headers.emplace("Content-Type", content_type);
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT #ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); } if (compress_) { req.set_header("Content-Encoding", "gzip"); }
#endif #endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT #ifdef CPPHTTPLIB_ZLIB_SUPPORT
@@ -6541,8 +6770,6 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
return ok; return ok;
}; };
data_sink.is_writable = [&](void) { return ok && true; };
while (ok && offset < content_length) { while (ok && offset < content_length) {
if (!content_provider(offset, content_length - offset, data_sink)) { if (!content_provider(offset, content_length - offset, data_sink)) {
error = Error::Canceled; error = Error::Canceled;
@@ -6571,7 +6798,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
req.content_provider_ = detail::ContentProviderAdapter( req.content_provider_ = detail::ContentProviderAdapter(
std::move(content_provider_without_length)); std::move(content_provider_without_length));
req.is_chunked_content_provider_ = true; req.is_chunked_content_provider_ = true;
req.headers.emplace("Transfer-Encoding", "chunked"); req.set_header("Transfer-Encoding", "chunked");
} else { } else {
req.body.assign(body, content_length); req.body.assign(body, content_length);
; ;
@@ -6613,6 +6840,20 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
// Send request // Send request
if (!write_request(strm, req, close_connection, error)) { return false; } if (!write_request(strm, req, close_connection, error)) { return false; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (is_ssl()) {
auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
if (!is_proxy_enabled) {
char buf[1];
if (SSL_peek(socket_.ssl, buf, 1) == 0 &&
SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {
error = Error::SSLPeerCouldBeClosed_;
return false;
}
}
}
#endif
// Receive response and headers // Receive response and headers
if (!read_response_line(strm, req, res) || if (!read_response_line(strm, req, res) ||
!detail::read_headers(strm, res.headers)) { !detail::read_headers(strm, res.headers)) {
@@ -6714,7 +6955,6 @@ inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
bool has_data = true; bool has_data = true;
cur_sink.write = sink.write; cur_sink.write = sink.write;
cur_sink.done = [&]() { has_data = false; }; cur_sink.done = [&]() { has_data = false; };
cur_sink.is_writable = sink.is_writable;
if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
return false; return false;
@@ -7181,9 +7421,7 @@ inline Result ClientImpl::Delete(const std::string &path,
req.headers = headers; req.headers = headers;
req.path = path; req.path = path;
if (!content_type.empty()) { if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
req.headers.emplace("Content-Type", content_type);
}
req.body.assign(body, content_length); req.body.assign(body, content_length);
return send_(std::move(req)); return send_(std::move(req));
@@ -7240,7 +7478,7 @@ inline void ClientImpl::stop() {
return; return;
} }
// Otherwise, sitll holding the mutex, we can shut everything down ourselves // Otherwise, still holding the mutex, we can shut everything down ourselves
shutdown_ssl(socket_, true); shutdown_ssl(socket_, true);
shutdown_socket(socket_); shutdown_socket(socket_);
close_socket(socket_); close_socket(socket_);
@@ -7653,7 +7891,7 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
}, },
[](SSL * /*ssl2*/) { return true; }); [](SSL * /*ssl2*/) { return true; });
bool ret = false; auto ret = false;
if (ssl) { if (ssl) {
ret = detail::process_server_socket_ssl( ret = detail::process_server_socket_ssl(
svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
@@ -7831,11 +8069,16 @@ inline bool SSLClient::load_certs() {
ret = false; ret = false;
} }
} else { } else {
auto loaded = false;
#ifdef _WIN32 #ifdef _WIN32
loaded =
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
#else #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
SSL_CTX_set_default_verify_paths(ctx_); #if TARGET_OS_OSX
#endif loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
#endif // TARGET_OS_OSX
#endif // _WIN32
if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
} }
}); });
@@ -7980,7 +8223,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
if (alt_names) { if (alt_names) {
auto dsn_matched = false; auto dsn_matched = false;
auto ip_mached = false; auto ip_matched = false;
auto count = sk_GENERAL_NAME_num(alt_names); auto count = sk_GENERAL_NAME_num(alt_names);
@@ -7996,14 +8239,14 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
case GEN_IPADD: case GEN_IPADD:
if (!memcmp(&addr6, name, addr_len) || if (!memcmp(&addr6, name, addr_len) ||
!memcmp(&addr, name, addr_len)) { !memcmp(&addr, name, addr_len)) {
ip_mached = true; ip_matched = true;
} }
break; break;
} }
} }
} }
if (dsn_matched || ip_mached) { ret = true; } if (dsn_matched || ip_matched) { ret = true; }
} }
GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
+17 -8
View File
@@ -12,19 +12,19 @@ set(HTTPLIB_VERSION @PROJECT_VERSION@)
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
# We add find_dependency calls here to not make the end-user have to call them. # We add find_dependency calls here to not make the end-user have to call them.
find_dependency(Threads REQUIRED) find_dependency(Threads)
if(@HTTPLIB_IS_USING_OPENSSL@) if(@HTTPLIB_IS_USING_OPENSSL@)
# OpenSSL COMPONENTS were added in Cmake v3.11 # OpenSSL COMPONENTS were added in Cmake v3.11
if(CMAKE_VERSION VERSION_LESS "3.11") if(CMAKE_VERSION VERSION_LESS "3.11")
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED) find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@)
else() else()
# Once the COMPONENTS were added, they were made optional when not specified. # Once the COMPONENTS were added, they were made optional when not specified.
# Since we use both, we need to search for both. # Since we use both, we need to search for both.
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED) find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL)
endif() endif()
endif() endif()
if(@HTTPLIB_IS_USING_ZLIB@) if(@HTTPLIB_IS_USING_ZLIB@)
find_dependency(ZLIB REQUIRED) find_dependency(ZLIB)
endif() endif()
if(@HTTPLIB_IS_USING_BROTLI@) if(@HTTPLIB_IS_USING_BROTLI@)
@@ -32,7 +32,7 @@ if(@HTTPLIB_IS_USING_BROTLI@)
# Note that the FindBrotli.cmake file is installed in the same dir as this file. # Note that the FindBrotli.cmake file is installed in the same dir as this file.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@) set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@)
find_dependency(Brotli COMPONENTS common encoder decoder REQUIRED) find_dependency(Brotli COMPONENTS common encoder decoder)
endif() endif()
# Mildly useful for end-users # Mildly useful for end-users
@@ -42,10 +42,19 @@ set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
# This is helpful if you're using Cmake's pre-compiled header feature # This is helpful if you're using Cmake's pre-compiled header feature
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h") set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
# Brings in the target library # Consider each library support as a "component"
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake") set(httplib_OpenSSL_FOUND @HTTPLIB_IS_USING_OPENSSL@)
set(httplib_ZLIB_FOUND @HTTPLIB_IS_USING_ZLIB@)
set(httplib_Brotli_FOUND @HTTPLIB_IS_USING_BROTLI@)
# Ouputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib) check_required_components(httplib)
# Brings in the target library, but only if all required components are found
if(NOT DEFINED httplib_FOUND OR httplib_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
endif()
# Outputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
include(FindPackageMessage) include(FindPackageMessage)
if(TARGET httplib::httplib) if(TARGET httplib::httplib)
set(HTTPLIB_FOUND TRUE) set(HTTPLIB_FOUND TRUE)
+7
View File
@@ -34,6 +34,13 @@ openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cp
if openssl_dep.found() if openssl_dep.found()
deps += openssl_dep deps += openssl_dep
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT' args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
if host_machine.system() == 'darwin'
macosx_keychain_dep = dependency('appleframeworks', modules: ['CoreFoundation', 'Security'], required: get_option('cpp-httplib_macosx_keychain'))
if macosx_keychain_dep.found()
deps += macosx_keychain_dep
args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN'
endif
endif
endif endif
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib')) zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
+1
View File
@@ -5,5 +5,6 @@
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support') option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support') option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support') option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)') option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests') option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
+104
View File
@@ -0,0 +1,104 @@
find_package(GTest)
if(GTest_FOUND)
if(NOT TARGET GTest::gtest_main AND TARGET GTest::Main)
# CMake <3.20
add_library(GTest::gtest_main INTERFACE IMPORTED)
target_link_libraries(GTest::gtest_main INTERFACE GTest::Main)
endif()
else()
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
include(FetchContent)
set(BUILD_GMOCK OFF)
set(INSTALL_GTEST OFF)
set(gtest_force_shared_crt ON)
FetchContent_Declare(
gtest
URL https://github.com/google/googletest/archive/main.tar.gz
)
FetchContent_MakeAvailable(gtest)
endif()
add_executable(httplib-test test.cc)
target_compile_options(httplib-test PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/bigobj>")
target_link_libraries(httplib-test PRIVATE httplib GTest::gtest_main)
gtest_discover_tests(httplib-test)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www www
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www2 www2
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/www3 www3
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_LIST_DIR}/ca-bundle.crt ca-bundle.crt
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_LIST_DIR}/image.jpg image.jpg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
if(HTTPLIB_IS_USING_OPENSSL)
find_program(OPENSSL_COMMAND
NAMES openssl
PATHS ${OPENSSL_INCLUDE_DIR}/../bin
REQUIRED
)
execute_process(
COMMAND ${OPENSSL_COMMAND} genrsa 2048
OUTPUT_FILE key.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key.pem
OUTPUT_FILE cert.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} req -x509 -new -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} genrsa 2048
OUTPUT_FILE rootCA.key.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} req -x509 -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.rootCA.conf -key rootCA.key.pem -days 1024
OUTPUT_FILE rootCA.cert.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} genrsa 2048
OUTPUT_FILE client.key.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client.key.pem
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
OUTPUT_FILE client.cert.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} genrsa -passout pass:test123! 2048
OUTPUT_FILE key_encrypted.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key_encrypted.pem
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key_encrypted.pem
OUTPUT_FILE cert_encrypted.pem
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
endif()
+7
View File
@@ -8,6 +8,13 @@ OPENSSL_DIR = $(PREFIX)/opt/openssl@1.1
#OPENSSL_DIR = $(PREFIX)/opt/openssl@3 #OPENSSL_DIR = $(PREFIX)/opt/openssl@3
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
ifneq ($(OS), Windows_NT)
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security
endif
endif
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
BROTLI_DIR = $(PREFIX)/opt/brotli BROTLI_DIR = $(PREFIX)/opt/brotli
@@ -11,7 +11,7 @@
#include <vector> #include <vector>
// Forward declare the "fuzz target" interface. // Forward declare the "fuzz target" interface.
// We deliberately keep this inteface simple and header-free. // We deliberately keep this interface simple and header-free.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
// It reads all files passed as parameters and feeds their contents // It reads all files passed as parameters and feeds their contents
+652 -284
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -193,7 +193,7 @@ void DigestAuthTestFromHTTPWatch(T& cli) {
} }
// NOTE: Until httpbin.org fixes issue #46, the following test is commented // NOTE: Until httpbin.org fixes issue #46, the following test is commented
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world // out. Please see https://httpbin.org/digest-auth/auth/hello/world
// cli.set_digest_auth("bad", "world"); // cli.set_digest_auth("bad", "world");
// for (auto path : paths) { // for (auto path : paths) {
// auto res = cli.Get(path.c_str()); // auto res = cli.Get(path.c_str());