Add fuzz properties for HTTP/1 and HTTP/2 connections - #520
Merged
Merged
Conversation
Coverage Report for CI Build 4Coverage increased (+0.2%) to 90.374%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
ericmj
marked this pull request as ready for review
September 27, 2026 18:50
The property pipelines one to three requests and feeds stream/2 randomly generated responses (interim responses, HTTP/1.0 and HTTP/1.1, valid and invalid status lines and headers, content-length, chunked with extensions and trailers, and close-delimited bodies, with optional byte mutations) in random chunk sizes, then checks that stream/2 never raises, that every response belongs to a known request in the order status, headers, data, trailers, done, that nothing follows done or an error, and that a closed connection has no open requests left. FUZZ_RUNS sets the number of scenarios; the default is 100.
…_headers Scenarios now pick active or passive mode, feeding bytes through recv/3 over the real socket in passive mode, randomly enable stream_headers, stream request bodies with stream_request_body/3 and open new requests while responses arrive. The harness accepts its own socket so listen sockets are closed after each scenario.
Scenarios now vary max_header_list_size, case_sensitive_headers and optional_responses, include CONNECT requests, and check that a completed response has exactly the body its content-length announced, that HEAD, 204, 304 and CONNECT 2xx responses have no body, and that open_request_count/1 matches the number of unfinished requests.
The property opens one to three requests against the test server and feeds stream/2 a random sequence of server frames (HEADERS with valid and invalid header lists, CONTINUATION, padding, DATA, PUSH_PROMISE with valid and invalid stream IDs, RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE, PRIORITY, unknown frame types and raw bytes) in random chunk sizes, interleaved with cancel_request/2, stream_request_body/3 and ping/2 calls. It checks that stream/2 never raises, that every response belongs to a known request in the order status, headers, data, trailers, done, that nothing follows done, an error or a cancel, that the connection is not writable after an error, and that the stream counters and the ref-to-stream index match the stream map. FUZZ_RUNS sets the number of scenarios; the default is 10.
…and passive mode Server actions are now encoded as the scenario runs, so client actions can open requests, change client settings and stream request bodies between server frames, and HEADERS frames can carry random bytes as their header block. Scenarios also pick active or passive mode, feeding bytes over the real socket with recv/3 in passive mode.
Scenarios now vary the request method, the client settings, the connection window and its refill threshold, and the server's handshake settings. The server keeps its own view of the connection window from the DATA payloads it sent and the WINDOW_UPDATE frames it received, and the property checks it against Mint's window after every segment, that increments are within 1..2^31-1, that a completed response's body size matches a valid content-length, that HEAD, 204 and 304 responses have no body, and that open_request_count/1 matches the unfinished requests.
ericmj
force-pushed
the
http-fuzz-properties
branch
from
September 27, 2026 20:00
ea3de22 to
4f337e5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #519, which includes #518. Their commits are included here until they merge, and the HTTP/2 property fails without them. The six fuzz commits are meant to be rebase-merged rather than squashed.
Both properties use StreamData to generate whole connection scenarios. The server's bytes are fed in random chunk sizes through
stream/2(active mode) orrecv/3over the real socket (passive mode), interleaved with client calls.test/mint/http1/fuzz_test.exspipelines one to three requests against random responses. The responses include interim responses, HTTP/1.0, invalid status lines and headers, content-length, chunked with extensions and trailers, close-delimited bodies, and byte mutations.stream_headers,max_header_list_size,case_sensitive_headers,optional_responsesand CONNECT requests.test/mint/http2/fuzz_test.exssends random server frames: every frame type, padding, CONTINUATION, valid and invalid header lists, PUSH_PROMISE with valid and invalid stream IDs, and raw bytes.The properties check that nothing raises and that every response belongs to a known request in the order status, headers, data, trailers, done. They also check the following:
open_request_count/1and the HTTP/2 stream counters match the unfinished requests.FUZZ_RUNSsets the number of scenarios. The defaults are 100 for HTTP/1 (about 0.3 s) and 10 for HTTP/2 (about 3 s).Run against v1.10.1, every one of 20 seeds × 300 runs fails for both properties. The failure classes are:
stream_request_body/3raisingFunctionClauseError;:donefor a request that had no status yet.{:stream_not_found, id}for PUSH_PROMISE on a closed stream;{:done, ref}from RST_STREAM NO_ERROR before any headers.With the current fixes, both pass 2,000 runs on several seeds.