Skip to content

Reject malformed HTTP/3 requests with a stream error - #6

Open
xl32 wants to merge 1 commit into
machine-moon:trunkfrom
xl32:rfc9114-malformed-requests
Open

Reject malformed HTTP/3 requests with a stream error#6
xl32 wants to merge 1 commit into
machine-moon:trunkfrom
xl32:rfc9114-malformed-requests

Conversation

@xl32

@xl32 xl32 commented Aug 6, 2026

Copy link
Copy Markdown

RFC 9114 4.1.2 requires a malformed request to be treated as a stream error of type H3_MESSAGE_ERROR. nghttp3 already detects every case (missing, duplicate or invalid pseudo-header fields, connection-specific fields, TE other than trailers, content-length mismatch) and reports it as a non-fatal NGHTTP3_ERR_MALFORMED_HTTP_HEADER/_MESSAGING out of nghttp3_conn_read_stream, but the module treated any negative return as fatal and closed the whole QUIC connection, ending every other request in flight on it.

Translate exactly those two errors into a per-stream rejection instead: reset the request stream with the inferred H3_MESSAGE_ERROR code, stop reading it, drop it from nghttp3, and keep the connection serving. All other nghttp3 errors keep the existing connection-error path, as RFC 9114 demands for frame-layer violations.

The new checks run only on the error path; the success path is unchanged except for a done-flag test that also stops rejected or already-reset streams from being dispatched to a worker.

Tested by test/http3/test_020_malformed.py with a raw QPACK client: each malformed request must yield a stream reset with 0x010E and the same connection must then serve a well-formed request.

RFC 9114 4.1.2 requires a malformed request to be treated as a stream
error of type H3_MESSAGE_ERROR. nghttp3 already detects every case
(missing, duplicate or invalid pseudo-header fields, connection-specific
fields, TE other than trailers, content-length mismatch) and reports it
as a non-fatal NGHTTP3_ERR_MALFORMED_HTTP_HEADER/_MESSAGING out of
nghttp3_conn_read_stream, but the module treated any negative return as
fatal and closed the whole QUIC connection, ending every other request
in flight on it.

Translate exactly those two errors into a per-stream rejection instead:
reset the request stream with the inferred H3_MESSAGE_ERROR code, stop
reading it, drop it from nghttp3, and keep the connection serving. All
other nghttp3 errors keep the existing connection-error path, as RFC
9114 demands for frame-layer violations.

The new checks run only on the error path; the success path is
unchanged except for a done-flag test that also stops rejected or
already-reset streams from being dispatched to a worker.

Tested by test/http3/test_020_malformed.py with a raw QPACK client:
each malformed request must yield a stream reset with 0x010E and the
same connection must then serve a well-formed request.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant