Loading src/http.cpp +60 −4 Original line number Diff line number Diff line Loading @@ -1604,10 +1604,38 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he _h2BodyBytesSent = 0; SendData.pos = 0; // Store ALL headers (including pseudo-headers) in _firstHeaderData // Store ALL headers (including pseudo-headers) in _firstHeaderData. // Split values by ';' (matching parseH1 behaviour) so that cookie // parsing and other code that iterates individual Values works. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; if (i) s.erase(0, i); size_t e = s.size(); while (e && (s[e-1] == ' ' || s[e-1] == '\t')) --e; if (e != s.size()) s.erase(e); }; for (const auto &h : headers) { auto *hd = setHeaderData(h.name); if (hd) hd->push_back(h.value); if (!hd) continue; const std::string &val = h.value; size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { std::string token = val.substr(oldiv, iv - oldiv); trim(token); if (!token.empty()) hd->push_back(token); ++iv; while (iv < val.size() && val[iv] == ' ') ++iv; oldiv = iv; } else { ++iv; } } std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } // Store :authority as host header too (RFC 7540 §8.1.2.3) Loading Loading @@ -1638,10 +1666,38 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he _httpProtocol = 2; SendData.pos = 0; // Store ALL headers (including pseudo-headers) in _firstHeaderData // Store ALL headers (including pseudo-headers) in _firstHeaderData. // Split values by ';' (matching parseH1 behaviour) so that cookie // parsing and other code that iterates individual Values works. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; if (i) s.erase(0, i); size_t e = s.size(); while (e && (s[e-1] == ' ' || s[e-1] == '\t')) --e; if (e != s.size()) s.erase(e); }; for (const auto &h : headers) { auto *hd = setHeaderData(h.name); if (hd) hd->push_back(h.value); if (!hd) continue; const std::string &val = h.value; size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { std::string token = val.substr(oldiv, iv - oldiv); trim(token); if (!token.empty()) hd->push_back(token); ++iv; while (iv < val.size() && val[iv] == ' ') ++iv; oldiv = iv; } else { ++iv; } } std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } // Store :authority as host header too (RFC 9114 §4.3.1) Loading src/httpd.cpp +30 −5 Original line number Diff line number Diff line Loading @@ -193,10 +193,11 @@ static uint64_t h3DecodeVarInt(const uint8_t *data, size_t len, size_t &bytes_re static std::vector<uint8_t> h3BuildResponse(uint16_t status_code, const std::string &body, const std::string &content_type) { const std::string &content_type, const std::vector<libhttppp::qpack::HeaderField> &extra = {}) { std::vector<uint8_t> out; std::vector<uint8_t> headers = libhttppp::qpack::Encoder::encodeResponseHeaders( status_code, content_type, body.size()); status_code, content_type, body.size(), extra); out.push_back(0x01); uint8_t len_buf[8]; Loading Loading @@ -434,8 +435,15 @@ bool libhttppp::HttpEvent::Http2RequestEvent(netplus::con &curcon, if (key == ":res-valid" || key == ":res-status" || key == ":res-content-type" || key == ":res-content-length") continue; std::string realKey = key.substr(5); if (hd->getfirstValue()) extra.push_back({realKey, hd->getfirstValue()->getvalue()}); // Join all Values with "; " to reconstruct the full // header value (e.g. set-cookie attributes). std::string joined; for (auto *v = hd->getfirstValue(); v; v = v->nextvalue()) { if (!joined.empty()) joined += "; "; joined += v->getvalue(); } if (!joined.empty()) extra.push_back({realKey, joined}); } // Build HPACK-encoded HEADERS frame Loading Loading @@ -604,9 +612,26 @@ void libhttppp::HttpEvent::Http3StreamEvent(netplus::socket *sock, } } // Collect extra :res-* headers (e.g. set-cookie) std::vector<libhttppp::qpack::HeaderField> extra; for (auto *hd = tempreq.getfirstHeaderData(); hd; hd = hd->nextHeaderData()) { const std::string &ekey = hd->getkey(); if (ekey.substr(0, 5) != ":res-") continue; if (ekey == ":res-valid" || ekey == ":res-status" || ekey == ":res-content-type" || ekey == ":res-content-length") continue; std::string realKey = ekey.substr(5); std::string joined; for (auto *v = hd->getfirstValue(); v; v = v->nextvalue()) { if (!joined.empty()) joined += "; "; joined += v->getvalue(); } if (!joined.empty()) extra.push_back({realKey, joined}); } std::vector<uint8_t> response = h3BuildResponse( status_code, body, content_type.empty() ? "text/html" : content_type); content_type.empty() ? "text/html" : content_type, extra); q->sendStreamData(stream_id, response, true); // Clean up :res-* pseudo-headers Loading Loading
src/http.cpp +60 −4 Original line number Diff line number Diff line Loading @@ -1604,10 +1604,38 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he _h2BodyBytesSent = 0; SendData.pos = 0; // Store ALL headers (including pseudo-headers) in _firstHeaderData // Store ALL headers (including pseudo-headers) in _firstHeaderData. // Split values by ';' (matching parseH1 behaviour) so that cookie // parsing and other code that iterates individual Values works. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; if (i) s.erase(0, i); size_t e = s.size(); while (e && (s[e-1] == ' ' || s[e-1] == '\t')) --e; if (e != s.size()) s.erase(e); }; for (const auto &h : headers) { auto *hd = setHeaderData(h.name); if (hd) hd->push_back(h.value); if (!hd) continue; const std::string &val = h.value; size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { std::string token = val.substr(oldiv, iv - oldiv); trim(token); if (!token.empty()) hd->push_back(token); ++iv; while (iv < val.size() && val[iv] == ' ') ++iv; oldiv = iv; } else { ++iv; } } std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } // Store :authority as host header too (RFC 7540 §8.1.2.3) Loading Loading @@ -1638,10 +1666,38 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he _httpProtocol = 2; SendData.pos = 0; // Store ALL headers (including pseudo-headers) in _firstHeaderData // Store ALL headers (including pseudo-headers) in _firstHeaderData. // Split values by ';' (matching parseH1 behaviour) so that cookie // parsing and other code that iterates individual Values works. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; if (i) s.erase(0, i); size_t e = s.size(); while (e && (s[e-1] == ' ' || s[e-1] == '\t')) --e; if (e != s.size()) s.erase(e); }; for (const auto &h : headers) { auto *hd = setHeaderData(h.name); if (hd) hd->push_back(h.value); if (!hd) continue; const std::string &val = h.value; size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { std::string token = val.substr(oldiv, iv - oldiv); trim(token); if (!token.empty()) hd->push_back(token); ++iv; while (iv < val.size() && val[iv] == ' ') ++iv; oldiv = iv; } else { ++iv; } } std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } // Store :authority as host header too (RFC 9114 §4.3.1) Loading
src/httpd.cpp +30 −5 Original line number Diff line number Diff line Loading @@ -193,10 +193,11 @@ static uint64_t h3DecodeVarInt(const uint8_t *data, size_t len, size_t &bytes_re static std::vector<uint8_t> h3BuildResponse(uint16_t status_code, const std::string &body, const std::string &content_type) { const std::string &content_type, const std::vector<libhttppp::qpack::HeaderField> &extra = {}) { std::vector<uint8_t> out; std::vector<uint8_t> headers = libhttppp::qpack::Encoder::encodeResponseHeaders( status_code, content_type, body.size()); status_code, content_type, body.size(), extra); out.push_back(0x01); uint8_t len_buf[8]; Loading Loading @@ -434,8 +435,15 @@ bool libhttppp::HttpEvent::Http2RequestEvent(netplus::con &curcon, if (key == ":res-valid" || key == ":res-status" || key == ":res-content-type" || key == ":res-content-length") continue; std::string realKey = key.substr(5); if (hd->getfirstValue()) extra.push_back({realKey, hd->getfirstValue()->getvalue()}); // Join all Values with "; " to reconstruct the full // header value (e.g. set-cookie attributes). std::string joined; for (auto *v = hd->getfirstValue(); v; v = v->nextvalue()) { if (!joined.empty()) joined += "; "; joined += v->getvalue(); } if (!joined.empty()) extra.push_back({realKey, joined}); } // Build HPACK-encoded HEADERS frame Loading Loading @@ -604,9 +612,26 @@ void libhttppp::HttpEvent::Http3StreamEvent(netplus::socket *sock, } } // Collect extra :res-* headers (e.g. set-cookie) std::vector<libhttppp::qpack::HeaderField> extra; for (auto *hd = tempreq.getfirstHeaderData(); hd; hd = hd->nextHeaderData()) { const std::string &ekey = hd->getkey(); if (ekey.substr(0, 5) != ":res-") continue; if (ekey == ":res-valid" || ekey == ":res-status" || ekey == ":res-content-type" || ekey == ":res-content-length") continue; std::string realKey = ekey.substr(5); std::string joined; for (auto *v = hd->getfirstValue(); v; v = v->nextvalue()) { if (!joined.empty()) joined += "; "; joined += v->getvalue(); } if (!joined.empty()) extra.push_back({realKey, joined}); } std::vector<uint8_t> response = h3BuildResponse( status_code, body, content_type.empty() ? "text/html" : content_type); content_type.empty() ? "text/html" : content_type, extra); q->sendStreamData(stream_id, response, true); // Clean up :res-* pseudo-headers Loading