Loading src/http.cpp +67 −7 Changes for src/http.cpp: 67 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -867,8 +867,12 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest bool got_headers = false; _streamH3Sid = stream_id; // pumpNetwork() returns normally (never throws) when a non-blocking // read simply found no datagrams, so looping a fixed N times used // to always burn N non-blocking recv syscalls on every idle wait. // Stop as soon as our stream actually has something to show for it. auto h3_pump = [&]() { for (int pump_i = 0; pump_i < 16; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -876,6 +880,8 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest throw; break; } if (q->hasStreamData(stream_id) || q->isStreamComplete(stream_id)) break; } }; Loading Loading @@ -1793,9 +1799,12 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { return 0; } // Pump network and read stream data // Pump network and read stream data. pumpNetwork() returns normally // (never throws) on an empty non-blocking read, so a fixed 256-iteration // loop used to always cost 256 non-blocking recv syscalls per call -- // stop as soon as our stream actually has something to show for it. auto h3_pump = [&]() { for (int pump_i = 0; pump_i < 256; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -1803,6 +1812,8 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { throw; break; } if (q->hasStreamData(_streamH3Sid) || q->isStreamComplete(_streamH3Sid)) break; } }; Loading Loading @@ -2017,8 +2028,11 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize return static_cast<size_t>(-1); } // Non-blocking pump and drain all available stream data for (int pump_i = 0; pump_i < 256; ++pump_i) { // Non-blocking pump and drain all available stream data. pumpNetwork() // returns normally (never throws) on an empty non-blocking read, so a // fixed 256-iteration loop used to always cost 256 non-blocking recv // syscalls per call regardless of whether anything arrived. for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -2026,6 +2040,8 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize throw; break; } if (q->hasStreamData(_streamH3Sid) || q->isStreamComplete(_streamH3Sid)) break; } { Loading Loading @@ -2840,10 +2856,14 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( return got_any; }; // Helper: pump QUIC network layer (process incoming UDP datagrams) // Helper: pump QUIC network layer (process incoming UDP datagrams). // pumpNetwork() returns normally (never throws) on an empty non-blocking // read, so a fixed 256-iteration loop used to always cost 256 non-blocking // recv syscalls per call regardless of whether anything arrived. Stop as // soon as our stream actually has something to show for it. auto pumpQuic = [&]() { bool pumped_any = false; for (int pump_i = 0; pump_i < 256; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); pumped_any = true; Loading @@ -2855,6 +2875,8 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( } break; } if (q->hasStreamData(stream_id) || q->isStreamComplete(stream_id)) break; } return pumped_any; }; Loading Loading @@ -3893,6 +3915,44 @@ size_t libhttppp::HttpResponse::parse(const char *data, size_t inlen) { return bytes_consumed; } // Shared by parseH2/parseH3 below: HPACK/QPACK already split each field into // a clean name/value pair, so ingestion here is a straight copy into the // header list -- no CRLF/colon scanning, and (unlike the old synthetic-text // approach) no way for a value to be misinterpreted as introducing new // header lines. template <typename HeaderFieldT> static size_t ingestDecodedHeaders(libhttppp::HttpResponse &resp, const std::vector<HeaderFieldT> &headers, const std::string &httpVersion) { int statusCode = 0; for (const auto &h : headers) { if (h.name == ":status") { try { statusCode = std::stoi(h.value); } catch (...) { statusCode = 0; } break; } } resp.setResponseLine(httpVersion, statusCode); for (const auto &h : headers) { if (h.name.empty() || h.name[0] == ':') continue; std::string key = h.name; for (char &c : key) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c))); resp.setHeaderData(key)->push_back(h.value); } resp.refreshCachedHeaders(); return headers.size(); } size_t libhttppp::HttpResponse::parseH2(const std::vector<hpack::HeaderField> &headers) { return ingestDecodedHeaders(*this, headers, "HTTP/2"); } size_t libhttppp::HttpResponse::parseH3(const std::vector<qpack::HeaderField> &headers) { return ingestDecodedHeaders(*this, headers, "HTTP/3"); } void libhttppp::HttpResponse::setTransferEncoding(const std ::string &enc){ _TransferEncoding=setHeaderData("transfer-encoding"); _TransferEncoding->push_back(enc); Loading src/http.h +10 −0 Changes for src/http.h: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -358,6 +358,16 @@ namespace libhttppp { /*client method*/ size_t parse(const char *in,size_t inlen); // Ingest already-decoded HPACK/QPACK header fields directly (client // side, HTTP/2 and HTTP/3). Avoids building a synthetic HTTP/1.1-style // text block and reparsing it -- besides the redundant copy/reparse, // that round-trip had no way to reject a header value containing an // embedded "\r\n", letting a malicious/compromised peer inject extra // header lines (including a premature blank line truncating the block). // Returns the number of header fields ingested. size_t parseH2(const std::vector<hpack::HeaderField> &headers); size_t parseH3(const std::vector<qpack::HeaderField> &headers); private: bool _storeResponseInfo(netplus::con &curconnection, int datalen); Loading Loading
src/http.cpp +67 −7 Changes for src/http.cpp: 67 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -867,8 +867,12 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest bool got_headers = false; _streamH3Sid = stream_id; // pumpNetwork() returns normally (never throws) when a non-blocking // read simply found no datagrams, so looping a fixed N times used // to always burn N non-blocking recv syscalls on every idle wait. // Stop as soon as our stream actually has something to show for it. auto h3_pump = [&]() { for (int pump_i = 0; pump_i < 16; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -876,6 +880,8 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest throw; break; } if (q->hasStreamData(stream_id) || q->isStreamComplete(stream_id)) break; } }; Loading Loading @@ -1793,9 +1799,12 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { return 0; } // Pump network and read stream data // Pump network and read stream data. pumpNetwork() returns normally // (never throws) on an empty non-blocking read, so a fixed 256-iteration // loop used to always cost 256 non-blocking recv syscalls per call -- // stop as soon as our stream actually has something to show for it. auto h3_pump = [&]() { for (int pump_i = 0; pump_i < 256; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -1803,6 +1812,8 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { throw; break; } if (q->hasStreamData(_streamH3Sid) || q->isStreamComplete(_streamH3Sid)) break; } }; Loading Loading @@ -2017,8 +2028,11 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize return static_cast<size_t>(-1); } // Non-blocking pump and drain all available stream data for (int pump_i = 0; pump_i < 256; ++pump_i) { // Non-blocking pump and drain all available stream data. pumpNetwork() // returns normally (never throws) on an empty non-blocking read, so a // fixed 256-iteration loop used to always cost 256 non-blocking recv // syscalls per call regardless of whether anything arrived. for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); } catch (netplus::NetException &e) { Loading @@ -2026,6 +2040,8 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize throw; break; } if (q->hasStreamData(_streamH3Sid) || q->isStreamComplete(_streamH3Sid)) break; } { Loading Loading @@ -2840,10 +2856,14 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( return got_any; }; // Helper: pump QUIC network layer (process incoming UDP datagrams) // Helper: pump QUIC network layer (process incoming UDP datagrams). // pumpNetwork() returns normally (never throws) on an empty non-blocking // read, so a fixed 256-iteration loop used to always cost 256 non-blocking // recv syscalls per call regardless of whether anything arrived. Stop as // soon as our stream actually has something to show for it. auto pumpQuic = [&]() { bool pumped_any = false; for (int pump_i = 0; pump_i < 256; ++pump_i) { for (int pump_i = 0; pump_i < 4; ++pump_i) { try { q->pumpNetwork(MSG_DONTWAIT); pumped_any = true; Loading @@ -2855,6 +2875,8 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( } break; } if (q->hasStreamData(stream_id) || q->isStreamComplete(stream_id)) break; } return pumped_any; }; Loading Loading @@ -3893,6 +3915,44 @@ size_t libhttppp::HttpResponse::parse(const char *data, size_t inlen) { return bytes_consumed; } // Shared by parseH2/parseH3 below: HPACK/QPACK already split each field into // a clean name/value pair, so ingestion here is a straight copy into the // header list -- no CRLF/colon scanning, and (unlike the old synthetic-text // approach) no way for a value to be misinterpreted as introducing new // header lines. template <typename HeaderFieldT> static size_t ingestDecodedHeaders(libhttppp::HttpResponse &resp, const std::vector<HeaderFieldT> &headers, const std::string &httpVersion) { int statusCode = 0; for (const auto &h : headers) { if (h.name == ":status") { try { statusCode = std::stoi(h.value); } catch (...) { statusCode = 0; } break; } } resp.setResponseLine(httpVersion, statusCode); for (const auto &h : headers) { if (h.name.empty() || h.name[0] == ':') continue; std::string key = h.name; for (char &c : key) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c))); resp.setHeaderData(key)->push_back(h.value); } resp.refreshCachedHeaders(); return headers.size(); } size_t libhttppp::HttpResponse::parseH2(const std::vector<hpack::HeaderField> &headers) { return ingestDecodedHeaders(*this, headers, "HTTP/2"); } size_t libhttppp::HttpResponse::parseH3(const std::vector<qpack::HeaderField> &headers) { return ingestDecodedHeaders(*this, headers, "HTTP/3"); } void libhttppp::HttpResponse::setTransferEncoding(const std ::string &enc){ _TransferEncoding=setHeaderData("transfer-encoding"); _TransferEncoding->push_back(enc); Loading
src/http.h +10 −0 Changes for src/http.h: 10 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -358,6 +358,16 @@ namespace libhttppp { /*client method*/ size_t parse(const char *in,size_t inlen); // Ingest already-decoded HPACK/QPACK header fields directly (client // side, HTTP/2 and HTTP/3). Avoids building a synthetic HTTP/1.1-style // text block and reparsing it -- besides the redundant copy/reparse, // that round-trip had no way to reject a header value containing an // embedded "\r\n", letting a malicious/compromised peer inject extra // header lines (including a premature blank line truncating the block). // Returns the number of header fields ingested. size_t parseH2(const std::vector<hpack::HeaderField> &headers); size_t parseH3(const std::vector<qpack::HeaderField> &headers); private: bool _storeResponseInfo(netplus::con &curconnection, int datalen); Loading