Skip to content

Commit e81bd82

Browse files
mathieu17gclaude
andcommitted
Run the h2 retry tests in CI and cover non-retryable reset codes
The new test file was not listed in test/runtests.jl, so CI never executed it (codecov flagged the fix lines as uncovered). Also cover the codes without an unprocessed guarantee: a CANCEL reset surfaces H2StreamResetError on first occurrence without retry, and showerror names the RFC 9113 section 7 codes (REFUSED_STREAM, ENHANCE_YOUR_CALM, hex fallback for unknown codes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d9029ae commit e81bd82

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

test/http2_refused_stream_retry_tests.jl

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ end
5050
# REFUSED_STREAM, or GOAWAY rejecting the in-flight stream), then answers 200.
5151
# An accept loop plus a per-connection headers loop keeps it agnostic to
5252
# whether the client retries on the same connection or on a new one.
53-
function _rsr_serve!(listener; scenario::Symbol, refuse_first::Int = 1)
53+
function _rsr_serve!(listener; scenario::Symbol, refuse_first::Int = 1, rst_code::UInt32 = _RSR_REFUSED_STREAM)
5454
attempts = Threads.Atomic{Int}(0)
5555
conns = Threads.Atomic{Int}(0)
5656
accept_task = errormonitor(Threads.@spawn begin
@@ -72,7 +72,7 @@ function _rsr_serve!(listener; scenario::Symbol, refuse_first::Int = 1)
7272
attempt = Threads.atomic_add!(attempts, 1) + 1
7373
if attempt <= refuse_first
7474
if scenario === :rst
75-
_rsr_write_frame!(conn, HT.RSTStreamFrame(hf.stream_id, _RSR_REFUSED_STREAM))
75+
_rsr_write_frame!(conn, HT.RSTStreamFrame(hf.stream_id, rst_code))
7676
elseif scenario === :goaway
7777
_rsr_write_frame!(conn, HT.GoAwayFrame(UInt32(0), _RSR_NO_ERROR, UInt8[]))
7878
break
@@ -144,4 +144,27 @@ end
144144
end
145145
end
146146

147+
# Other reset codes give no unprocessed guarantee: they must surface, not retry.
148+
@testset "HTTP/2 client does not retry a stream reset with CANCEL" begin
149+
listener = ND.listen("tcp", "127.0.0.1:0"; backlog = 8)
150+
laddr = NC.addr(listener)::NC.SocketAddrV4
151+
url = "http://" * ND.join_host_port("127.0.0.1", Int(laddr.port)) * "/"
152+
attempts, conns, accept_task = _rsr_serve!(listener; scenario = :rst, rst_code = UInt32(0x8))
153+
try
154+
result = _rsr_request(url)
155+
@test attempts[] == 1 # surfaced on first occurrence
156+
@test result isa HT.H2StreamResetError
157+
result isa HT.H2StreamResetError && @test occursin("CANCEL", sprint(showerror, result))
158+
finally
159+
HTTP.@try_ignore NC.close(listener)
160+
HTTP.@try_ignore wait(accept_task)
161+
end
162+
end
163+
164+
@testset "H2StreamResetError names RFC 9113 §7 error codes" begin
165+
@test sprint(showerror, HT.H2StreamResetError(UInt32(0x7))) == "HTTP/2 stream reset by peer: REFUSED_STREAM"
166+
@test sprint(showerror, HT.H2StreamResetError(UInt32(0xb))) == "HTTP/2 stream reset by peer: ENHANCE_YOUR_CALM"
167+
@test endswith(sprint(showerror, HT.H2StreamResetError(UInt32(0xff))), ": 0xff")
168+
end
169+
147170
end # parent testset

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ test_files = [
112112
"hpack_tests.jl",
113113
"http2_frame_tests.jl",
114114
"http2_client_tests.jl",
115+
"http2_refused_stream_retry_tests.jl",
115116
"http2_server_tests.jl",
116117
"http_integration_tests.jl",
117118
"http_parity_tests.jl",

0 commit comments

Comments
 (0)