diff --git a/src/HttpParser.h b/src/HttpParser.h index cd49ec848..4da770438 100644 --- a/src/HttpParser.h +++ b/src/HttpParser.h @@ -580,8 +580,16 @@ struct HttpParser { * the Transfer-Encoding overrides the Content-Length. */ if (transferEncodingString.data() != nullptr) { - /* We only support chunked */ - if (transferEncodingString != "chunked") { + /* Transfer-coding names are case-insensitive (RFC 9112 7), so lowercase the value */ + for (unsigned int i = 0; i < transferEncodingString.length(); i++) { + char &c = ((char *) transferEncodingString.data())[i]; + if (c >= 'A' && c <= 'Z') { + c |= 32; + } + } + + /* The app can support any framing including gzip, deflate, but it always needs to end with chunked */ + if (transferEncodingString.length() < 7 || transferEncodingString.substr(transferEncodingString.length() - 7) != "chunked") { return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; } diff --git a/tests/HttpParser.cpp b/tests/HttpParser.cpp index a1b75317a..b924471e5 100644 --- a/tests/HttpParser.cpp +++ b/tests/HttpParser.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "../src/HttpParser.h" @@ -35,4 +36,39 @@ int main() { std::cout << "HTTP DONE" << std::endl; -} \ No newline at end of file + /* Issue 1941: accept Transfer-Encoding when the final coding is chunked (case-insensitive). */ + struct { + const char *te; + bool accept; + } cases[] = { + {"chunked", true}, + {"CHUNKED", true}, + {"gzip, chunked", true}, + {"gzip, CHUNKED", true}, + {"deflate, gzip, chunked", true}, + {"identity, chunked", true}, + {"gzip", false}, + {"chunked, gzip", false}, + }; + + for (auto &c : cases) { + std::string req = std::string("POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nTransfer-Encoding: ") + c.te + "\r\n\r\n0\r\n\r\n"; + unsigned int length = (unsigned int) req.size(); + req.append(32, 'E'); + + void *teUser = (void *) 13; + uWS::HttpParser teParser; + auto [teErr, teReturned] = teParser.consumePostPadded(req.data(), length, teUser, nullptr, [](void *s, uWS::HttpRequest *) -> void * { + return s; + }, [](void *s, std::string_view, bool) -> void * { + return s; + }); + + bool accepted = teReturned == teUser; + if (accepted != c.accept) { + std::cerr << "Transfer-Encoding \"" << c.te << "\" expected " << (c.accept ? "accept" : "400") + << ", got err=" << teErr << std::endl; + return 1; + } + } +}