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