diff --git a/include/boost/beast/http/basic_parser.hpp b/include/boost/beast/http/basic_parser.hpp index 3c02e45fa5..f9a4490a33 100644 --- a/include/boost/beast/http/basic_parser.hpp +++ b/include/boost/beast/http/basic_parser.hpp @@ -110,6 +110,7 @@ class basic_parser static unsigned constexpr flagContentLength = 1<< 10; static unsigned constexpr flagChunked = 1<< 11; static unsigned constexpr flagUpgrade = 1<< 12; + static unsigned constexpr flagTransferEncoding = 1<< 13; static constexpr std::uint64_t diff --git a/include/boost/beast/http/impl/basic_parser.ipp b/include/boost/beast/http/impl/basic_parser.ipp index 524ac12c87..b5449eed49 100644 --- a/include/boost/beast/http/impl/basic_parser.ipp +++ b/include/boost/beast/http/impl/basic_parser.ipp @@ -753,8 +753,11 @@ do_field(field f, BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length); }; - // conflicting field - if(f_ & flagChunked) + // conflicting field: a Transfer-Encoding was already seen, and a + // message must not carry both Transfer-Encoding and Content-Length + // regardless of the transfer coding or the order the two fields + // arrive in (RFC 7230 3.3.3). + if(f_ & (flagChunked | flagTransferEncoding)) return bad_content_length(); // Content-length may be a comma-separated list of integers @@ -811,6 +814,11 @@ do_field(field f, return; } + // Record that a Transfer-Encoding was present so a Content-Length + // arriving afterwards is rejected the same way it would be if the + // fields arrived in the opposite order. + f_ |= flagTransferEncoding; + ec = {}; auto const v = token_list{value}; auto const p = std::find_if(v.begin(), v.end(), diff --git a/test/beast/http/basic_parser.cpp b/test/beast/http/basic_parser.cpp index 0063e66768..17ed3e65e1 100644 --- a/test/beast/http/basic_parser.cpp +++ b/test/beast/http/basic_parser.cpp @@ -797,6 +797,19 @@ class basic_parser_test : public beast::unit_test::suite "GET / HTTP/1.0\r\n" "Transfer-Encoding: gzip, chunked\r\n" "\r\n0\r\n\r\n", error::bad_transfer_encoding); + + // a message must not carry both Transfer-Encoding and Content-Length, + // regardless of the transfer coding or the order the fields arrive in + failgrind>( + "POST / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "Transfer-Encoding: gzip\r\n" + "\r\n", error::bad_transfer_encoding); + failgrind>( + "POST / HTTP/1.1\r\n" + "Transfer-Encoding: gzip\r\n" + "Content-Length: 5\r\n" + "\r\n", error::bad_content_length); } void