nhttp_h1:parse_request_headers/2 and parse_request/2 only enforce max_header_size on completed header lines. When the input ends mid-line, the incomplete-tail clause returns {more, _} with no size check, so a peer that sends a header line that never terminates (no CRLF) makes the caller buffer indefinitely. Each new arrival also rescans the whole unparsed tail, so the same input is quadratic in CPU.
The chunked decoder has the same problem in two places:
- an incomplete trailer section returns {more, _} regardless of max_header_size
- the chunk-size line scan accepts arbitrarily long lines while waiting for CRLF
Reproduce
Opts = #{max_header_size => 1024},
Data = <<"GET / HTTP/1.1\r\nX-Endless: ", (binary:copy(<<"x">>, 1 bsl 20))/binary>>,
{more, _} = nhttp_h1:parse_request_headers(Data, Opts).
%% expected: {error, header_too_large}
Expected
With max_header_size set, an incomplete head should be rejected as header_too_large once the buffered input exceeds max_header_size plus a request-line allowance (8 KiB, since the request line is not counted by the per-line check). The same budget should bound incomplete chunked trailer sections, and chunk-size lines should be capped (1 KiB) and rejected as invalid_chunk_size. RFC 9112 permits rejecting lines longer than the server is willing to buffer.
nhttp_h1:parse_request_headers/2 and parse_request/2 only enforce max_header_size on completed header lines. When the input ends mid-line, the incomplete-tail clause returns {more, _} with no size check, so a peer that sends a header line that never terminates (no CRLF) makes the caller buffer indefinitely. Each new arrival also rescans the whole unparsed tail, so the same input is quadratic in CPU.
The chunked decoder has the same problem in two places:
Reproduce
Expected
With max_header_size set, an incomplete head should be rejected as header_too_large once the buffered input exceeds max_header_size plus a request-line allowance (8 KiB, since the request line is not counted by the per-line check). The same budget should bound incomplete chunked trailer sections, and chunk-size lines should be capped (1 KiB) and rejected as invalid_chunk_size. RFC 9112 permits rejecting lines longer than the server is willing to buffer.