Skip to content

Commit 4beb746

Browse files
committed
Add continuation limit.
1 parent 3f16852 commit 4beb746

File tree

3 files changed

+61
-3
lines changed

3 files changed

+61
-3
lines changed

lib/protocol/http2/continuation_frame.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ module Protocol
99
module HTTP2
1010
# Module for frames that can be continued with CONTINUATION frames.
1111
module Continued
12+
# @constant [Integer] The maximum number of continuation frames to read to prevent resource exhaustion.
13+
LIMIT = 8
14+
1215
# Initialize a continuable frame.
1316
# @parameter arguments [Array] Arguments passed to parent constructor.
1417
def initialize(*)
@@ -30,12 +33,20 @@ def end_headers?
3033
end
3134

3235
# Read the frame and any continuation frames from the stream.
36+
#
37+
# There is an upper limit to the number of continuation frames that can be read to prevent resource exhaustion. If the limit is 0, only one frame will be read (the initial frame). Otherwise, the limit decrements with each continuation frame read.
38+
#
3339
# @parameter stream [IO] The stream to read from.
3440
# @parameter maximum_frame_size [Integer] Maximum allowed frame size.
35-
def read(stream, maximum_frame_size)
36-
super
41+
# @parameter limit [Integer] The maximum number of continuation frames to read.
42+
def read(stream, maximum_frame_size, limit = LIMIT)
43+
super(stream, maximum_frame_size)
3744

3845
unless end_headers?
46+
if limit.zero?
47+
raise ProtocolError, "Too many continuation frames!"
48+
end
49+
3950
continuation = ContinuationFrame.new
4051
continuation.read_header(stream)
4152

@@ -48,7 +59,7 @@ def read(stream, maximum_frame_size)
4859
raise ProtocolError, "Invalid stream id: #{continuation.stream_id} for continuation of stream id: #{@stream_id}!"
4960
end
5061

51-
continuation.read(stream, maximum_frame_size)
62+
continuation.read(stream, maximum_frame_size, limit - 1)
5263

5364
@continuation = continuation
5465
end
@@ -112,6 +123,10 @@ class ContinuationFrame < Frame
112123

113124
TYPE = 0x9
114125

126+
def read(stream, maximum_frame_size, limit = 8)
127+
super
128+
end
129+
115130
# This is only invoked if the continuation is received out of the normal flow.
116131
def apply(connection)
117132
connection.receive_continuation(self)

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Introduce a limit to the number of CONTINUATION frames that can be read to prevent resource exhaustion. The default limit is 8 continuation frames, which means a total of 9 frames (1 initial + 8 continuation). This limit can be adjusted by passing a different value to the `limit` parameter in the `Continued.read` method. Setting the limit to 0 will only read the initial frame without any continuation frames. In order to change the default, you can redefine the `LIMIT` constant in the `Protocol::HTTP2::Continued` module, OR you can pass a different frame class to the framer.
6+
37
## v0.22.0
48

59
### Added Priority Update Frame and Stream Priority

test/protocol/http2/continuation_frame.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,43 @@ def before
6262
expect(frame.inspect).to be =~ /stream_id=0 flags=0 length=0/
6363
end
6464
end
65+
66+
with "read(limit)" do
67+
it "reads a single frame" do
68+
frame.pack data
69+
70+
buffer = StringIO.new
71+
frame.write(buffer)
72+
buffer.rewind
73+
74+
new_frame = subject.new
75+
new_frame.read(buffer, 128, 0)
76+
expect(new_frame.unpack).to be == data
77+
end
78+
79+
it "raises if too many continuation frames" do
80+
frame.pack data, maximum_size: 2
81+
82+
buffer = StringIO.new
83+
frame.write(buffer)
84+
buffer.rewind
85+
86+
expect do
87+
new_frame = subject.new
88+
new_frame.read(buffer, 128, 1)
89+
end.to raise_exception(Protocol::HTTP2::ProtocolError, message: be =~ /Too many continuation frames!/)
90+
end
91+
92+
it "reads multiple continuation frames" do
93+
frame.pack data, maximum_size: 2
94+
95+
buffer = StringIO.new
96+
frame.write(buffer)
97+
buffer.rewind
98+
99+
new_frame = subject.new
100+
new_frame.read(buffer, 128, 8)
101+
expect(new_frame.unpack).to be == data
102+
end
103+
end
65104
end

0 commit comments

Comments
 (0)