http-2 0.12.0 → 1.0.2
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 +184 -90
- data/lib/http/2/emitter.rb +4 -5
- data/lib/http/2/error.rb +22 -1
- data/lib/http/2/extensions.rb +51 -0
- data/lib/http/2/flow_buffer.rb +88 -35
- data/lib/http/2/framer.rb +145 -108
- 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 +339 -0
- data/lib/http/2/{huffman.rb → header/huffman.rb} +9 -7
- data/lib/http/2/{huffman_statemachine.rb → header/huffman_statemachine.rb} +3 -1
- 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 +94 -0
- data/sig/emitter.rbs +13 -0
- data/sig/error.rbs +35 -0
- data/sig/extensions.rbs +13 -0
- data/sig/flow_buffer.rbs +21 -0
- data/sig/frame_buffer.rbs +13 -0
- data/sig/framer.rbs +55 -0
- data/sig/header/compressor.rbs +27 -0
- data/sig/header/decompressor.rbs +24 -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
@@ -5,12 +5,14 @@ module HTTP2
|
|
5
5
|
#
|
6
6
|
class Framer
|
7
7
|
include Error
|
8
|
+
include PackingExtensions
|
9
|
+
include BufferUtils
|
8
10
|
|
9
11
|
# Default value of max frame size (16384 bytes)
|
10
|
-
DEFAULT_MAX_FRAME_SIZE = 2
|
12
|
+
DEFAULT_MAX_FRAME_SIZE = 2 << 13
|
11
13
|
|
12
|
-
#
|
13
|
-
attr_accessor :
|
14
|
+
# maximum frame size
|
15
|
+
attr_accessor :local_max_frame_size, :remote_max_frame_size
|
14
16
|
|
15
17
|
# Maximum stream ID (2^31)
|
16
18
|
MAX_STREAM_ID = 0x7fffffff
|
@@ -30,7 +32,8 @@ module HTTP2
|
|
30
32
|
goaway: 0x7,
|
31
33
|
window_update: 0x8,
|
32
34
|
continuation: 0x9,
|
33
|
-
altsvc: 0xa
|
35
|
+
altsvc: 0xa,
|
36
|
+
origin: 0xc
|
34
37
|
}.freeze
|
35
38
|
|
36
39
|
FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
|
@@ -59,7 +62,13 @@ module HTTP2
|
|
59
62
|
goaway: {},
|
60
63
|
window_update: {},
|
61
64
|
continuation: { end_headers: 2 },
|
62
|
-
altsvc: {}
|
65
|
+
altsvc: {},
|
66
|
+
origin: {
|
67
|
+
reserved: 1,
|
68
|
+
reserved2: 2,
|
69
|
+
reserved3: 4,
|
70
|
+
reserved4: 8
|
71
|
+
}
|
63
72
|
}.each_value(&:freeze).freeze
|
64
73
|
|
65
74
|
# Default settings as defined by the spec
|
@@ -93,9 +102,9 @@ module HTTP2
|
|
93
102
|
RBIT = 0x7fffffff
|
94
103
|
RBYTE = 0x0fffffff
|
95
104
|
EBIT = 0x80000000
|
96
|
-
UINT32 =
|
97
|
-
UINT16 =
|
98
|
-
UINT8 =
|
105
|
+
UINT32 = "N"
|
106
|
+
UINT16 = "n"
|
107
|
+
UINT8 = "C"
|
99
108
|
HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
|
100
109
|
FRAME_LENGTH_HISHIFT = 16
|
101
110
|
FRAME_LENGTH_LOMASK = 0xFFFF
|
@@ -104,23 +113,24 @@ module HTTP2
|
|
104
113
|
|
105
114
|
# Initializes new framer object.
|
106
115
|
#
|
107
|
-
def initialize
|
108
|
-
|
116
|
+
def initialize(local_max_frame_size = DEFAULT_MAX_FRAME_SIZE,
|
117
|
+
remote_max_frame_size = DEFAULT_MAX_FRAME_SIZE)
|
118
|
+
@local_max_frame_size = local_max_frame_size
|
119
|
+
@remote_max_frame_size = remote_max_frame_size
|
109
120
|
end
|
110
121
|
|
111
122
|
# Generates common 9-byte frame header.
|
112
123
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
|
113
124
|
#
|
114
125
|
# @param frame [Hash]
|
126
|
+
# @param buffer [String] buffer to pack bytes into
|
115
127
|
# @return [String]
|
116
|
-
def common_header(frame)
|
117
|
-
header = []
|
118
|
-
|
128
|
+
def common_header(frame, buffer:)
|
119
129
|
raise CompressionError, "Invalid frame type (#{frame[:type]})" unless FRAME_TYPES[frame[:type]]
|
120
130
|
|
121
|
-
raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @
|
131
|
+
raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @remote_max_frame_size
|
122
132
|
|
123
|
-
raise CompressionError, "Frame size is invalid: #{frame[:length]}" if
|
133
|
+
raise CompressionError, "Frame size is invalid: #{frame[:length]}" if frame[:length] < 0
|
124
134
|
|
125
135
|
raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID
|
126
136
|
|
@@ -128,32 +138,33 @@ module HTTP2
|
|
128
138
|
raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
|
129
139
|
end
|
130
140
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
141
|
+
pack([
|
142
|
+
(frame[:length] >> FRAME_LENGTH_HISHIFT),
|
143
|
+
(frame[:length] & FRAME_LENGTH_LOMASK),
|
144
|
+
FRAME_TYPES[frame[:type]],
|
145
|
+
frame[:flags].reduce(0) do |acc, f|
|
146
|
+
position = FRAME_FLAGS[frame[:type]][f]
|
147
|
+
raise CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}" unless position
|
148
|
+
|
149
|
+
acc | (1 << position)
|
150
|
+
end,
|
151
|
+
frame[:stream]
|
152
|
+
], HEADERPACK, buffer: buffer, offset: 0) # 8+16,8,8,32
|
143
153
|
end
|
144
154
|
|
145
155
|
# Decodes common 9-byte header.
|
146
156
|
#
|
147
157
|
# @param buf [Buffer]
|
158
|
+
# @return [Hash] the corresponding frame
|
148
159
|
def read_common_header(buf)
|
149
160
|
frame = {}
|
150
|
-
len_hi, len_lo, type, flags, stream = buf.
|
161
|
+
len_hi, len_lo, type, flags, stream = buf.byteslice(0, 9).unpack(HEADERPACK)
|
151
162
|
|
152
163
|
frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
|
153
164
|
frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
|
154
165
|
if frame[:type]
|
155
166
|
frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
|
156
|
-
acc << name if
|
167
|
+
acc << name if flags.anybits?((1 << pos))
|
157
168
|
end
|
158
169
|
end
|
159
170
|
|
@@ -166,7 +177,7 @@ module HTTP2
|
|
166
177
|
#
|
167
178
|
# @param frame [Hash]
|
168
179
|
def generate(frame)
|
169
|
-
bytes =
|
180
|
+
bytes = "".b
|
170
181
|
length = 0
|
171
182
|
|
172
183
|
frame[:flags] ||= []
|
@@ -175,11 +186,12 @@ module HTTP2
|
|
175
186
|
case frame[:type]
|
176
187
|
when :data
|
177
188
|
bytes << frame[:payload]
|
189
|
+
bytes.force_encoding(Encoding::BINARY)
|
178
190
|
length += frame[:payload].bytesize
|
179
191
|
|
180
192
|
when :headers
|
181
|
-
if frame[:weight] || frame[:
|
182
|
-
unless frame[:weight] && frame[:
|
193
|
+
if frame[:weight] || frame[:dependency] || !frame[:exclusive].nil?
|
194
|
+
unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
|
183
195
|
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
184
196
|
end
|
185
197
|
|
@@ -187,8 +199,8 @@ module HTTP2
|
|
187
199
|
end
|
188
200
|
|
189
201
|
if frame[:flags].include? :priority
|
190
|
-
|
191
|
-
|
202
|
+
pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
|
203
|
+
pack([frame[:weight] - 1], UINT8, buffer: bytes)
|
192
204
|
length += 5
|
193
205
|
end
|
194
206
|
|
@@ -196,23 +208,23 @@ module HTTP2
|
|
196
208
|
length += frame[:payload].bytesize
|
197
209
|
|
198
210
|
when :priority
|
199
|
-
unless frame[:weight] && frame[:
|
211
|
+
unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
|
200
212
|
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
201
213
|
end
|
202
214
|
|
203
|
-
|
204
|
-
|
215
|
+
pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
|
216
|
+
pack([frame[:weight] - 1], UINT8, buffer: bytes)
|
205
217
|
length += 5
|
206
218
|
|
207
219
|
when :rst_stream
|
208
|
-
|
220
|
+
pack_error(frame[:error], buffer: bytes)
|
209
221
|
length += 4
|
210
222
|
|
211
223
|
when :settings
|
212
224
|
raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
213
225
|
|
214
226
|
frame[:payload].each do |(k, v)|
|
215
|
-
if k.is_a? Integer
|
227
|
+
if k.is_a? Integer # rubocop:disable Style/GuardClause
|
216
228
|
DEFINED_SETTINGS.value?(k) || next
|
217
229
|
else
|
218
230
|
k = DEFINED_SETTINGS[k]
|
@@ -220,27 +232,25 @@ module HTTP2
|
|
220
232
|
raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
|
221
233
|
end
|
222
234
|
|
223
|
-
|
224
|
-
|
235
|
+
pack([k], UINT16, buffer: bytes)
|
236
|
+
pack([v], UINT32, buffer: bytes)
|
225
237
|
length += 6
|
226
238
|
end
|
227
239
|
|
228
240
|
when :push_promise
|
229
|
-
|
241
|
+
pack([frame[:promise_stream] & RBIT], UINT32, buffer: bytes)
|
230
242
|
bytes << frame[:payload]
|
231
243
|
length += 4 + frame[:payload].bytesize
|
232
244
|
|
233
245
|
when :ping
|
234
|
-
if frame[:payload].bytesize != 8
|
235
|
-
raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
|
236
|
-
end
|
246
|
+
raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)" if frame[:payload].bytesize != 8
|
237
247
|
|
238
248
|
bytes << frame[:payload]
|
239
249
|
length += 8
|
240
250
|
|
241
251
|
when :goaway
|
242
|
-
|
243
|
-
|
252
|
+
pack([frame[:last_stream] & RBIT], UINT32, buffer: bytes)
|
253
|
+
pack_error(frame[:error], buffer: bytes)
|
244
254
|
length += 8
|
245
255
|
|
246
256
|
if frame[:payload]
|
@@ -249,7 +259,7 @@ module HTTP2
|
|
249
259
|
end
|
250
260
|
|
251
261
|
when :window_update
|
252
|
-
|
262
|
+
pack([frame[:increment] & RBIT], UINT32, buffer: bytes)
|
253
263
|
length += 4
|
254
264
|
|
255
265
|
when :continuation
|
@@ -257,32 +267,40 @@ module HTTP2
|
|
257
267
|
length += frame[:payload].bytesize
|
258
268
|
|
259
269
|
when :altsvc
|
260
|
-
|
270
|
+
pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes)
|
261
271
|
length += 6
|
262
272
|
if frame[:proto]
|
263
|
-
raise CompressionError,
|
273
|
+
raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
|
264
274
|
|
265
|
-
|
266
|
-
bytes << frame[:proto]
|
275
|
+
pack([frame[:proto].bytesize], UINT8, buffer: bytes)
|
276
|
+
bytes << frame[:proto]
|
267
277
|
length += 1 + frame[:proto].bytesize
|
268
278
|
else
|
269
|
-
|
279
|
+
pack([0], UINT8, buffer: bytes)
|
270
280
|
length += 1
|
271
281
|
end
|
272
282
|
if frame[:host]
|
273
|
-
raise CompressionError,
|
283
|
+
raise CompressionError, "Host too long" if frame[:host].bytesize > 255
|
274
284
|
|
275
|
-
|
276
|
-
bytes << frame[:host]
|
285
|
+
pack([frame[:host].bytesize], UINT8, buffer: bytes)
|
286
|
+
bytes << frame[:host]
|
277
287
|
length += 1 + frame[:host].bytesize
|
278
288
|
else
|
279
|
-
|
289
|
+
pack([0], UINT8, buffer: bytes)
|
280
290
|
length += 1
|
281
291
|
end
|
282
292
|
if frame[:origin]
|
283
293
|
bytes << frame[:origin]
|
284
294
|
length += frame[:origin].bytesize
|
285
295
|
end
|
296
|
+
|
297
|
+
when :origin
|
298
|
+
frame[:payload].each do |origin|
|
299
|
+
pack([origin.bytesize], UINT16, buffer: bytes)
|
300
|
+
bytes << origin
|
301
|
+
length += 2 + origin.bytesize
|
302
|
+
end
|
303
|
+
|
286
304
|
end
|
287
305
|
|
288
306
|
# Process padding.
|
@@ -295,12 +313,12 @@ module HTTP2
|
|
295
313
|
|
296
314
|
padlen = frame[:padding]
|
297
315
|
|
298
|
-
if padlen <= 0 || padlen > 256 || padlen + length > @
|
316
|
+
if padlen <= 0 || padlen > 256 || padlen + length > @remote_max_frame_size
|
299
317
|
raise CompressionError, "Invalid padding #{padlen}"
|
300
318
|
end
|
301
319
|
|
302
320
|
length += padlen
|
303
|
-
|
321
|
+
pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
|
304
322
|
frame[:flags] << :padded
|
305
323
|
|
306
324
|
# Padding: Padding octets that contain no application semantic value.
|
@@ -310,7 +328,7 @@ module HTTP2
|
|
310
328
|
end
|
311
329
|
|
312
330
|
frame[:length] = length
|
313
|
-
|
331
|
+
common_header(frame, buffer: bytes)
|
314
332
|
end
|
315
333
|
|
316
334
|
# Decodes complete HTTP/2 frame from provided buffer. If the buffer
|
@@ -318,66 +336,74 @@ module HTTP2
|
|
318
336
|
#
|
319
337
|
# @param buf [Buffer]
|
320
338
|
def parse(buf)
|
321
|
-
return
|
339
|
+
return if buf.size < 9
|
322
340
|
|
323
341
|
frame = read_common_header(buf)
|
324
|
-
return
|
342
|
+
return if buf.size < 9 + frame[:length]
|
325
343
|
|
326
|
-
raise ProtocolError,
|
344
|
+
raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
|
327
345
|
|
328
|
-
buf
|
329
|
-
payload = buf
|
346
|
+
read_str(buf, 9)
|
347
|
+
payload = read_str(buf, frame[:length])
|
330
348
|
|
331
349
|
# Implementations MUST discard frames
|
332
350
|
# that have unknown or unsupported types.
|
333
351
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.5
|
334
|
-
return
|
352
|
+
return frame if frame[:type].nil?
|
335
353
|
|
336
354
|
# Process padding
|
337
355
|
padlen = 0
|
338
356
|
if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
339
357
|
padded = frame[:flags].include?(:padded)
|
340
358
|
if padded
|
341
|
-
padlen = payload
|
359
|
+
padlen = read_str(payload, 1).unpack1(UINT8)
|
342
360
|
frame[:padding] = padlen + 1
|
343
|
-
raise ProtocolError,
|
361
|
+
raise ProtocolError, "padding too long" if padlen > payload.bytesize
|
344
362
|
|
345
|
-
payload.
|
363
|
+
payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
|
346
364
|
frame[:length] -= frame[:padding]
|
347
365
|
frame[:flags].delete(:padded)
|
348
366
|
end
|
349
367
|
end
|
350
368
|
|
351
369
|
case frame[:type]
|
352
|
-
when :data
|
353
|
-
frame[:payload] = payload
|
370
|
+
when :data, :ping, :continuation
|
371
|
+
frame[:payload] = read_str(payload, frame[:length])
|
354
372
|
when :headers
|
355
373
|
if frame[:flags].include? :priority
|
356
|
-
e_sd = payload
|
357
|
-
frame[:
|
358
|
-
frame[:exclusive] = (
|
359
|
-
|
374
|
+
e_sd = read_uint32(payload)
|
375
|
+
frame[:dependency] = e_sd & RBIT
|
376
|
+
frame[:exclusive] = e_sd.anybits?(EBIT)
|
377
|
+
weight = payload.byteslice(0, 1).ord + 1
|
378
|
+
frame[:weight] = weight
|
379
|
+
payload = payload.byteslice(1..-1)
|
360
380
|
end
|
361
|
-
frame[:payload] = payload
|
381
|
+
frame[:payload] = read_str(payload, frame[:length])
|
362
382
|
when :priority
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
frame[:
|
383
|
+
raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
|
384
|
+
|
385
|
+
e_sd = read_uint32(payload)
|
386
|
+
frame[:dependency] = e_sd & RBIT
|
387
|
+
frame[:exclusive] = e_sd.anybits?(EBIT)
|
388
|
+
weight = payload.byteslice(0, 1).ord + 1
|
389
|
+
frame[:weight] = weight
|
390
|
+
payload = payload.byteslice(1..-1)
|
367
391
|
when :rst_stream
|
368
|
-
frame[:
|
392
|
+
raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
|
393
|
+
|
394
|
+
frame[:error] = unpack_error read_uint32(payload)
|
369
395
|
|
370
396
|
when :settings
|
371
397
|
# NOTE: frame[:length] might not match the number of frame[:payload]
|
372
398
|
# because unknown extensions are ignored.
|
373
399
|
frame[:payload] = []
|
374
|
-
raise ProtocolError,
|
400
|
+
raise ProtocolError, "Invalid settings payload length" unless (frame[:length] % 6).zero?
|
375
401
|
|
376
402
|
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
377
403
|
|
378
404
|
(frame[:length] / 6).times do
|
379
|
-
id = payload
|
380
|
-
val = payload
|
405
|
+
id = read_str(payload, 2).unpack1(UINT16)
|
406
|
+
val = read_uint32(payload)
|
381
407
|
|
382
408
|
# Unsupported or unrecognized settings MUST be ignored.
|
383
409
|
# Here we send it along.
|
@@ -385,30 +411,42 @@ module HTTP2
|
|
385
411
|
frame[:payload] << [name, val] if name
|
386
412
|
end
|
387
413
|
when :push_promise
|
388
|
-
frame[:promise_stream] = payload
|
389
|
-
frame[:payload] = payload
|
390
|
-
when :ping
|
391
|
-
frame[:payload] = payload.read(frame[:length])
|
414
|
+
frame[:promise_stream] = read_uint32(payload) & RBIT
|
415
|
+
frame[:payload] = read_str(payload, frame[:length])
|
392
416
|
when :goaway
|
393
|
-
frame[:last_stream] = payload
|
394
|
-
frame[:error] = unpack_error payload
|
417
|
+
frame[:last_stream] = read_uint32(payload) & RBIT
|
418
|
+
frame[:error] = unpack_error read_uint32(payload)
|
395
419
|
|
396
420
|
size = frame[:length] - 8 # for last_stream and error
|
397
|
-
frame[:payload] = payload
|
421
|
+
frame[:payload] = read_str(payload, size) if size > 0
|
398
422
|
when :window_update
|
399
|
-
frame[:
|
400
|
-
|
401
|
-
|
423
|
+
if frame[:length] % 4 != 0
|
424
|
+
raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)"
|
425
|
+
end
|
426
|
+
|
427
|
+
frame[:increment] = read_uint32(payload) & RBIT
|
402
428
|
when :altsvc
|
403
|
-
frame[:max_age], frame[:port] = payload
|
429
|
+
frame[:max_age], frame[:port] = read_str(payload, 6).unpack(UINT32 + UINT16)
|
404
430
|
|
405
|
-
len = payload.
|
406
|
-
|
431
|
+
len = payload.byteslice(0, 1).ord
|
432
|
+
payload = payload.byteslice(1..-1)
|
433
|
+
frame[:proto] = read_str(payload, len) if len > 0
|
407
434
|
|
408
|
-
len = payload.
|
409
|
-
|
435
|
+
len = payload.byteslice(0, 1).ord
|
436
|
+
payload = payload.byteslice(1..-1)
|
437
|
+
frame[:host] = read_str(payload, len) if len > 0
|
438
|
+
|
439
|
+
frame[:origin] = read_str(payload, payload.size) unless payload.empty?
|
440
|
+
|
441
|
+
when :origin
|
442
|
+
origins = []
|
443
|
+
|
444
|
+
until payload.empty?
|
445
|
+
len = read_str(payload, 2).unpack1(UINT16)
|
446
|
+
origins << read_str(payload, len)
|
447
|
+
end
|
410
448
|
|
411
|
-
frame[:
|
449
|
+
frame[:payload] = origins
|
412
450
|
# else # Unknown frame type is explicitly allowed
|
413
451
|
end
|
414
452
|
|
@@ -417,19 +455,18 @@ module HTTP2
|
|
417
455
|
|
418
456
|
private
|
419
457
|
|
420
|
-
def pack_error(
|
421
|
-
unless
|
422
|
-
|
458
|
+
def pack_error(error, buffer:)
|
459
|
+
unless error.is_a? Integer
|
460
|
+
error = DEFINED_ERRORS[error]
|
423
461
|
|
424
|
-
|
462
|
+
raise CompressionError, "Unknown error ID for #{error}" unless error
|
425
463
|
end
|
426
464
|
|
427
|
-
[
|
465
|
+
pack([error], UINT32, buffer: buffer)
|
428
466
|
end
|
429
467
|
|
430
|
-
def unpack_error(
|
431
|
-
|
432
|
-
name || error
|
468
|
+
def unpack_error(error)
|
469
|
+
DEFINED_ERRORS.key(error) || error
|
433
470
|
end
|
434
471
|
end
|
435
472
|
end
|