Skip to content

Fix fuzz property gaps and a flaky HTTP/2 test - #523

Open
ericmj wants to merge 6 commits into
mainfrom
test-followups
Open

ericmj wants to merge 6 commits into
mainfrom
test-followups

Conversation

@ericmj

@ericmj ericmj commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Fixes to the fuzz properties from #520 and a flaky HTTP/2 test, one commit each, meant to be rebase-merged rather than squashed. No library changes.

  • The HTTP/2 1xx test picked a random interim status that could be 101, which Mint rejects in HTTP/2 (fails with --seed 338383).
  • The HTTP/2 model treats any 2xx response to CONNECT as a tunnel (RFC 9110 9.3.6, RFC 9113 8.5) instead of requiring 204 to have no body.
  • Requests scheduled mid-scenario in the HTTP/1 property never ran, because the comprehension matched four-element tuples and request ops have three elements.
  • The HTTP/2 test server closes its listen socket after accepting. With FUZZ_RUNS=200 the property was left with 201 open ports.
  • The HTTP/2 model derives the connection window from the WINDOW_UPDATE frames the client sends, starting from 65,535, instead of copying Mint's own window. Dropping Mint's initial WINDOW_UPDATE now fails the property.
  • The HTTP/1 property generates gzip, chunked and chunked, gzip, and checks the body size of completed responses to the initial requests against the length their framing gives. Reverting to the first transfer coding now fails the property.

The default mix test time is unchanged within run-to-run noise.

The "multiple before a single HEADERS" test picked each interim status
from 100..199 and expected Mint to accept it. Mint rejects 101 in HTTP/2
since RFC 9113 section 8.6 doesn't support the Switching Protocols
status, so the test failed for seeds that drew 101 (for example
--seed 338383). The statuses are now drawn from 100..199 without 101.
The fuzz property required zero body bytes for every 204 response. A 2xx
response to CONNECT establishes a tunnel and the DATA frames after it are
tunnel data (RFC 9110 section 9.3.6, RFC 9113 section 8.5), so the model
now follows the RFC and exempts CONNECT 2xx responses from both the
bodiless and the content-length checks. Mint answers a CONNECT 204
followed by DATA with a stream error, which the property still accepts.
Client operations are {index, :body, ref_index, chunk} or
{index, :request, {method, body}}, but the comprehension that picks the
operations for the current chunk only matched four-element tuples, so
request operations were dropped and no request was ever issued in the
middle of a scenario. Operations are now matched on their index whatever
their size.
TestServer.listen_and_accept/0 opened a TLS listen socket that nothing
closed, so every HTTP/2 fuzz scenario left a listener behind: 200
scenarios grew the tcp_inet ports from 1 to 201 and the processes from
125 to 527. The accepting task now closes the listen socket once it has
accepted the single connection it serves, and the same 200 scenarios end
with 1 port and 127 processes.
The model seeded the server's view of the connection window from Mint's
own receive_window_remaining, so a client that kept a 16,777,216-byte
window internally without sending the WINDOW_UPDATE that announces it
went unnoticed. The view now starts at the 65,535-byte initial window
(RFC 9113 section 6.9.2) and only grows by the stream 0 WINDOW_UPDATE
frames the test server decodes from what Mint writes, starting with the
one that may follow the client preface. The preface is still required to
be a SETTINGS frame followed by at most that WINDOW_UPDATE, and anything
else fails the scenario before it runs.
@coveralls

coveralls commented Sep 28, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 5

Coverage decreased (-0.1%) to 90.406%

Details

  • Coverage decreased (-0.1%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 2 coverage regressions across 1 file.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

2 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
lib/mint/http2.ex 2 95.28%

Coverage Stats

Coverage Status
Relevant Lines: 1970
Covered Lines: 1781
Line Coverage: 90.41%
Coverage Strength: 714.4 hits per line

馃挍 - Coveralls

Responses with more than one transfer coding could come up, as a
generated Transfer-Encoding: chunked plus a random Transfer-Encoding:
gzip header, but no invariant compared the body size Mint delivered with
what the framing gives, so treating the first coding as the framing one
went unnoticed. Chunked bodies are now also sent with "gzip, chunked",
which RFC 9112 section 6.3 frames as chunked, and with "chunked, gzip",
whose body is read until the connection closes and includes the chunk
framing.

Each generated framing carries the body length it gives, and the model
checks the body size of completed responses to the initial requests.
That holds when the bytes are unmutated, every initial request was
issued, and the response and the ones before it are 1.1 responses to
GET or POST with a 200, 404 or 500 status, no 101, and no extra framing
headers. Responses to requests issued mid-scenario aren't checked. A
response read until close takes the rest of the bytes, so no response
after it has a known size.
@ericmj
ericmj marked this pull request as ready for review September 28, 2026 16:23
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.

2 participants