http-2 0.12.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|