http-2 0.6.3 → 0.7.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/.coveralls.yml +1 -0
- data/.gitmodules +3 -0
- data/Gemfile +5 -0
- data/README.md +5 -4
- data/Rakefile +1 -0
- data/example/client.rb +20 -5
- data/example/helper.rb +1 -1
- data/example/keys/mycert.pem +21 -22
- data/example/keys/mykey.pem +25 -25
- data/example/server.rb +10 -3
- data/http-2.gemspec +1 -1
- data/lib/http/2.rb +2 -0
- data/lib/http/2/client.rb +16 -10
- data/lib/http/2/compressor.rb +346 -286
- data/lib/http/2/connection.rb +254 -95
- data/lib/http/2/error.rb +0 -6
- data/lib/http/2/flow_buffer.rb +12 -10
- data/lib/http/2/framer.rb +203 -57
- data/lib/http/2/huffman.rb +332 -0
- data/lib/http/2/huffman_statemachine.rb +272 -0
- data/lib/http/2/server.rb +5 -4
- data/lib/http/2/stream.rb +72 -35
- data/lib/http/2/version.rb +1 -1
- data/lib/tasks/generate_huffman_table.rb +160 -0
- data/spec/client_spec.rb +3 -3
- data/spec/compressor_spec.rb +422 -281
- data/spec/connection_spec.rb +236 -56
- data/spec/framer_spec.rb +213 -45
- data/spec/helper.rb +42 -15
- data/spec/hpack_test_spec.rb +83 -0
- data/spec/huffman_spec.rb +68 -0
- data/spec/server_spec.rb +7 -6
- data/spec/stream_spec.rb +81 -54
- metadata +21 -11
data/lib/http/2/error.rb
CHANGED
@@ -18,12 +18,6 @@ module HTTP2
|
|
18
18
|
# @see ProtocolError
|
19
19
|
class CompressionError < ProtocolError; end
|
20
20
|
|
21
|
-
# Raised on invalid reference for current compression context: the
|
22
|
-
# client and server contexts are out of sync.
|
23
|
-
#
|
24
|
-
# @see ProtocolError
|
25
|
-
class HeaderException < ProtocolError; end
|
26
|
-
|
27
21
|
# Raised on invalid flow control frame or command.
|
28
22
|
#
|
29
23
|
# @see ProtocolError
|
data/lib/http/2/flow_buffer.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
module HTTP2
|
2
2
|
|
3
|
-
# Maximum size of a DATA payload (16383 bytes, ~16K).
|
4
|
-
MAX_FRAME_SIZE = 2**14-1
|
5
|
-
|
6
3
|
# Implementation of stream and connection DATA flow control: frames may
|
7
4
|
# be split and / or may be buffered based on current flow control window.
|
8
5
|
#
|
@@ -30,16 +27,21 @@ module HTTP2
|
|
30
27
|
def send_data(frame = nil, encode = false)
|
31
28
|
@send_buffer.push frame if !frame.nil?
|
32
29
|
|
33
|
-
|
30
|
+
# FIXME: Frames with zero length with the END_STREAM flag set (that
|
31
|
+
# is, an empty DATA frame) MAY be sent if there is no available space
|
32
|
+
# in either flow control window.
|
33
|
+
while @remote_window > 0 && !@send_buffer.empty? do
|
34
34
|
frame = @send_buffer.shift
|
35
35
|
|
36
36
|
sent, frame_size = 0, frame[:payload].bytesize
|
37
37
|
|
38
|
-
if frame_size > @
|
38
|
+
if frame_size > @remote_window
|
39
39
|
payload = frame.delete(:payload)
|
40
40
|
chunk = frame.dup
|
41
41
|
|
42
|
-
frame
|
42
|
+
# Split frame so that it fits in the window
|
43
|
+
# TODO: consider padding!
|
44
|
+
frame[:payload] = payload.slice!(0, @remote_window)
|
43
45
|
chunk[:length] = payload.bytesize
|
44
46
|
chunk[:payload] = payload
|
45
47
|
|
@@ -49,14 +51,14 @@ module HTTP2
|
|
49
51
|
end
|
50
52
|
|
51
53
|
@send_buffer.unshift chunk
|
52
|
-
sent = @
|
54
|
+
sent = @remote_window
|
53
55
|
else
|
54
56
|
sent = frame_size
|
55
57
|
end
|
56
58
|
|
57
|
-
|
58
|
-
emit(:frame,
|
59
|
-
@
|
59
|
+
frames = encode ? encode(frame) : [frame]
|
60
|
+
frames.each {|f| emit(:frame, f) }
|
61
|
+
@remote_window -= sent
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
data/lib/http/2/framer.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module HTTP2
|
2
2
|
|
3
|
-
# Performs encoding, decoding, and validation of binary HTTP
|
3
|
+
# Performs encoding, decoding, and validation of binary HTTP/2 frames.
|
4
4
|
#
|
5
5
|
class Framer
|
6
6
|
include Error
|
7
7
|
|
8
|
-
#
|
9
|
-
|
8
|
+
# Default value of max frame size (16384 bytes)
|
9
|
+
DEFAULT_MAX_FRAME_SIZE = 2**14
|
10
|
+
|
11
|
+
# Current maximum frame size
|
12
|
+
attr_accessor :max_frame_size
|
10
13
|
|
11
14
|
# Maximum stream ID (2^31)
|
12
15
|
MAX_STREAM_ID = 0x7fffffff
|
@@ -14,7 +17,7 @@ module HTTP2
|
|
14
17
|
# Maximum window increment value (2^31)
|
15
18
|
MAX_WINDOWINC = 0x7fffffff
|
16
19
|
|
17
|
-
# HTTP
|
20
|
+
# HTTP/2 frame type mapping as defined by the spec
|
18
21
|
FRAME_TYPES = {
|
19
22
|
data: 0x0,
|
20
23
|
headers: 0x1,
|
@@ -24,36 +27,45 @@ module HTTP2
|
|
24
27
|
push_promise: 0x5,
|
25
28
|
ping: 0x6,
|
26
29
|
goaway: 0x7,
|
27
|
-
window_update:
|
28
|
-
continuation:
|
30
|
+
window_update: 0x8,
|
31
|
+
continuation: 0x9,
|
32
|
+
altsvc: 0xa,
|
29
33
|
}
|
30
34
|
|
35
|
+
FRAME_TYPES_WITH_PADDING = [ :data, :headers, :push_promise ]
|
36
|
+
|
31
37
|
# Per frame flags as defined by the spec
|
32
38
|
FRAME_FLAGS = {
|
33
39
|
data: {
|
34
|
-
end_stream: 0,
|
40
|
+
end_stream: 0,
|
41
|
+
padded: 3, compressed: 5
|
35
42
|
},
|
36
43
|
headers: {
|
37
|
-
end_stream: 0,
|
38
|
-
|
44
|
+
end_stream: 0, end_headers: 2,
|
45
|
+
padded: 3, priority: 5,
|
39
46
|
},
|
40
47
|
priority: {},
|
41
48
|
rst_stream: {},
|
42
|
-
settings: {},
|
43
|
-
push_promise: {
|
44
|
-
|
49
|
+
settings: { ack: 0 },
|
50
|
+
push_promise: {
|
51
|
+
end_headers: 2,
|
52
|
+
padded: 3,
|
53
|
+
},
|
54
|
+
ping: { ack: 0 },
|
45
55
|
goaway: {},
|
46
56
|
window_update:{},
|
47
|
-
continuation: {
|
48
|
-
|
49
|
-
}
|
57
|
+
continuation: { end_headers: 2 },
|
58
|
+
altsvc: {},
|
50
59
|
}
|
51
60
|
|
52
61
|
# Default settings as defined by the spec
|
53
62
|
DEFINED_SETTINGS = {
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
settings_header_table_size: 1,
|
64
|
+
settings_enable_push: 2,
|
65
|
+
settings_max_concurrent_streams: 3,
|
66
|
+
settings_initial_window_size: 4,
|
67
|
+
settings_max_frame_size: 5,
|
68
|
+
settings_max_header_list_size: 6,
|
57
69
|
}
|
58
70
|
|
59
71
|
# Default error types as defined by the spec
|
@@ -62,22 +74,38 @@ module HTTP2
|
|
62
74
|
protocol_error: 1,
|
63
75
|
internal_error: 2,
|
64
76
|
flow_control_error: 3,
|
77
|
+
settings_timeout: 4,
|
65
78
|
stream_closed: 5,
|
66
|
-
|
79
|
+
frame_size_error: 6,
|
67
80
|
refused_stream: 7,
|
68
81
|
cancel: 8,
|
69
|
-
compression_error: 9
|
82
|
+
compression_error: 9,
|
83
|
+
connect_error: 10,
|
84
|
+
enhance_your_calm: 11,
|
85
|
+
inadequate_security: 12,
|
70
86
|
}
|
71
87
|
|
72
88
|
RBIT = 0x7fffffff
|
73
89
|
RBYTE = 0x0fffffff
|
74
|
-
|
75
|
-
UINT32 = "N"
|
76
|
-
|
77
|
-
|
90
|
+
EBIT = 0x80000000
|
91
|
+
UINT32 = "N".freeze
|
92
|
+
UINT16 = "n".freeze
|
93
|
+
UINT8 = "C".freeze
|
94
|
+
HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
|
95
|
+
FRAME_LENGTH_HISHIFT = 16
|
96
|
+
FRAME_LENGTH_LOMASK = 0xFFFF
|
97
|
+
BINARY = 'binary'.freeze
|
98
|
+
|
99
|
+
private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :UINT32, :UINT16, :UINT8, :BINARY
|
100
|
+
|
101
|
+
# Initializes new framer object.
|
102
|
+
#
|
103
|
+
def initialize
|
104
|
+
@max_frame_size = DEFAULT_MAX_FRAME_SIZE
|
105
|
+
end
|
78
106
|
|
79
|
-
# Generates common
|
80
|
-
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-
|
107
|
+
# Generates common 9-byte frame header.
|
108
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-4.1
|
81
109
|
#
|
82
110
|
# @param frame [Hash]
|
83
111
|
# @return [String]
|
@@ -88,10 +116,14 @@ module HTTP2
|
|
88
116
|
raise CompressionError.new("Invalid frame type (#{frame[:type]})")
|
89
117
|
end
|
90
118
|
|
91
|
-
if frame[:length] >
|
119
|
+
if frame[:length] > @max_frame_size
|
92
120
|
raise CompressionError.new("Frame size is too large: #{frame[:length]}")
|
93
121
|
end
|
94
122
|
|
123
|
+
if frame[:length] < 0
|
124
|
+
raise CompressionError.new("Frame size is invalid: #{frame[:length]}")
|
125
|
+
end
|
126
|
+
|
95
127
|
if frame[:stream] > MAX_STREAM_ID
|
96
128
|
raise CompressionError.new("Stream ID (#{frame[:stream]}) is too large")
|
97
129
|
end
|
@@ -100,7 +132,8 @@ module HTTP2
|
|
100
132
|
raise CompressionError.new("Window increment (#{frame[:increment]}) is too large")
|
101
133
|
end
|
102
134
|
|
103
|
-
header << frame[:length]
|
135
|
+
header << (frame[:length] >> FRAME_LENGTH_HISHIFT)
|
136
|
+
header << (frame[:length] & FRAME_LENGTH_LOMASK)
|
104
137
|
header << FRAME_TYPES[frame[:type]]
|
105
138
|
header << frame[:flags].reduce(0) do |acc, f|
|
106
139
|
position = FRAME_FLAGS[frame[:type]][f]
|
@@ -113,27 +146,30 @@ module HTTP2
|
|
113
146
|
end
|
114
147
|
|
115
148
|
header << frame[:stream]
|
116
|
-
header.pack(HEADERPACK) # 16,8,8,32
|
149
|
+
header.pack(HEADERPACK) # 8+16,8,8,32
|
117
150
|
end
|
118
151
|
|
119
|
-
# Decodes common
|
152
|
+
# Decodes common 9-byte header.
|
120
153
|
#
|
121
154
|
# @param buf [Buffer]
|
122
155
|
def readCommonHeader(buf)
|
123
156
|
frame = {}
|
124
|
-
|
157
|
+
len_hi, len_lo, type, flags, stream = buf.slice(0,9).unpack(HEADERPACK)
|
125
158
|
|
159
|
+
frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
|
126
160
|
frame[:type], _ = FRAME_TYPES.select { |t,pos| type == pos }.first
|
127
|
-
|
128
|
-
|
129
|
-
|
161
|
+
if frame[:type]
|
162
|
+
frame[:flags] = FRAME_FLAGS[frame[:type]].reduce([]) do |acc, (name, pos)|
|
163
|
+
acc << name if (flags & (1 << pos)) > 0
|
164
|
+
acc
|
165
|
+
end
|
130
166
|
end
|
131
167
|
|
132
168
|
frame[:stream] = stream & RBIT
|
133
169
|
frame
|
134
170
|
end
|
135
171
|
|
136
|
-
# Generates encoded HTTP
|
172
|
+
# Generates encoded HTTP/2 frame.
|
137
173
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2
|
138
174
|
#
|
139
175
|
# @param frame [Hash]
|
@@ -150,21 +186,31 @@ module HTTP2
|
|
150
186
|
length += frame[:payload].bytesize
|
151
187
|
|
152
188
|
when :headers
|
153
|
-
if frame[:
|
189
|
+
if frame[:weight] || frame[:stream_dependency] || !frame[:exclusive].nil?
|
190
|
+
unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
|
191
|
+
raise CompressionError.new("Must specify all of priority parameters for #{frame[:type]}")
|
192
|
+
end
|
154
193
|
frame[:flags] += [:priority] if !frame[:flags].include? :priority
|
155
194
|
end
|
156
195
|
|
157
196
|
if frame[:flags].include? :priority
|
158
|
-
bytes
|
159
|
-
|
197
|
+
bytes << [(frame[:exclusive] ? EBIT : 0) |
|
198
|
+
(frame[:stream_dependency] & RBIT)].pack(UINT32)
|
199
|
+
bytes << [frame[:weight] - 1].pack(UINT8)
|
200
|
+
length += 5
|
160
201
|
end
|
161
202
|
|
162
203
|
bytes << frame[:payload]
|
163
204
|
length += frame[:payload].bytesize
|
164
205
|
|
165
206
|
when :priority
|
166
|
-
|
167
|
-
|
207
|
+
unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
|
208
|
+
raise CompressionError.new("Must specify all of priority parameters for #{frame[:type]}")
|
209
|
+
end
|
210
|
+
bytes << [(frame[:exclusive] ? EBIT : 0) |
|
211
|
+
(frame[:stream_dependency] & RBIT)].pack(UINT32)
|
212
|
+
bytes << [frame[:weight] - 1].pack(UINT8)
|
213
|
+
length += 5
|
168
214
|
|
169
215
|
when :rst_stream
|
170
216
|
bytes << pack_error(frame[:error])
|
@@ -176,7 +222,9 @@ module HTTP2
|
|
176
222
|
end
|
177
223
|
|
178
224
|
frame[:payload].each do |(k,v)|
|
179
|
-
if
|
225
|
+
if k.is_a? Integer
|
226
|
+
DEFINED_SETTINGS.has_value?(k) or next
|
227
|
+
else
|
180
228
|
k = DEFINED_SETTINGS[k]
|
181
229
|
|
182
230
|
if k.nil?
|
@@ -184,9 +232,9 @@ module HTTP2
|
|
184
232
|
end
|
185
233
|
end
|
186
234
|
|
187
|
-
bytes << [k
|
235
|
+
bytes << [k].pack(UINT16)
|
188
236
|
bytes << [v].pack(UINT32)
|
189
|
-
length +=
|
237
|
+
length += 6
|
190
238
|
end
|
191
239
|
|
192
240
|
when :push_promise
|
@@ -219,46 +267,130 @@ module HTTP2
|
|
219
267
|
when :continuation
|
220
268
|
bytes << frame[:payload]
|
221
269
|
length += frame[:payload].bytesize
|
270
|
+
|
271
|
+
when :altsvc
|
272
|
+
bytes << [frame[:max_age], frame[:port]].pack(UINT32 + UINT16)
|
273
|
+
length += 6
|
274
|
+
if frame[:proto]
|
275
|
+
frame[:proto].bytesize > 255 and raise CompressionError.new("Proto too long")
|
276
|
+
bytes << [frame[:proto].bytesize].pack(UINT8) << frame[:proto].force_encoding(BINARY)
|
277
|
+
length += 1 + frame[:proto].bytesize
|
278
|
+
else
|
279
|
+
bytes << [0].pack(UINT8)
|
280
|
+
length += 1
|
281
|
+
end
|
282
|
+
if frame[:host]
|
283
|
+
frame[:host].bytesize > 255 and raise CompressionError.new("Host too long")
|
284
|
+
bytes << [frame[:host].bytesize].pack(UINT8) << frame[:host].force_encoding(BINARY)
|
285
|
+
length += 1 + frame[:host].bytesize
|
286
|
+
else
|
287
|
+
bytes << [0].pack(UINT8)
|
288
|
+
length += 1
|
289
|
+
end
|
290
|
+
if frame[:origin]
|
291
|
+
bytes << frame[:origin]
|
292
|
+
length += frame[:origin].bytesize
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Process padding.
|
297
|
+
# frame[:padding] gives number of extra octets to be added.
|
298
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-12#section-6.1
|
299
|
+
if frame[:padding]
|
300
|
+
unless FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
301
|
+
raise CompressionError.new("Invalid padding flag for #{frame[:type]}")
|
302
|
+
end
|
303
|
+
|
304
|
+
padlen = frame[:padding]
|
305
|
+
|
306
|
+
if padlen <= 0 || padlen > 256 || padlen + length > @max_frame_size
|
307
|
+
raise CompressionError.new("Invalid padding #{padlen}")
|
308
|
+
end
|
309
|
+
|
310
|
+
length += padlen
|
311
|
+
bytes.prepend([padlen -= 1].pack(UINT8))
|
312
|
+
frame[:flags] << :padded
|
313
|
+
|
314
|
+
# Padding: Padding octets that contain no application semantic value.
|
315
|
+
# Padding octets MUST be set to zero when sending and ignored when
|
316
|
+
# receiving.
|
317
|
+
bytes << "\0" * padlen
|
222
318
|
end
|
223
319
|
|
224
320
|
frame[:length] = length
|
225
321
|
bytes.prepend(commonHeader(frame))
|
226
322
|
end
|
227
323
|
|
228
|
-
# Decodes complete HTTP
|
324
|
+
# Decodes complete HTTP/2 frame from provided buffer. If the buffer
|
229
325
|
# does not contain enough data, no further work is performed.
|
230
326
|
#
|
231
327
|
# @param buf [Buffer]
|
232
328
|
def parse(buf)
|
233
|
-
return nil if buf.size <
|
329
|
+
return nil if buf.size < 9
|
234
330
|
frame = readCommonHeader(buf)
|
235
|
-
return nil if buf.size <
|
331
|
+
return nil if buf.size < 9 + frame[:length]
|
236
332
|
|
237
|
-
buf.read(
|
333
|
+
buf.read(9)
|
238
334
|
payload = buf.read(frame[:length])
|
239
335
|
|
336
|
+
# Implementations MUST discard frames
|
337
|
+
# that have unknown or unsupported types.
|
338
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.5
|
339
|
+
return nil if frame[:type].nil?
|
340
|
+
|
341
|
+
# Process padding
|
342
|
+
padlen = 0
|
343
|
+
if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
344
|
+
padded = frame[:flags].include?(:padded)
|
345
|
+
if padded
|
346
|
+
padlen = payload.read(1).unpack(UINT8).first
|
347
|
+
frame[:padding] = padlen + 1
|
348
|
+
padlen > payload.bytesize and raise ProtocolError.new("padding too long")
|
349
|
+
padlen > 0 and payload.slice!(-padlen,padlen)
|
350
|
+
frame[:length] -= frame[:padding]
|
351
|
+
frame[:flags].delete(:padded)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
240
355
|
case frame[:type]
|
241
356
|
when :data
|
242
357
|
frame[:payload] = payload.read(frame[:length])
|
243
358
|
when :headers
|
244
359
|
if frame[:flags].include? :priority
|
245
|
-
|
360
|
+
e_sd = payload.read_uint32
|
361
|
+
frame[:stream_dependency] = e_sd & RBIT
|
362
|
+
frame[:exclusive] = (e_sd & EBIT) != 0
|
363
|
+
frame[:weight] = payload.getbyte + 1
|
246
364
|
end
|
247
365
|
frame[:payload] = payload.read(frame[:length])
|
248
366
|
when :priority
|
249
|
-
|
367
|
+
e_sd = payload.read_uint32
|
368
|
+
frame[:stream_dependency] = e_sd & RBIT
|
369
|
+
frame[:exclusive] = (e_sd & EBIT) != 0
|
370
|
+
frame[:weight] = payload.getbyte + 1
|
250
371
|
when :rst_stream
|
251
372
|
frame[:error] = unpack_error payload.read_uint32
|
252
373
|
|
253
374
|
when :settings
|
254
|
-
frame[:
|
255
|
-
|
256
|
-
|
375
|
+
# NOTE: frame[:length] might not match the number of frame[:payload]
|
376
|
+
# because unknown extensions are ignored.
|
377
|
+
frame[:payload] = []
|
378
|
+
unless frame[:length] % 6 == 0
|
379
|
+
raise ProtocolError.new("Invalid settings payload length")
|
380
|
+
end
|
381
|
+
|
382
|
+
if frame[:stream] != 0
|
383
|
+
raise ProtocolError.new("Invalid stream ID (#{frame[:stream]})")
|
384
|
+
end
|
385
|
+
|
386
|
+
(frame[:length] / 6).times do
|
387
|
+
id = payload.read(2).unpack(UINT16).first
|
257
388
|
val = payload.read_uint32
|
258
389
|
|
259
390
|
# Unsupported or unrecognized settings MUST be ignored.
|
391
|
+
# Here we send it along.
|
260
392
|
name, _ = DEFINED_SETTINGS.select { |name, v| v == id }.first
|
261
|
-
frame[:payload][name
|
393
|
+
frame[:payload] << [name, val] if name
|
262
394
|
end
|
263
395
|
when :push_promise
|
264
396
|
frame[:promise_stream] = payload.read_uint32 & RBIT
|
@@ -269,12 +401,26 @@ module HTTP2
|
|
269
401
|
frame[:last_stream] = payload.read_uint32 & RBIT
|
270
402
|
frame[:error] = unpack_error payload.read_uint32
|
271
403
|
|
272
|
-
size = frame[:length] - 8
|
404
|
+
size = frame[:length] - 8 # for last_stream and error
|
273
405
|
frame[:payload] = payload.read(size) if size > 0
|
274
406
|
when :window_update
|
275
407
|
frame[:increment] = payload.read_uint32 & RBIT
|
276
408
|
when :continuation
|
277
409
|
frame[:payload] = payload.read(frame[:length])
|
410
|
+
when :altsvc
|
411
|
+
frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
|
412
|
+
|
413
|
+
len = payload.getbyte
|
414
|
+
len > 0 and frame[:proto] = payload.read(len)
|
415
|
+
|
416
|
+
len = payload.getbyte
|
417
|
+
len > 0 and frame[:host] = payload.read(len)
|
418
|
+
|
419
|
+
if payload.size > 0
|
420
|
+
frame[:origin] = payload.read(payload.size)
|
421
|
+
end
|
422
|
+
else
|
423
|
+
# Unknown frame type is explicitly allowed
|
278
424
|
end
|
279
425
|
|
280
426
|
frame
|
@@ -284,11 +430,11 @@ module HTTP2
|
|
284
430
|
|
285
431
|
def pack_error(e)
|
286
432
|
if !e.is_a? Integer
|
287
|
-
|
288
|
-
|
289
|
-
if e.nil?
|
433
|
+
if DEFINED_ERRORS[e].nil?
|
290
434
|
raise CompressionError.new("Unknown error ID for #{e}")
|
291
435
|
end
|
436
|
+
|
437
|
+
e = DEFINED_ERRORS[e]
|
292
438
|
end
|
293
439
|
|
294
440
|
[e].pack(UINT32)
|