http-2 0.11.0 → 1.0.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/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
|