Skip to content

Commit dee548e

Browse files
committed
Fix handling of stream reset frames.
1 parent 9bd4644 commit dee548e

File tree

5 files changed

+60
-12
lines changed

5 files changed

+60
-12
lines changed

lib/protocol/http2/client.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ def initialize(framer)
2727
super(framer, 1)
2828
end
2929

30+
def local_stream_id?(id)
31+
id.odd?
32+
end
33+
34+
def remote_stream_id?(id)
35+
id.even?
36+
end
37+
3038
def valid_remote_stream_id?(stream_id)
3139
stream_id.even?
3240
end

lib/protocol/http2/connection.rb

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def initialize(framer, local_stream_id)
4141
@dependencies = {0 => @dependency}
4242

4343
@framer = framer
44+
# The next stream id to use:
4445
@local_stream_id = local_stream_id
4546
@remote_stream_id = 0
4647

@@ -420,9 +421,42 @@ def receive_push_promise(frame)
420421
raise ProtocolError, "Unable to receive push promise!"
421422
end
422423

424+
def client_stream_id?(id)
425+
id.odd?
426+
end
427+
428+
def server_stream_id?(id)
429+
id.even?
430+
end
431+
432+
def closed_stream_id?(id)
433+
if id.zero?
434+
# The connection "stream id" can never be closed:
435+
false
436+
elsif id.even?
437+
# Server-initiated streams are even.
438+
if @local_stream_id.even?
439+
id < @local_stream_id
440+
else
441+
id < @remote_stream_id
442+
end
443+
elsif id.odd?
444+
# Client-initiated streams are odd.
445+
if @local_stream_id.odd?
446+
id < @local_stream_id
447+
else
448+
id < @remote_stream_id
449+
end
450+
end
451+
end
452+
423453
def receive_reset_stream(frame)
424-
if stream = @streams[frame.stream_id]
454+
if frame.connection?
455+
raise ProtocolError, "Cannot reset connection!"
456+
elsif stream = @streams[frame.stream_id]
425457
stream.receive_reset_stream(frame)
458+
elsif closed_stream_id?(frame.stream_id)
459+
# Ignore.
426460
else
427461
raise StreamClosed, "Cannot reset stream #{frame.stream_id}"
428462
end
@@ -439,8 +473,10 @@ def receive_window_update(frame)
439473
rescue ProtocolError => error
440474
stream.send_reset_stream(error.code)
441475
end
442-
elsif frame.stream_id > @remote_stream_id
443-
# Receiving any frame other than HEADERS or PRIORITY on a stream in this state MUST be treated as a connection error of type PROTOCOL_ERROR.
476+
elsif closed_stream_id?(frame.stream_id)
477+
# Ignore.
478+
else
479+
# Receiving any frame other than HEADERS or PRIORITY on a stream in this state (idle) MUST be treated as a connection error of type PROTOCOL_ERROR.
444480
raise ProtocolError, "Cannot update window of idle stream #{frame.stream_id}"
445481
end
446482
end

lib/protocol/http2/server.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ def initialize(framer)
2727
super(framer, 2)
2828
end
2929

30+
def local_stream_id?(id)
31+
id.even?
32+
end
33+
34+
def remote_stream_id?(id)
35+
id.odd?
36+
end
37+
3038
def valid_remote_stream_id?(stream_id)
3139
stream_id.odd?
3240
end

lib/protocol/http2/stream.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ def children
130130

131131
# The stream is being closed because the connection is being closed.
132132
def close(error = nil)
133-
@connection.delete(@id)
134133
end
135134

136135
def maximum_frame_size
@@ -247,6 +246,7 @@ def open!
247246
# @param error_code [Integer] the error code if the stream was closed due to a stream reset.
248247
def close!(error_code = nil)
249248
@state = :closed
249+
@connection.delete(@id)
250250

251251
if error_code
252252
error = StreamError.new("Stream closed!", error_code)
@@ -352,16 +352,10 @@ def receive_data(frame)
352352
end
353353
end
354354

355-
def ignore_reset_stream(frame)
356-
# Async.logger.warn(self) {"Received reset stream (#{error_code}) in state: #{@state}!"}
357-
end
358-
359355
def receive_reset_stream(frame)
360356
if @state == :idle
361357
# If a RST_STREAM frame identifying an idle stream is received, the recipient MUST treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
362358
raise ProtocolError, "Cannot receive reset stream in state: #{@state}!"
363-
elsif self.closed?
364-
ignore_reset_stream(frame)
365359
else
366360
error_code = frame.unpack
367361

spec/protocol/http2/stream_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,11 @@
118118
end
119119

120120
it "ignores reset stream" do
121-
expect(stream).to receive(:ignore_reset_stream)
121+
server_stream = server.create_stream(stream.id)
122+
server_stream.open!
123+
server_stream.send_reset_stream
122124

123-
stream.receive_reset_stream(double)
125+
client.read_frame
124126
end
125127
end
126128
end

0 commit comments

Comments
 (0)