Loading src/http.cpp +79 −55 Original line number Diff line number Diff line Loading @@ -4104,8 +4104,12 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he SendData.pos = 0; // 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. // Only Cookie's grammar (RFC 6265 SS4.2.1) packs several "name=value" // pairs into one field separated by ';' -- every other header (User-Agent // above all) is one value per HPACK field, full stop. Blanket-splitting // on ';' shredded semicolon-heavy values like User-Agent into bogus // fragments that then got re-serialized as literal duplicate header // fields when forwarded upstream. See HttpResponse::parse()'s fix. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; Loading @@ -4123,6 +4127,7 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he if (!hd) hd = setHeaderData(h.name); if (!hd) continue; const std::string &val = h.value; if (h.name == "cookie") { size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { Loading @@ -4139,6 +4144,9 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } else if (!val.empty()) { hd->push_back(val); } } // Store :authority as host header too (RFC 7540 §8.1.2.3) Loading Loading @@ -4175,8 +4183,12 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he SendData.pos = 0; // 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. // Only Cookie's grammar (RFC 6265 SS4.2.1) packs several "name=value" // pairs into one field separated by ';' -- every other header (User-Agent // above all) is one value per QPACK field, full stop. Blanket-splitting // on ';' shredded semicolon-heavy values like User-Agent into bogus // fragments that then got re-serialized as literal duplicate header // fields when forwarded upstream. See HttpResponse::parse()'s fix. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; Loading @@ -4194,6 +4206,7 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he if (!hd) hd = setHeaderData(h.name); if (!hd) continue; const std::string &val = h.value; if (h.name == "cookie") { size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { Loading @@ -4210,6 +4223,9 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } else if (!val.empty()) { hd->push_back(val); } } // Store :authority as host header too (RFC 9114 §4.3.1) Loading Loading @@ -4400,7 +4416,6 @@ size_t libhttppp::HttpRequest::parseH1() { key[it] = static_cast<char>(std::tolower(static_cast<unsigned char>(key[it]))); } // store header: split value on ';' into tokens (preserving case) // Use getHeaderData first so that duplicate header lines (e.g. repeated // Cookie: lines) APPEND values instead of clobbering earlier ones -- // matches parseH2/parseH3 behaviour. Loading @@ -4408,12 +4423,20 @@ size_t libhttppp::HttpRequest::parseH1() { if (!ncontent) ncontent = setHeaderData(key); if (!ncontent) continue; // Only Cookie's grammar (RFC 6265 SS4.2.1) actually packs several // "name=value" pairs into one line separated by ';'. Every other // header -- User-Agent above all, which is full of semicolons // ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...") -- is a single // value per physical line. Blanket-splitting on ';' (as this used to // do for every header) shredded those into bogus fragments that then // got re-serialized as literal duplicate header lines when forwarded // upstream. See HttpResponse::parse()'s identical fix above. if (key == "cookie") { size_t oldiv = 0, iv = 0; while (iv < value.size()) { if (value[iv] == ';') { // substring [oldiv, iv) std::string token = value.substr(oldiv, iv - oldiv); trim_inplace(token); // remove leading/trailing spaces trim_inplace(token); if (!token.empty()) { ncontent->push_back(token); } Loading @@ -4427,13 +4450,14 @@ size_t libhttppp::HttpRequest::parseH1() { } } // last token (after last ';' or whole value if no ';') std::string token = value.substr(oldiv); trim_inplace(token); if (!token.empty()) { ncontent->push_back(token); } } else if (!value.empty()) { ncontent->push_back(value); } } } catch (netplus::NetException& e) { Loading Loading
src/http.cpp +79 −55 Original line number Diff line number Diff line Loading @@ -4104,8 +4104,12 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he SendData.pos = 0; // 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. // Only Cookie's grammar (RFC 6265 SS4.2.1) packs several "name=value" // pairs into one field separated by ';' -- every other header (User-Agent // above all) is one value per HPACK field, full stop. Blanket-splitting // on ';' shredded semicolon-heavy values like User-Agent into bogus // fragments that then got re-serialized as literal duplicate header // fields when forwarded upstream. See HttpResponse::parse()'s fix. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; Loading @@ -4123,6 +4127,7 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he if (!hd) hd = setHeaderData(h.name); if (!hd) continue; const std::string &val = h.value; if (h.name == "cookie") { size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { Loading @@ -4139,6 +4144,9 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } else if (!val.empty()) { hd->push_back(val); } } // Store :authority as host header too (RFC 7540 §8.1.2.3) Loading Loading @@ -4175,8 +4183,12 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he SendData.pos = 0; // 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. // Only Cookie's grammar (RFC 6265 SS4.2.1) packs several "name=value" // pairs into one field separated by ';' -- every other header (User-Agent // above all) is one value per QPACK field, full stop. Blanket-splitting // on ';' shredded semicolon-heavy values like User-Agent into bogus // fragments that then got re-serialized as literal duplicate header // fields when forwarded upstream. See HttpResponse::parse()'s fix. auto trim = [](std::string& s) { size_t i = 0; while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) ++i; Loading @@ -4194,6 +4206,7 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he if (!hd) hd = setHeaderData(h.name); if (!hd) continue; const std::string &val = h.value; if (h.name == "cookie") { size_t oldiv = 0, iv = 0; while (iv < val.size()) { if (val[iv] == ';') { Loading @@ -4210,6 +4223,9 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he std::string token = val.substr(oldiv); trim(token); if (!token.empty()) hd->push_back(token); } else if (!val.empty()) { hd->push_back(val); } } // Store :authority as host header too (RFC 9114 §4.3.1) Loading Loading @@ -4400,7 +4416,6 @@ size_t libhttppp::HttpRequest::parseH1() { key[it] = static_cast<char>(std::tolower(static_cast<unsigned char>(key[it]))); } // store header: split value on ';' into tokens (preserving case) // Use getHeaderData first so that duplicate header lines (e.g. repeated // Cookie: lines) APPEND values instead of clobbering earlier ones -- // matches parseH2/parseH3 behaviour. Loading @@ -4408,12 +4423,20 @@ size_t libhttppp::HttpRequest::parseH1() { if (!ncontent) ncontent = setHeaderData(key); if (!ncontent) continue; // Only Cookie's grammar (RFC 6265 SS4.2.1) actually packs several // "name=value" pairs into one line separated by ';'. Every other // header -- User-Agent above all, which is full of semicolons // ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...") -- is a single // value per physical line. Blanket-splitting on ';' (as this used to // do for every header) shredded those into bogus fragments that then // got re-serialized as literal duplicate header lines when forwarded // upstream. See HttpResponse::parse()'s identical fix above. if (key == "cookie") { size_t oldiv = 0, iv = 0; while (iv < value.size()) { if (value[iv] == ';') { // substring [oldiv, iv) std::string token = value.substr(oldiv, iv - oldiv); trim_inplace(token); // remove leading/trailing spaces trim_inplace(token); if (!token.empty()) { ncontent->push_back(token); } Loading @@ -4427,13 +4450,14 @@ size_t libhttppp::HttpRequest::parseH1() { } } // last token (after last ';' or whole value if no ';') std::string token = value.substr(oldiv); trim_inplace(token); if (!token.empty()) { ncontent->push_back(token); } } else if (!value.empty()) { ncontent->push_back(value); } } } catch (netplus::NetException& e) { Loading