From b18a6e8456ceb3d2c2e5ab659bd24a5fe8144a52 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 3 Feb 2025 11:42:03 +0100 Subject: [PATCH] Add client stream handler Introduce a new request callback, stream_handler. If set, it replaces all other content-serving mechanisms. The handler is not called in response to HEAD or CONNECT requests, or when following a redirect. Content-related default header fields are not added to the request. In conjunction with a response handler, it provides a minimal interface to implement a WebSocket client. --- httplib.h | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/httplib.h b/httplib.h index f494187..3021786 100644 --- a/httplib.h +++ b/httplib.h @@ -646,6 +646,7 @@ struct Request { // for client ResponseHandler response_handler; + StreamHandler stream_handler; // EXPERIMENTAL function signature may change ContentReceiverWithProgress content_receiver; Progress progress; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT @@ -1180,6 +1181,7 @@ enum class Error { Compression, ConnectionTimeout, ProxyConnection, + StreamHandler, // For internal use only SSLPeerCouldBeClosed_, @@ -2270,6 +2272,7 @@ inline std::string to_string(const Error error) { case Error::Compression: return "Compression failed"; case Error::ConnectionTimeout: return "Connection timed out"; case Error::ProxyConnection: return "Proxy connection failed"; + case Error::StreamHandler: return "Stream handler failed"; case Error::Unknown: return "Unknown"; default: break; } @@ -7825,10 +7828,12 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req, } } - if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } + if (!req.stream_handler && !req.has_header("Accept")) { + req.set_header("Accept", "*/*"); + } if (!req.content_receiver) { - if (!req.has_header("Accept-Encoding")) { + if (!req.stream_handler && !req.has_header("Accept-Encoding")) { std::string accept_encoding; #ifdef CPPHTTPLIB_BROTLI_SUPPORT accept_encoding = "br"; @@ -7846,7 +7851,7 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req, req.set_header("User-Agent", agent); } #endif - }; + } if (req.body.empty()) { if (req.content_provider_) { @@ -8078,10 +8083,23 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req, res.status != StatusCode::NotModified_304 && follow_location_; - if (req.response_handler && !redirect) { - if (!req.response_handler(res)) { - error = Error::Canceled; - return false; + if (!redirect) { + if (req.response_handler) { + if (!req.response_handler(res)) { + error = Error::Canceled; + return false; + } + } + + if (req.stream_handler) { + // Log early + if (logger_) { logger_(req, res); } + + if (!req.stream_handler(strm)) { + error = Error::StreamHandler; + return false; + } + return true; } }