Client: cpp
THttpServer::parseHeader compared a name for as many characters as the peer
had sent before the colon:
size_t sz = colon - header;
if (THRIFT_strncasecmp(header, "Content-length", sz) == 0) ...
so every name that is a prefix of one the transport knows was accepted as that
name. Measured against the unmodified library: "C: 5", "co: 7" and
"Content-len: 9" each set the content length, and "T: chunked" switched on
chunked decoding. A header name is the whole token before the colon (RFC 9110
5.1), and a transport that reads it otherwise disagrees with every other party
on the connection about where a message ends -- one of these requests carries a
five byte body here and none at all to a parser that follows the grammar.
TWebSocketServer::parseHeader is the same idiom on the same base class, and the
handshake is satisfied by "U: websocket", "C: Upgrade" and "S: <key>"; note
that "S:" reaches the Sec-WebSocket-Key arm, which is tested before the version
one. Fixed here as well, together with the "value + 1" beside it that dropped
the space after the colon by stepping over it, and read one past the terminator
for a header that has no value at all.
Content-Length went through atoi(), which cannot report that it was handed a
negative number, something that is not a number, or a value too large to hold.
"Content-length: -1" therefore arrived in the uint32_t member as 4294967295.
It is now parsed with strtoll against RFC 9110 8.6's 1*DIGIT and refused
otherwise; the client had the same line and shares the new helper, though its
name comparison was already an equality and needed nothing.
lib/cpp/README.md gains a Breaking Changes entry under 0.25.0: refusing an
abbreviated name, and refusing a malformed Content-Length, can turn away
traffic that works today.
Nine tests, written first. Six fail against the unmodified library. The two
that cover a bad number assert the length that was parsed rather than that the
read threw, because an over-declared length outruns a five byte body and throws
on the unmodified library too -- "did it throw" would have passed either way.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>