@@ -3705,6 +3705,12 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
37053705 // Trailer
37063706 if (trailer) {
37073707 for (const auto &kv : *trailer) {
3708+ // Skip fields with invalid names or values to prevent response
3709+ // splitting via CR/LF injection, matching set_header().
3710+ if (!fields::is_field_name(kv.first) ||
3711+ !fields::is_field_value(kv.second)) {
3712+ continue;
3713+ }
37083714 std::string field_line = kv.first + ": " + kv.second + "\r\n";
37093715 if (!write_data(strm, field_line.data(), field_line.size())) {
37103716 ok = false;
@@ -8301,8 +8307,8 @@ void Server::apply_ranges(const Request &req, Response &res,
83018307 }
83028308 }
83038309
8304- auto length = std::to_string( res.body.size() );
8305- res.set_header("Content-Length", length );
8310+ res.content_length_ = res.body.size();
8311+ res.set_header("Content-Length", std::to_string(res.content_length_) );
83068312 }
83078313}
83088314
@@ -10270,6 +10276,11 @@ Result ClientImpl::Get(const std::string &path,
1027010276 return Get(path, Headers(), std::move(progress));
1027110277}
1027210278
10279+ Result ClientImpl::Get(const std::string &path, const Params ¶ms,
10280+ DownloadProgress progress) {
10281+ return Get(path, params, Headers(), std::move(progress));
10282+ }
10283+
1027310284Result ClientImpl::Get(const std::string &path, const Params ¶ms,
1027410285 const Headers &headers,
1027510286 DownloadProgress progress) {
@@ -11348,6 +11359,10 @@ Result Client::Get(const std::string &path, const Headers &headers,
1134811359 return cli_->Get(path, headers, std::move(response_handler),
1134911360 std::move(content_receiver), std::move(progress));
1135011361}
11362+ Result Client::Get(const std::string &path, const Params ¶ms,
11363+ DownloadProgress progress) {
11364+ return cli_->Get(path, params, std::move(progress));
11365+ }
1135111366Result Client::Get(const std::string &path, const Params ¶ms,
1135211367 const Headers &headers, DownloadProgress progress) {
1135311368 return cli_->Get(path, params, headers, std::move(progress));
@@ -12076,11 +12091,18 @@ bool SSLServer::update_certs_pem(const char *cert_pem,
1207612091
1207712092// SSL HTTP client implementation
1207812093SSLClient::~SSLClient() {
12079- if (ctx_) { tls::free_context(ctx_); }
1208012094 // Make sure to shut down SSL since shutdown_ssl will resolve to the
1208112095 // base function rather than the derived function once we get to the
1208212096 // base class destructor, and won't free the SSL (causing a leak).
12097+ // This must happen before the context is freed below: some backends
12098+ // (e.g. mbedTLS) have the SSL session borrow a raw pointer into the
12099+ // context, so freeing the context first leaves close_notify reading
12100+ // freed memory.
1208312101 shutdown_ssl_impl(socket_, true);
12102+ if (ctx_) {
12103+ tls::free_context(ctx_);
12104+ ctx_ = nullptr;
12105+ }
1208412106}
1208512107
1208612108bool SSLClient::is_valid() const { return ctx_ != nullptr; }
@@ -16501,6 +16523,11 @@ WebSocketClient::~WebSocketClient() {
1650116523bool WebSocketClient::is_valid() const { return is_valid_; }
1650216524
1650316525void WebSocketClient::shutdown_and_close() {
16526+ // Send the close frame while the TLS session is still alive: ws_ holds an
16527+ // SSLSocketStream that keeps a raw pointer to tls_session_, so the session
16528+ // must outlive ws_->close() and ws_.reset() to avoid a use-after-free.
16529+ if (ws_ && ws_->is_open()) { ws_->close(); }
16530+ ws_.reset();
1650416531#ifdef CPPHTTPLIB_SSL_ENABLED
1650516532 if (is_ssl_) {
1650616533 if (tls_session_) {
@@ -16510,8 +16537,6 @@ void WebSocketClient::shutdown_and_close() {
1651016537 }
1651116538 }
1651216539#endif
16513- if (ws_ && ws_->is_open()) { ws_->close(); }
16514- ws_.reset();
1651516540 if (sock_ != INVALID_SOCKET) {
1651616541 detail::shutdown_socket(sock_);
1651716542 detail::close_socket(sock_);
0 commit comments