Skip to content

Commit cb7b594

Browse files
pspacekdaurnimator
authored andcommitted
http/h2_stream: ignore delayed RST_STREAM frames
Fixes: daurnimator#145
1 parent bc49a4b commit cb7b594

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

http/h2_stream.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,9 @@ frame_handlers[frame_types.RST_STREAM] = function(stream, flags, payload, deadli
690690
end
691691
if stream.state == "idle" then
692692
return nil, h2_errors.PROTOCOL_ERROR:new_traceback("'RST_STREAM' frames MUST NOT be sent for a stream in the 'idle' state"), ce.EILSEQ
693+
elseif stream.state == "closed" then
694+
-- probably a delayed RST_STREAM, ignore
695+
return true
693696
end
694697

695698
local err_code = sunpack(">I4", payload)

spec/h2_stream_spec.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,43 @@ describe("http.h2_stream", function()
124124
assert_loop(cq, TEST_TIMEOUT)
125125
assert.truthy(cq:empty())
126126
end)
127+
it("ignores delayed RST_STREAM on already closed stream", function()
128+
local s, c = new_pair()
129+
local cq = cqueues.new()
130+
cq:wrap(function()
131+
local client_stream = c:new_stream()
132+
local req_headers = new_headers()
133+
req_headers:append(":method", "GET")
134+
req_headers:append(":scheme", "http")
135+
req_headers:append(":path", "/")
136+
req_headers:append(":authority", "example.com")
137+
assert(client_stream:write_headers(req_headers, true))
138+
assert(client_stream:get_headers())
139+
assert("closed", client_stream.state)
140+
-- both sides now have stream in closed state
141+
-- send server a RST_STREAM: it should get ignored
142+
assert(client_stream:rst_stream("post-closed rst_stream"))
143+
assert(c:close())
144+
end)
145+
cq:wrap(function()
146+
local stream = assert(s:get_next_incoming_stream())
147+
assert(stream:get_headers())
148+
local res_headers = new_headers()
149+
res_headers:append(":status", "200")
150+
assert(stream:write_headers(res_headers, true))
151+
-- both sides now have stream in closed state
152+
assert("closed", stream.state)
153+
-- process incoming frames until EOF (i.e. drain RST_STREAM)
154+
-- the RST_STREAM frame should be ignored.
155+
assert(s:loop())
156+
assert(s:close())
157+
end)
158+
cq:wrap(function()
159+
assert(s:loop())
160+
end)
161+
assert_loop(cq, TEST_TIMEOUT)
162+
assert.truthy(cq:empty())
163+
end)
127164
end)
128165
describe("push_promise", function()
129166
it("permits a simple push promise from server => client", function()

0 commit comments

Comments
 (0)