Compare commits

..

24 Commits

Author SHA1 Message Date
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 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
8 changed files with 696 additions and 400 deletions
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
git config --global core.autocrlf false
git config --global core.eol lf
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: install brotli library on ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt update && sudo apt-get install -y libbrotli-dev
@@ -32,7 +32,7 @@ jobs:
run: cd test && make fuzz_test
- name: setup msbuild on windows
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.0.2
uses: microsoft/setup-msbuild@v1.1
- name: make-windows
if: matrix.os == 'windows-latest'
run: |
+9 -1
View File
@@ -6,6 +6,7 @@
* HTTPLIB_REQUIRE_OPENSSL (default off)
* HTTPLIB_REQUIRE_ZLIB (default off)
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
* HTTPLIB_REQUIRE_BROTLI (default off)
* HTTPLIB_COMPILE (default off)
* HTTPLIB_TEST (default off)
@@ -43,6 +44,7 @@
* 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_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_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).
@@ -92,6 +94,7 @@ 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_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
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)
@@ -137,6 +140,10 @@ if(Brotli_FOUND)
set(HTTPLIB_IS_USING_BROTLI TRUE)
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)
# like CMAKE_INSTALL_INCLUDEDIR or CMAKE_INSTALL_DATADIR
include(GNUInstallDirs)
@@ -207,7 +214,7 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<PLATFORM_ID:Windows>:crypt32>
$<$<PLATFORM_ID:Windows>:cryptui>
# Needed for API from MacOS Security framework
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>>:-framework CoreFoundation -framework Security>"
"$<$<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.
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
@@ -222,6 +229,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_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
+27 -5
View File
@@ -54,7 +54,8 @@ SSL Support
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
NOTE for macOS: cpp-httplib now uses system certs. `CoreFoundation` and `Security` should be linked with `-framework`.
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++
#define CPPHTTPLIB_OPENSSL_SUPPORT
@@ -75,7 +76,7 @@ cli.set_ca_cert_path("./ca-bundle.crt");
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
------
@@ -347,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
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
@@ -813,14 +835,14 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
#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
-------
MIT license (© 2022 Yuji Hirose)
MIT license (© 2023 Yuji Hirose)
Special Thanks To
-----------------
+147 -71
View File
@@ -1,14 +1,14 @@
//
// httplib.h
//
// Copyright (c) 2022 Yuji Hirose. All rights reserved.
// Copyright (c) 2023 Yuji Hirose. All rights reserved.
// MIT License
//
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.12.1"
#define CPPHTTPLIB_VERSION "0.12.4"
/*
* Configuration
@@ -239,10 +239,13 @@ using socket_t = int;
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
#elif defined(__APPLE__) // _WIN32
#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 // __APPLE__
#endif // TARGET_OS_OSX
#endif // _WIN32
#include <openssl/err.h>
#include <openssl/evp.h>
@@ -314,8 +317,8 @@ struct ci {
// This is based on
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
template <typename EF> struct scope_exit {
explicit scope_exit(EF &&f)
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)
@@ -335,7 +338,7 @@ private:
void operator=(const scope_exit &) = delete;
scope_exit &operator=(scope_exit &&) = delete;
EF exit_function;
std::function<void(void)> exit_function;
bool execute_on_destruction;
};
@@ -371,6 +374,7 @@ public:
std::function<bool(const char *data, size_t data_len)> write;
std::function<void()> done;
std::function<void(const Headers &trailer)> done_with_trailer;
std::ostream os;
private:
@@ -739,6 +743,7 @@ public:
bool listen(const std::string &host, int port, int socket_flags = 0);
bool is_running() const;
void wait_until_ready() const;
void stop();
std::function<TaskQueue *(void)> new_task_queue;
@@ -748,7 +753,7 @@ protected:
bool &connection_closed,
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;
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
@@ -812,7 +817,8 @@ private:
};
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_;
Handler file_request_handler_;
Handlers get_handlers_;
@@ -1823,7 +1829,8 @@ std::string params_to_query_str(const 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_multipart_boundary(const std::string &content_type,
std::string &boundary);
bool parse_range_header(const std::string &s, Ranges &ranges);
@@ -2663,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);
if (sock != INVALID_SOCKET) {
sockaddr_un addr;
sockaddr_un addr{};
addr.sun_family = AF_UNIX;
std::copy(host.begin(), host.end(), addr.sun_path);
@@ -2721,7 +2728,10 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
if (sock == INVALID_SOCKET) { continue; }
#ifndef _WIN32
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
close_socket(sock);
continue;
}
#endif
if (tcp_nodelay) {
@@ -2976,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,
unsigned int h) {
return (l == 0) ? h
: str2tag_core(s + 1, l - 1,
(h * 33) ^ static_cast<unsigned char>(*s));
return (l == 0)
? h
: 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) {
@@ -3391,6 +3406,14 @@ inline const char *get_header_value(const Headers &headers,
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>
inline bool parse_header(const char *beg, const char *end, T fn) {
// Skip trailing spaces and tabs.
@@ -3414,7 +3437,11 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
}
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;
}
@@ -3512,7 +3539,8 @@ inline bool read_content_without_length(Stream &strm,
return true;
}
inline bool read_content_chunked(Stream &strm,
template <typename T>
inline bool read_content_chunked(Stream &strm, T &x,
ContentReceiverWithProgress out) {
const auto bufsiz = 16;
char buf[bufsiz];
@@ -3538,15 +3566,29 @@ inline bool read_content_chunked(Stream &strm,
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 (chunk_len == 0) {
// Reader terminator after chunks
if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n"))
return false;
assert(chunk_len == 0);
// Trailer
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;
@@ -3616,7 +3658,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
auto exceed_payload_max_length = false;
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")) {
ret = read_content_without_length(strm, out);
} else {
@@ -3772,7 +3814,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
return ok;
};
data_sink.done = [&](void) {
auto done_with_trailer = [&](const Headers *trailer) {
if (!ok) { return; }
data_available = false;
@@ -3790,16 +3832,36 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
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())) {
if (!strm.is_writable() ||
!write_data(strm, chunk.data(), chunk.size())) {
ok = false;
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())) {
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.done = [&](void) { done_with_trailer(nullptr); };
data_sink.done_with_trailer = [&](const Headers &trailer) {
done_with_trailer(&trailer);
};
while (data_available && !is_shutting_down()) {
@@ -4456,7 +4518,8 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
return result;
}
#elif defined(__APPLE__)
#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)>;
@@ -4531,7 +4594,7 @@ inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
return result;
}
inline bool load_system_certs_on_apple(X509_STORE *store) {
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) {
@@ -4544,8 +4607,9 @@ inline bool load_system_certs_on_apple(X509_STORE *store) {
return result;
}
#endif
#endif
#endif // TARGET_OS_OSX
#endif // _WIN32
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
class WSInit {
@@ -4882,10 +4946,9 @@ inline void Response::set_content(const std::string &s,
inline void Response::set_content_provider(
size_t in_length, const std::string &content_type, ContentProvider provider,
ContentProviderResourceReleaser resource_releaser) {
assert(in_length > 0);
set_header("Content-Type", content_type);
content_length_ = in_length;
content_provider_ = std::move(provider);
if (in_length > 0) { content_provider_ = std::move(provider); }
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ = false;
}
@@ -5062,8 +5125,7 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
// HTTP server implementation
inline Server::Server()
: new_task_queue(
[] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
svr_sock_(INVALID_SOCKET), is_running_(false) {
[] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
@@ -5276,15 +5338,25 @@ inline int Server::bind_to_any_port(const std::string &host, int 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,
int socket_flags) {
auto se = detail::scope_exit([&]() { done_ = true; });
return bind_to_port(host, port, socket_flags) && listen_internal();
}
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() {
if (is_running_) {
assert(svr_sock_ != INVALID_SOCKET);
@@ -5675,6 +5747,7 @@ inline int Server::bind_internal(const std::string &host, int port,
inline bool Server::listen_internal() {
auto ret = true;
is_running_ = true;
auto se = detail::scope_exit([&]() { is_running_ = false; });
{
std::unique_ptr<TaskQueue> task_queue(new_task_queue());
@@ -5746,7 +5819,6 @@ inline bool Server::listen_internal() {
task_queue->shutdown();
}
is_running_ = false;
return ret;
}
@@ -5853,8 +5925,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
res.headers.erase(it);
}
res.headers.emplace("Content-Type",
"multipart/byteranges; boundary=" + boundary);
res.set_header("Content-Type",
"multipart/byteranges; boundary=" + boundary);
}
auto type = detail::encoding_type(req, res);
@@ -6356,7 +6428,7 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
auto ret = false;
auto close_connection = !keep_alive_;
auto se = detail::scope_exit<std::function<void(void)>>([&]() {
auto se = detail::scope_exit([&]() {
// Briefly lock mutex in order to mark that a request is no longer ongoing
std::lock_guard<std::mutex> guard(socket_mutex_);
socket_requests_in_flight_ -= 1;
@@ -6463,11 +6535,11 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
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; }
const static std::regex re(
R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
std::smatch m;
if (!std::regex_match(location, m, re)) { return false; }
@@ -6479,6 +6551,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
if (next_host.empty()) { next_host = m[3].str(); }
auto port_str = m[4].str();
auto next_path = m[5].str();
auto next_query = m[6].str();
auto next_port = port_;
if (!port_str.empty()) {
@@ -6491,22 +6564,24 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
if (next_host.empty()) { next_host = host_; }
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_) {
return detail::redirect(*this, req, res, next_path, location, error);
return detail::redirect(*this, req, res, path, location, error);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
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
return false;
#endif
} else {
ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path, location, error);
return detail::redirect(cli, req, res, path, location, error);
}
}
}
@@ -6541,32 +6616,32 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
// Prepare additional headers
if (close_connection) {
if (!req.has_header("Connection")) {
req.headers.emplace("Connection", "close");
req.set_header("Connection", "close");
}
}
if (!req.has_header("Host")) {
if (is_ssl()) {
if (port_ == 443) {
req.headers.emplace("Host", host_);
req.set_header("Host", host_);
} else {
req.headers.emplace("Host", host_and_port_);
req.set_header("Host", host_and_port_);
}
} else {
if (port_ == 80) {
req.headers.emplace("Host", host_);
req.set_header("Host", host_);
} 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
if (!req.has_header("User-Agent")) {
auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
req.headers.emplace("User-Agent", agent);
req.set_header("User-Agent", agent);
}
#endif
@@ -6575,23 +6650,23 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
if (!req.is_chunked_content_provider_) {
if (!req.has_header("Content-Length")) {
auto length = std::to_string(req.content_length_);
req.headers.emplace("Content-Length", length);
req.set_header("Content-Length", length);
}
}
} else {
if (req.method == "POST" || req.method == "PUT" ||
req.method == "PATCH") {
req.headers.emplace("Content-Length", "0");
req.set_header("Content-Length", "0");
}
}
} else {
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")) {
auto length = std::to_string(req.body.size());
req.headers.emplace("Content-Length", length);
req.set_header("Content-Length", length);
}
}
@@ -6659,12 +6734,10 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const std::string &content_type, Error &error) {
if (!content_type.empty()) {
req.headers.emplace("Content-Type", content_type);
}
if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); }
if (compress_) { req.set_header("Content-Encoding", "gzip"); }
#endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
@@ -6725,7 +6798,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
req.content_provider_ = detail::ContentProviderAdapter(
std::move(content_provider_without_length));
req.is_chunked_content_provider_ = true;
req.headers.emplace("Transfer-Encoding", "chunked");
req.set_header("Transfer-Encoding", "chunked");
} else {
req.body.assign(body, content_length);
;
@@ -6769,11 +6842,14 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (is_ssl()) {
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;
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
@@ -7345,9 +7421,7 @@ inline Result ClientImpl::Delete(const std::string &path,
req.headers = headers;
req.path = path;
if (!content_type.empty()) {
req.headers.emplace("Content-Type", content_type);
}
if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
req.body.assign(body, content_length);
return send_(std::move(req));
@@ -7999,9 +8073,11 @@ inline bool SSLClient::load_certs() {
#ifdef _WIN32
loaded =
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
#elif defined(__APPLE__)
loaded = detail::load_system_certs_on_apple(SSL_CTX_get_cert_store(ctx_));
#endif
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if TARGET_OS_OSX
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_); }
}
});
+5 -1
View File
@@ -35,7 +35,11 @@ if openssl_dep.found()
deps += openssl_dep
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
if host_machine.system() == 'darwin'
deps += dependency('appleframeworks', modules: ['CoreFoundation', 'Security'])
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
+1
View File
@@ -5,5 +5,6 @@
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('cpp-httplib_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_test', type: 'boolean', value: false, description: 'Build tests')
+1 -1
View File
@@ -11,7 +11,7 @@ OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPEN
ifneq ($(OS), Windows_NT)
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security
endif
endif
+504 -319
View File
File diff suppressed because it is too large Load Diff