http-2 0.12.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -7
- data/lib/http/2/base64.rb +45 -0
- data/lib/http/2/client.rb +14 -5
- data/lib/http/2/connection.rb +176 -90
- data/lib/http/2/emitter.rb +4 -5
- data/lib/http/2/error.rb +22 -1
- data/lib/http/2/extensions.rb +53 -0
- data/lib/http/2/flow_buffer.rb +88 -35
- data/lib/http/2/framer.rb +127 -89
- data/lib/http/2/header/compressor.rb +157 -0
- data/lib/http/2/header/decompressor.rb +144 -0
- data/lib/http/2/header/encoding_context.rb +337 -0
- data/lib/http/2/{huffman.rb → header/huffman.rb} +11 -7
- data/lib/http/2/{huffman_statemachine.rb → header/huffman_statemachine.rb} +2 -0
- data/lib/http/2/header.rb +35 -0
- data/lib/http/2/server.rb +39 -14
- data/lib/http/2/stream.rb +86 -17
- data/lib/http/2/version.rb +1 -1
- data/lib/http/2.rb +12 -13
- data/sig/client.rbs +9 -0
- data/sig/connection.rbs +93 -0
- data/sig/emitter.rbs +13 -0
- data/sig/error.rbs +35 -0
- data/sig/extensions.rbs +5 -0
- data/sig/flow_buffer.rbs +21 -0
- data/sig/frame_buffer.rbs +13 -0
- data/sig/framer.rbs +54 -0
- data/sig/header/compressor.rbs +27 -0
- data/sig/header/decompressor.rbs +22 -0
- data/sig/header/encoding_context.rbs +34 -0
- data/sig/header/huffman.rbs +9 -0
- data/sig/header.rbs +27 -0
- data/sig/next.rbs +101 -0
- data/sig/server.rbs +12 -0
- data/sig/stream.rbs +91 -0
- metadata +37 -25
- data/LICENSE +0 -21
- data/lib/http/2/buffer.rb +0 -78
- data/lib/http/2/compressor.rb +0 -580
data/lib/http/2/flow_buffer.rb
CHANGED
@@ -5,16 +5,28 @@ module HTTP2
|
|
5
5
|
# be split and / or may be buffered based on current flow control window.
|
6
6
|
#
|
7
7
|
module FlowBuffer
|
8
|
+
include Error
|
9
|
+
|
10
|
+
MAX_WINDOW_SIZE = (2 << 30) - 1
|
11
|
+
|
8
12
|
# Amount of buffered data. Only DATA payloads are subject to flow stream
|
9
13
|
# and connection flow control.
|
10
14
|
#
|
11
15
|
# @return [Integer]
|
12
16
|
def buffered_amount
|
13
|
-
|
17
|
+
send_buffer.bytesize
|
18
|
+
end
|
19
|
+
|
20
|
+
def flush
|
21
|
+
send_data
|
14
22
|
end
|
15
23
|
|
16
24
|
private
|
17
25
|
|
26
|
+
def send_buffer
|
27
|
+
@send_buffer ||= FrameBuffer.new
|
28
|
+
end
|
29
|
+
|
18
30
|
def update_local_window(frame)
|
19
31
|
frame_size = frame[:payload].bytesize
|
20
32
|
frame_size += frame[:padding] || 0
|
@@ -26,7 +38,7 @@ module HTTP2
|
|
26
38
|
# current received window size + delta length is strictly larger than
|
27
39
|
# local window size, it throws a flow control error.
|
28
40
|
#
|
29
|
-
error(:flow_control_error) if @local_window
|
41
|
+
error(:flow_control_error) if @local_window < 0
|
30
42
|
|
31
43
|
# Send WINDOW_UPDATE if the received window size goes over
|
32
44
|
# the local window size / 2.
|
@@ -55,51 +67,92 @@ module HTTP2
|
|
55
67
|
# Buffered DATA frames are emitted in FIFO order.
|
56
68
|
#
|
57
69
|
# @param frame [Hash]
|
58
|
-
# @param encode [Boolean] set to true by
|
70
|
+
# @param encode [Boolean] set to true by connection
|
59
71
|
def send_data(frame = nil, encode = false)
|
60
|
-
|
61
|
-
|
62
|
-
# FIXME: Frames with zero length with the END_STREAM flag set (that
|
63
|
-
# is, an empty DATA frame) MAY be sent if there is no available space
|
64
|
-
# in either flow control window.
|
65
|
-
while @remote_window.positive? && !@send_buffer.empty?
|
66
|
-
frame = @send_buffer.shift
|
72
|
+
send_buffer << frame unless frame.nil?
|
67
73
|
|
68
|
-
|
69
|
-
frame_size = frame[:payload].bytesize
|
74
|
+
while (frame = send_buffer.retrieve(@remote_window))
|
70
75
|
|
71
|
-
|
72
|
-
|
73
|
-
chunk = frame.dup
|
74
|
-
|
75
|
-
# Split frame so that it fits in the window
|
76
|
-
# TODO: consider padding!
|
77
|
-
frame[:payload] = payload.slice!(0, @remote_window)
|
78
|
-
chunk[:length] = payload.bytesize
|
79
|
-
chunk[:payload] = payload
|
80
|
-
|
81
|
-
# if no longer last frame in sequence...
|
82
|
-
frame[:flags] -= [:end_stream] if frame[:flags].include? :end_stream
|
83
|
-
|
84
|
-
@send_buffer.unshift chunk
|
85
|
-
sent = @remote_window
|
86
|
-
else
|
87
|
-
sent = frame_size
|
88
|
-
end
|
76
|
+
# puts "#{self.class} -> #{@remote_window}"
|
77
|
+
sent = frame[:payload].bytesize
|
89
78
|
|
90
79
|
manage_state(frame) do
|
91
|
-
|
92
|
-
|
80
|
+
if encode
|
81
|
+
encode(frame).each { |f| emit(:frame, f) }
|
82
|
+
else
|
83
|
+
emit(:frame, frame)
|
84
|
+
end
|
93
85
|
@remote_window -= sent
|
94
86
|
end
|
95
87
|
end
|
96
88
|
end
|
97
89
|
|
98
|
-
def process_window_update(frame)
|
90
|
+
def process_window_update(frame:, encode: false)
|
99
91
|
return if frame[:ignore]
|
100
92
|
|
101
|
-
|
102
|
-
|
93
|
+
if frame[:increment]
|
94
|
+
raise ProtocolError, "increment MUST be higher than zero" if frame[:increment].zero?
|
95
|
+
|
96
|
+
@remote_window += frame[:increment]
|
97
|
+
error(:flow_control_error, msg: "window size too large") if @remote_window > MAX_WINDOW_SIZE
|
98
|
+
end
|
99
|
+
send_data(nil, encode)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class FrameBuffer
|
104
|
+
attr_reader :bytesize
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
@buffer = []
|
108
|
+
@bytesize = 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def <<(frame)
|
112
|
+
@buffer << frame
|
113
|
+
@bytesize += frame[:payload].bytesize
|
114
|
+
end
|
115
|
+
|
116
|
+
def empty?
|
117
|
+
@bytesize.zero?
|
118
|
+
end
|
119
|
+
|
120
|
+
def retrieve(window_size)
|
121
|
+
frame = @buffer.first or return
|
122
|
+
|
123
|
+
frame_size = frame[:payload].bytesize
|
124
|
+
end_stream = frame[:flags].include? :end_stream
|
125
|
+
|
126
|
+
# Frames with zero length with the END_STREAM flag set (that
|
127
|
+
# is, an empty DATA frame) MAY be sent if there is no available space
|
128
|
+
# in either flow control window.
|
129
|
+
return if window_size <= 0 && !(frame_size == 0 && end_stream)
|
130
|
+
|
131
|
+
@buffer.shift
|
132
|
+
|
133
|
+
if frame_size > window_size
|
134
|
+
payload = frame[:payload]
|
135
|
+
chunk = frame.dup
|
136
|
+
|
137
|
+
# Split frame so that it fits in the window
|
138
|
+
# TODO: consider padding!
|
139
|
+
frame_bytes = payload.byteslice(0, window_size)
|
140
|
+
payload = payload.byteslice(window_size..-1)
|
141
|
+
|
142
|
+
frame[:payload] = frame_bytes
|
143
|
+
frame[:length] = frame_bytes.bytesize
|
144
|
+
chunk[:payload] = payload
|
145
|
+
chunk[:length] = payload.bytesize
|
146
|
+
|
147
|
+
# if no longer last frame in sequence...
|
148
|
+
frame[:flags] -= [:end_stream] if end_stream
|
149
|
+
|
150
|
+
@buffer.unshift(chunk)
|
151
|
+
@bytesize -= window_size
|
152
|
+
else
|
153
|
+
@bytesize -= frame_size
|
154
|
+
end
|
155
|
+
frame
|
103
156
|
end
|
104
157
|
end
|
105
158
|
end
|
data/lib/http/2/framer.rb
CHANGED
@@ -4,13 +4,16 @@ module HTTP2
|
|
4
4
|
# Performs encoding, decoding, and validation of binary HTTP/2 frames.
|
5
5
|
#
|
6
6
|
class Framer
|
7
|
+
using StringExtensions
|
8
|
+
|
7
9
|
include Error
|
10
|
+
include PackingExtensions
|
8
11
|
|
9
12
|
# Default value of max frame size (16384 bytes)
|
10
|
-
DEFAULT_MAX_FRAME_SIZE = 2
|
13
|
+
DEFAULT_MAX_FRAME_SIZE = 2 << 13
|
11
14
|
|
12
|
-
#
|
13
|
-
attr_accessor :
|
15
|
+
# maximum frame size
|
16
|
+
attr_accessor :local_max_frame_size, :remote_max_frame_size
|
14
17
|
|
15
18
|
# Maximum stream ID (2^31)
|
16
19
|
MAX_STREAM_ID = 0x7fffffff
|
@@ -30,7 +33,8 @@ module HTTP2
|
|
30
33
|
goaway: 0x7,
|
31
34
|
window_update: 0x8,
|
32
35
|
continuation: 0x9,
|
33
|
-
altsvc: 0xa
|
36
|
+
altsvc: 0xa,
|
37
|
+
origin: 0xc
|
34
38
|
}.freeze
|
35
39
|
|
36
40
|
FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
|
@@ -59,7 +63,13 @@ module HTTP2
|
|
59
63
|
goaway: {},
|
60
64
|
window_update: {},
|
61
65
|
continuation: { end_headers: 2 },
|
62
|
-
altsvc: {}
|
66
|
+
altsvc: {},
|
67
|
+
origin: {
|
68
|
+
reserved: 1,
|
69
|
+
reserved2: 2,
|
70
|
+
reserved3: 4,
|
71
|
+
reserved4: 8
|
72
|
+
}
|
63
73
|
}.each_value(&:freeze).freeze
|
64
74
|
|
65
75
|
# Default settings as defined by the spec
|
@@ -93,9 +103,9 @@ module HTTP2
|
|
93
103
|
RBIT = 0x7fffffff
|
94
104
|
RBYTE = 0x0fffffff
|
95
105
|
EBIT = 0x80000000
|
96
|
-
UINT32 =
|
97
|
-
UINT16 =
|
98
|
-
UINT8 =
|
106
|
+
UINT32 = "N"
|
107
|
+
UINT16 = "n"
|
108
|
+
UINT8 = "C"
|
99
109
|
HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
|
100
110
|
FRAME_LENGTH_HISHIFT = 16
|
101
111
|
FRAME_LENGTH_LOMASK = 0xFFFF
|
@@ -104,23 +114,24 @@ module HTTP2
|
|
104
114
|
|
105
115
|
# Initializes new framer object.
|
106
116
|
#
|
107
|
-
def initialize
|
108
|
-
|
117
|
+
def initialize(local_max_frame_size = DEFAULT_MAX_FRAME_SIZE,
|
118
|
+
remote_max_frame_size = DEFAULT_MAX_FRAME_SIZE)
|
119
|
+
@local_max_frame_size = local_max_frame_size
|
120
|
+
@remote_max_frame_size = remote_max_frame_size
|
109
121
|
end
|
110
122
|
|
111
123
|
# Generates common 9-byte frame header.
|
112
124
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
|
113
125
|
#
|
114
126
|
# @param frame [Hash]
|
127
|
+
# @param buffer [String] buffer to pack bytes into
|
115
128
|
# @return [String]
|
116
|
-
def common_header(frame)
|
117
|
-
header = []
|
118
|
-
|
129
|
+
def common_header(frame, buffer:)
|
119
130
|
raise CompressionError, "Invalid frame type (#{frame[:type]})" unless FRAME_TYPES[frame[:type]]
|
120
131
|
|
121
|
-
raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @
|
132
|
+
raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @remote_max_frame_size
|
122
133
|
|
123
|
-
raise CompressionError, "Frame size is invalid: #{frame[:length]}" if
|
134
|
+
raise CompressionError, "Frame size is invalid: #{frame[:length]}" if frame[:length] < 0
|
124
135
|
|
125
136
|
raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID
|
126
137
|
|
@@ -128,32 +139,33 @@ module HTTP2
|
|
128
139
|
raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
|
129
140
|
end
|
130
141
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
142
|
+
pack([
|
143
|
+
(frame[:length] >> FRAME_LENGTH_HISHIFT),
|
144
|
+
(frame[:length] & FRAME_LENGTH_LOMASK),
|
145
|
+
FRAME_TYPES[frame[:type]],
|
146
|
+
frame[:flags].reduce(0) do |acc, f|
|
147
|
+
position = FRAME_FLAGS[frame[:type]][f]
|
148
|
+
raise CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}" unless position
|
149
|
+
|
150
|
+
acc | (1 << position)
|
151
|
+
end,
|
152
|
+
frame[:stream]
|
153
|
+
], HEADERPACK, buffer: buffer, offset: 0) # 8+16,8,8,32
|
143
154
|
end
|
144
155
|
|
145
156
|
# Decodes common 9-byte header.
|
146
157
|
#
|
147
158
|
# @param buf [Buffer]
|
159
|
+
# @return [Hash] the corresponding frame
|
148
160
|
def read_common_header(buf)
|
149
161
|
frame = {}
|
150
|
-
len_hi, len_lo, type, flags, stream = buf.
|
162
|
+
len_hi, len_lo, type, flags, stream = buf.byteslice(0, 9).unpack(HEADERPACK)
|
151
163
|
|
152
164
|
frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
|
153
165
|
frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
|
154
166
|
if frame[:type]
|
155
167
|
frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
|
156
|
-
acc << name if (flags & (1 << pos))
|
168
|
+
acc << name if (flags & (1 << pos)) > 0
|
157
169
|
end
|
158
170
|
end
|
159
171
|
|
@@ -166,7 +178,7 @@ module HTTP2
|
|
166
178
|
#
|
167
179
|
# @param frame [Hash]
|
168
180
|
def generate(frame)
|
169
|
-
bytes =
|
181
|
+
bytes = "".b
|
170
182
|
length = 0
|
171
183
|
|
172
184
|
frame[:flags] ||= []
|
@@ -175,11 +187,12 @@ module HTTP2
|
|
175
187
|
case frame[:type]
|
176
188
|
when :data
|
177
189
|
bytes << frame[:payload]
|
190
|
+
bytes.force_encoding(Encoding::BINARY)
|
178
191
|
length += frame[:payload].bytesize
|
179
192
|
|
180
193
|
when :headers
|
181
|
-
if frame[:weight] || frame[:
|
182
|
-
unless frame[:weight] && frame[:
|
194
|
+
if frame[:weight] || frame[:dependency] || !frame[:exclusive].nil?
|
195
|
+
unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
|
183
196
|
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
184
197
|
end
|
185
198
|
|
@@ -187,8 +200,8 @@ module HTTP2
|
|
187
200
|
end
|
188
201
|
|
189
202
|
if frame[:flags].include? :priority
|
190
|
-
|
191
|
-
|
203
|
+
pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
|
204
|
+
pack([frame[:weight] - 1], UINT8, buffer: bytes)
|
192
205
|
length += 5
|
193
206
|
end
|
194
207
|
|
@@ -196,23 +209,23 @@ module HTTP2
|
|
196
209
|
length += frame[:payload].bytesize
|
197
210
|
|
198
211
|
when :priority
|
199
|
-
unless frame[:weight] && frame[:
|
212
|
+
unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
|
200
213
|
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
201
214
|
end
|
202
215
|
|
203
|
-
|
204
|
-
|
216
|
+
pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
|
217
|
+
pack([frame[:weight] - 1], UINT8, buffer: bytes)
|
205
218
|
length += 5
|
206
219
|
|
207
220
|
when :rst_stream
|
208
|
-
|
221
|
+
pack_error(frame[:error], buffer: bytes)
|
209
222
|
length += 4
|
210
223
|
|
211
224
|
when :settings
|
212
225
|
raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
213
226
|
|
214
227
|
frame[:payload].each do |(k, v)|
|
215
|
-
if k.is_a? Integer
|
228
|
+
if k.is_a? Integer # rubocop:disable Style/GuardClause
|
216
229
|
DEFINED_SETTINGS.value?(k) || next
|
217
230
|
else
|
218
231
|
k = DEFINED_SETTINGS[k]
|
@@ -220,27 +233,25 @@ module HTTP2
|
|
220
233
|
raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
|
221
234
|
end
|
222
235
|
|
223
|
-
|
224
|
-
|
236
|
+
pack([k], UINT16, buffer: bytes)
|
237
|
+
pack([v], UINT32, buffer: bytes)
|
225
238
|
length += 6
|
226
239
|
end
|
227
240
|
|
228
241
|
when :push_promise
|
229
|
-
|
242
|
+
pack([frame[:promise_stream] & RBIT], UINT32, buffer: bytes)
|
230
243
|
bytes << frame[:payload]
|
231
244
|
length += 4 + frame[:payload].bytesize
|
232
245
|
|
233
246
|
when :ping
|
234
|
-
if frame[:payload].bytesize != 8
|
235
|
-
raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
|
236
|
-
end
|
247
|
+
raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)" if frame[:payload].bytesize != 8
|
237
248
|
|
238
249
|
bytes << frame[:payload]
|
239
250
|
length += 8
|
240
251
|
|
241
252
|
when :goaway
|
242
|
-
|
243
|
-
|
253
|
+
pack([frame[:last_stream] & RBIT], UINT32, buffer: bytes)
|
254
|
+
pack_error(frame[:error], buffer: bytes)
|
244
255
|
length += 8
|
245
256
|
|
246
257
|
if frame[:payload]
|
@@ -249,7 +260,7 @@ module HTTP2
|
|
249
260
|
end
|
250
261
|
|
251
262
|
when :window_update
|
252
|
-
|
263
|
+
pack([frame[:increment] & RBIT], UINT32, buffer: bytes)
|
253
264
|
length += 4
|
254
265
|
|
255
266
|
when :continuation
|
@@ -257,32 +268,40 @@ module HTTP2
|
|
257
268
|
length += frame[:payload].bytesize
|
258
269
|
|
259
270
|
when :altsvc
|
260
|
-
|
271
|
+
pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes)
|
261
272
|
length += 6
|
262
273
|
if frame[:proto]
|
263
|
-
raise CompressionError,
|
274
|
+
raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
|
264
275
|
|
265
|
-
|
266
|
-
bytes << frame[:proto]
|
276
|
+
pack([frame[:proto].bytesize], UINT8, buffer: bytes)
|
277
|
+
bytes << frame[:proto]
|
267
278
|
length += 1 + frame[:proto].bytesize
|
268
279
|
else
|
269
|
-
|
280
|
+
pack([0], UINT8, buffer: bytes)
|
270
281
|
length += 1
|
271
282
|
end
|
272
283
|
if frame[:host]
|
273
|
-
raise CompressionError,
|
284
|
+
raise CompressionError, "Host too long" if frame[:host].bytesize > 255
|
274
285
|
|
275
|
-
|
276
|
-
bytes << frame[:host]
|
286
|
+
pack([frame[:host].bytesize], UINT8, buffer: bytes)
|
287
|
+
bytes << frame[:host]
|
277
288
|
length += 1 + frame[:host].bytesize
|
278
289
|
else
|
279
|
-
|
290
|
+
pack([0], UINT8, buffer: bytes)
|
280
291
|
length += 1
|
281
292
|
end
|
282
293
|
if frame[:origin]
|
283
294
|
bytes << frame[:origin]
|
284
295
|
length += frame[:origin].bytesize
|
285
296
|
end
|
297
|
+
|
298
|
+
when :origin
|
299
|
+
frame[:payload].each do |origin|
|
300
|
+
pack([origin.bytesize], UINT16, buffer: bytes)
|
301
|
+
bytes << origin
|
302
|
+
length += 2 + origin.bytesize
|
303
|
+
end
|
304
|
+
|
286
305
|
end
|
287
306
|
|
288
307
|
# Process padding.
|
@@ -295,12 +314,12 @@ module HTTP2
|
|
295
314
|
|
296
315
|
padlen = frame[:padding]
|
297
316
|
|
298
|
-
if padlen <= 0 || padlen > 256 || padlen + length > @
|
317
|
+
if padlen <= 0 || padlen > 256 || padlen + length > @remote_max_frame_size
|
299
318
|
raise CompressionError, "Invalid padding #{padlen}"
|
300
319
|
end
|
301
320
|
|
302
321
|
length += padlen
|
303
|
-
|
322
|
+
pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
|
304
323
|
frame[:flags] << :padded
|
305
324
|
|
306
325
|
# Padding: Padding octets that contain no application semantic value.
|
@@ -310,7 +329,7 @@ module HTTP2
|
|
310
329
|
end
|
311
330
|
|
312
331
|
frame[:length] = length
|
313
|
-
|
332
|
+
common_header(frame, buffer: bytes)
|
314
333
|
end
|
315
334
|
|
316
335
|
# Decodes complete HTTP/2 frame from provided buffer. If the buffer
|
@@ -318,12 +337,12 @@ module HTTP2
|
|
318
337
|
#
|
319
338
|
# @param buf [Buffer]
|
320
339
|
def parse(buf)
|
321
|
-
return
|
340
|
+
return if buf.size < 9
|
322
341
|
|
323
342
|
frame = read_common_header(buf)
|
324
|
-
return
|
343
|
+
return if buf.size < 9 + frame[:length]
|
325
344
|
|
326
|
-
raise ProtocolError,
|
345
|
+
raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
|
327
346
|
|
328
347
|
buf.read(9)
|
329
348
|
payload = buf.read(frame[:length])
|
@@ -331,7 +350,7 @@ module HTTP2
|
|
331
350
|
# Implementations MUST discard frames
|
332
351
|
# that have unknown or unsupported types.
|
333
352
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.5
|
334
|
-
return
|
353
|
+
return frame if frame[:type].nil?
|
335
354
|
|
336
355
|
# Process padding
|
337
356
|
padlen = 0
|
@@ -340,38 +359,46 @@ module HTTP2
|
|
340
359
|
if padded
|
341
360
|
padlen = payload.read(1).unpack1(UINT8)
|
342
361
|
frame[:padding] = padlen + 1
|
343
|
-
raise ProtocolError,
|
362
|
+
raise ProtocolError, "padding too long" if padlen > payload.bytesize
|
344
363
|
|
345
|
-
payload.
|
364
|
+
payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
|
346
365
|
frame[:length] -= frame[:padding]
|
347
366
|
frame[:flags].delete(:padded)
|
348
367
|
end
|
349
368
|
end
|
350
369
|
|
351
370
|
case frame[:type]
|
352
|
-
when :data
|
371
|
+
when :data, :ping, :continuation
|
353
372
|
frame[:payload] = payload.read(frame[:length])
|
354
373
|
when :headers
|
355
374
|
if frame[:flags].include? :priority
|
356
375
|
e_sd = payload.read_uint32
|
357
|
-
frame[:
|
376
|
+
frame[:dependency] = e_sd & RBIT
|
358
377
|
frame[:exclusive] = (e_sd & EBIT) != 0
|
359
|
-
|
378
|
+
weight = payload.byteslice(0, 1).ord + 1
|
379
|
+
frame[:weight] = weight
|
380
|
+
payload = payload.byteslice(1..-1)
|
360
381
|
end
|
361
382
|
frame[:payload] = payload.read(frame[:length])
|
362
383
|
when :priority
|
384
|
+
raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
|
385
|
+
|
363
386
|
e_sd = payload.read_uint32
|
364
|
-
frame[:
|
387
|
+
frame[:dependency] = e_sd & RBIT
|
365
388
|
frame[:exclusive] = (e_sd & EBIT) != 0
|
366
|
-
|
389
|
+
weight = payload.byteslice(0, 1).ord + 1
|
390
|
+
frame[:weight] = weight
|
391
|
+
payload = payload.byteslice(1..-1)
|
367
392
|
when :rst_stream
|
393
|
+
raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
|
394
|
+
|
368
395
|
frame[:error] = unpack_error payload.read_uint32
|
369
396
|
|
370
397
|
when :settings
|
371
398
|
# NOTE: frame[:length] might not match the number of frame[:payload]
|
372
399
|
# because unknown extensions are ignored.
|
373
400
|
frame[:payload] = []
|
374
|
-
raise ProtocolError,
|
401
|
+
raise ProtocolError, "Invalid settings payload length" unless (frame[:length] % 6).zero?
|
375
402
|
|
376
403
|
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
377
404
|
|
@@ -387,28 +414,40 @@ module HTTP2
|
|
387
414
|
when :push_promise
|
388
415
|
frame[:promise_stream] = payload.read_uint32 & RBIT
|
389
416
|
frame[:payload] = payload.read(frame[:length])
|
390
|
-
when :ping
|
391
|
-
frame[:payload] = payload.read(frame[:length])
|
392
417
|
when :goaway
|
393
418
|
frame[:last_stream] = payload.read_uint32 & RBIT
|
394
419
|
frame[:error] = unpack_error payload.read_uint32
|
395
420
|
|
396
421
|
size = frame[:length] - 8 # for last_stream and error
|
397
|
-
frame[:payload] = payload.read(size) if size
|
422
|
+
frame[:payload] = payload.read(size) if size > 0
|
398
423
|
when :window_update
|
424
|
+
if frame[:length] % 4 != 0
|
425
|
+
raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)"
|
426
|
+
end
|
427
|
+
|
399
428
|
frame[:increment] = payload.read_uint32 & RBIT
|
400
|
-
when :continuation
|
401
|
-
frame[:payload] = payload.read(frame[:length])
|
402
429
|
when :altsvc
|
403
430
|
frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
|
404
431
|
|
405
|
-
len = payload.
|
406
|
-
|
432
|
+
len = payload.byteslice(0, 1).ord
|
433
|
+
payload = payload.byteslice(1..-1)
|
434
|
+
frame[:proto] = payload.read(len) if len > 0
|
407
435
|
|
408
|
-
len = payload.
|
409
|
-
|
436
|
+
len = payload.byteslice(0, 1).ord
|
437
|
+
payload = payload.byteslice(1..-1)
|
438
|
+
frame[:host] = payload.read(len) if len > 0
|
410
439
|
|
411
440
|
frame[:origin] = payload.read(payload.size) unless payload.empty?
|
441
|
+
|
442
|
+
when :origin
|
443
|
+
origins = []
|
444
|
+
|
445
|
+
until payload.empty?
|
446
|
+
len = payload.read(2).unpack1(UINT16)
|
447
|
+
origins << payload.read(len)
|
448
|
+
end
|
449
|
+
|
450
|
+
frame[:payload] = origins
|
412
451
|
# else # Unknown frame type is explicitly allowed
|
413
452
|
end
|
414
453
|
|
@@ -417,19 +456,18 @@ module HTTP2
|
|
417
456
|
|
418
457
|
private
|
419
458
|
|
420
|
-
def pack_error(
|
421
|
-
unless
|
422
|
-
|
459
|
+
def pack_error(error, buffer:)
|
460
|
+
unless error.is_a? Integer
|
461
|
+
error = DEFINED_ERRORS[error]
|
423
462
|
|
424
|
-
|
463
|
+
raise CompressionError, "Unknown error ID for #{error}" unless error
|
425
464
|
end
|
426
465
|
|
427
|
-
[
|
466
|
+
pack([error], UINT32, buffer: buffer)
|
428
467
|
end
|
429
468
|
|
430
|
-
def unpack_error(
|
431
|
-
|
432
|
-
name || error
|
469
|
+
def unpack_error(error)
|
470
|
+
DEFINED_ERRORS.key(error) || error
|
433
471
|
end
|
434
472
|
end
|
435
473
|
end
|