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