http-2 0.11.0 → 0.12.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 +0 -2
- data/lib/http/2/buffer.rb +6 -4
- data/lib/http/2/client.rb +5 -1
- data/lib/http/2/compressor.rb +42 -34
- data/lib/http/2/connection.rb +72 -86
- data/lib/http/2/emitter.rb +4 -1
- data/lib/http/2/error.rb +2 -0
- data/lib/http/2/flow_buffer.rb +8 -3
- data/lib/http/2/framer.rb +83 -94
- data/lib/http/2/huffman.rb +19 -17
- data/lib/http/2/server.rb +9 -7
- data/lib/http/2/stream.rb +48 -48
- data/lib/http/2/version.rb +3 -1
- data/lib/http/2.rb +2 -0
- metadata +7 -60
- 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/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/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/emitter.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTP2
|
2
4
|
# Basic event emitter implementation with support for persistent and
|
3
5
|
# one-time event callbacks.
|
@@ -8,7 +10,8 @@ module HTTP2
|
|
8
10
|
# @param event [Symbol]
|
9
11
|
# @param block [Proc] callback function
|
10
12
|
def add_listener(event, &block)
|
11
|
-
|
13
|
+
raise ArgumentError, 'must provide callback' unless block_given?
|
14
|
+
|
12
15
|
listeners(event.to_sym).push block
|
13
16
|
end
|
14
17
|
alias on add_listener
|
data/lib/http/2/error.rb
CHANGED
data/lib/http/2/flow_buffer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTP2
|
2
4
|
# Implementation of stream and connection DATA flow control: frames may
|
3
5
|
# be split and / or may be buffered based on current flow control window.
|
@@ -24,7 +26,7 @@ module HTTP2
|
|
24
26
|
# current received window size + delta length is strictly larger than
|
25
27
|
# local window size, it throws a flow control error.
|
26
28
|
#
|
27
|
-
error(:flow_control_error) if @local_window
|
29
|
+
error(:flow_control_error) if @local_window.negative?
|
28
30
|
|
29
31
|
# Send WINDOW_UPDATE if the received window size goes over
|
30
32
|
# the local window size / 2.
|
@@ -41,6 +43,7 @@ module HTTP2
|
|
41
43
|
# until the receiver window is exhausted, after which he'll be
|
42
44
|
# waiting for the WINDOW_UPDATE frame.
|
43
45
|
return unless @local_window <= (window_max_size / 2)
|
46
|
+
|
44
47
|
window_update(window_max_size - @local_window)
|
45
48
|
end
|
46
49
|
|
@@ -59,10 +62,11 @@ module HTTP2
|
|
59
62
|
# FIXME: Frames with zero length with the END_STREAM flag set (that
|
60
63
|
# is, an empty DATA frame) MAY be sent if there is no available space
|
61
64
|
# in either flow control window.
|
62
|
-
while @remote_window
|
65
|
+
while @remote_window.positive? && !@send_buffer.empty?
|
63
66
|
frame = @send_buffer.shift
|
64
67
|
|
65
|
-
sent
|
68
|
+
sent = 0
|
69
|
+
frame_size = frame[:payload].bytesize
|
66
70
|
|
67
71
|
if frame_size > @remote_window
|
68
72
|
payload = frame.delete(:payload)
|
@@ -93,6 +97,7 @@ module HTTP2
|
|
93
97
|
|
94
98
|
def process_window_update(frame)
|
95
99
|
return if frame[:ignore]
|
100
|
+
|
96
101
|
@remote_window += frame[:increment]
|
97
102
|
send_data
|
98
103
|
end
|
data/lib/http/2/framer.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
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
|
6
7
|
include Error
|
7
8
|
|
@@ -19,82 +20,82 @@ module HTTP2
|
|
19
20
|
|
20
21
|
# HTTP/2 frame type mapping as defined by the spec
|
21
22
|
FRAME_TYPES = {
|
22
|
-
data:
|
23
|
-
headers:
|
24
|
-
priority:
|
25
|
-
rst_stream:
|
26
|
-
settings:
|
27
|
-
push_promise:
|
28
|
-
ping:
|
29
|
-
goaway:
|
23
|
+
data: 0x0,
|
24
|
+
headers: 0x1,
|
25
|
+
priority: 0x2,
|
26
|
+
rst_stream: 0x3,
|
27
|
+
settings: 0x4,
|
28
|
+
push_promise: 0x5,
|
29
|
+
ping: 0x6,
|
30
|
+
goaway: 0x7,
|
30
31
|
window_update: 0x8,
|
31
|
-
continuation:
|
32
|
-
altsvc:
|
32
|
+
continuation: 0x9,
|
33
|
+
altsvc: 0xa
|
33
34
|
}.freeze
|
34
35
|
|
35
|
-
FRAME_TYPES_WITH_PADDING = [
|
36
|
+
FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
|
36
37
|
|
37
38
|
# Per frame flags as defined by the spec
|
38
39
|
FRAME_FLAGS = {
|
39
40
|
data: {
|
40
|
-
end_stream:
|
41
|
+
end_stream: 0,
|
41
42
|
padded: 3,
|
42
|
-
compressed: 5
|
43
|
+
compressed: 5
|
43
44
|
},
|
44
45
|
headers: {
|
45
|
-
end_stream:
|
46
|
+
end_stream: 0,
|
46
47
|
end_headers: 2,
|
47
48
|
padded: 3,
|
48
|
-
priority: 5
|
49
|
+
priority: 5
|
49
50
|
},
|
50
|
-
priority:
|
51
|
-
rst_stream:
|
52
|
-
settings:
|
51
|
+
priority: {},
|
52
|
+
rst_stream: {},
|
53
|
+
settings: { ack: 0 },
|
53
54
|
push_promise: {
|
54
55
|
end_headers: 2,
|
55
|
-
padded: 3
|
56
|
+
padded: 3
|
56
57
|
},
|
57
|
-
ping:
|
58
|
-
goaway:
|
58
|
+
ping: { ack: 0 },
|
59
|
+
goaway: {},
|
59
60
|
window_update: {},
|
60
61
|
continuation: { end_headers: 2 },
|
61
|
-
altsvc: {}
|
62
|
+
altsvc: {}
|
62
63
|
}.each_value(&:freeze).freeze
|
63
64
|
|
64
65
|
# Default settings as defined by the spec
|
65
66
|
DEFINED_SETTINGS = {
|
66
|
-
settings_header_table_size:
|
67
|
-
settings_enable_push:
|
67
|
+
settings_header_table_size: 1,
|
68
|
+
settings_enable_push: 2,
|
68
69
|
settings_max_concurrent_streams: 3,
|
69
|
-
settings_initial_window_size:
|
70
|
-
settings_max_frame_size:
|
71
|
-
settings_max_header_list_size:
|
70
|
+
settings_initial_window_size: 4,
|
71
|
+
settings_max_frame_size: 5,
|
72
|
+
settings_max_header_list_size: 6
|
72
73
|
}.freeze
|
73
74
|
|
74
75
|
# Default error types as defined by the spec
|
75
76
|
DEFINED_ERRORS = {
|
76
|
-
no_error:
|
77
|
-
protocol_error:
|
78
|
-
internal_error:
|
77
|
+
no_error: 0,
|
78
|
+
protocol_error: 1,
|
79
|
+
internal_error: 2,
|
79
80
|
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:
|
81
|
+
settings_timeout: 4,
|
82
|
+
stream_closed: 5,
|
83
|
+
frame_size_error: 6,
|
84
|
+
refused_stream: 7,
|
85
|
+
cancel: 8,
|
86
|
+
compression_error: 9,
|
87
|
+
connect_error: 10,
|
88
|
+
enhance_your_calm: 11,
|
88
89
|
inadequate_security: 12,
|
89
|
-
http_1_1_required:
|
90
|
+
http_1_1_required: 13
|
90
91
|
}.freeze
|
91
92
|
|
92
93
|
RBIT = 0x7fffffff
|
93
94
|
RBYTE = 0x0fffffff
|
94
95
|
EBIT = 0x80000000
|
95
|
-
UINT32 = 'N'
|
96
|
-
UINT16 = 'n'
|
97
|
-
UINT8 = 'C'
|
96
|
+
UINT32 = 'N'
|
97
|
+
UINT16 = 'n'
|
98
|
+
UINT8 = 'C'
|
98
99
|
HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
|
99
100
|
FRAME_LENGTH_HISHIFT = 16
|
100
101
|
FRAME_LENGTH_LOMASK = 0xFFFF
|
@@ -115,24 +116,16 @@ module HTTP2
|
|
115
116
|
def common_header(frame)
|
116
117
|
header = []
|
117
118
|
|
118
|
-
unless FRAME_TYPES[frame[:type]]
|
119
|
-
fail CompressionError, "Invalid frame type (#{frame[:type]})"
|
120
|
-
end
|
119
|
+
raise CompressionError, "Invalid frame type (#{frame[:type]})" unless FRAME_TYPES[frame[:type]]
|
121
120
|
|
122
|
-
if frame[:length] > @max_frame_size
|
123
|
-
fail CompressionError, "Frame size is too large: #{frame[:length]}"
|
124
|
-
end
|
121
|
+
raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @max_frame_size
|
125
122
|
|
126
|
-
|
127
|
-
fail CompressionError, "Frame size is invalid: #{frame[:length]}"
|
128
|
-
end
|
123
|
+
raise CompressionError, "Frame size is invalid: #{frame[:length]}" if (frame[:length]).negative?
|
129
124
|
|
130
|
-
if frame[:stream] > MAX_STREAM_ID
|
131
|
-
fail CompressionError, "Stream ID (#{frame[:stream]}) is too large"
|
132
|
-
end
|
125
|
+
raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID
|
133
126
|
|
134
127
|
if frame[:type] == :window_update && frame[:increment] > MAX_WINDOWINC
|
135
|
-
|
128
|
+
raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
|
136
129
|
end
|
137
130
|
|
138
131
|
header << (frame[:length] >> FRAME_LENGTH_HISHIFT)
|
@@ -140,9 +133,7 @@ module HTTP2
|
|
140
133
|
header << FRAME_TYPES[frame[:type]]
|
141
134
|
header << frame[:flags].reduce(0) do |acc, f|
|
142
135
|
position = FRAME_FLAGS[frame[:type]][f]
|
143
|
-
unless position
|
144
|
-
fail CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}"
|
145
|
-
end
|
136
|
+
raise CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}" unless position
|
146
137
|
|
147
138
|
acc | (1 << position)
|
148
139
|
end
|
@@ -159,10 +150,10 @@ module HTTP2
|
|
159
150
|
len_hi, len_lo, type, flags, stream = buf.slice(0, 9).unpack(HEADERPACK)
|
160
151
|
|
161
152
|
frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
|
162
|
-
frame[:type],
|
153
|
+
frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
|
163
154
|
if frame[:type]
|
164
155
|
frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
|
165
|
-
acc << name if (flags & (1 << pos))
|
156
|
+
acc << name if (flags & (1 << pos)).positive?
|
166
157
|
end
|
167
158
|
end
|
168
159
|
|
@@ -189,8 +180,9 @@ module HTTP2
|
|
189
180
|
when :headers
|
190
181
|
if frame[:weight] || frame[:stream_dependency] || !frame[:exclusive].nil?
|
191
182
|
unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
|
192
|
-
|
183
|
+
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
193
184
|
end
|
185
|
+
|
194
186
|
frame[:flags] += [:priority] unless frame[:flags].include? :priority
|
195
187
|
end
|
196
188
|
|
@@ -205,8 +197,9 @@ module HTTP2
|
|
205
197
|
|
206
198
|
when :priority
|
207
199
|
unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
|
208
|
-
|
200
|
+
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
209
201
|
end
|
202
|
+
|
210
203
|
bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
|
211
204
|
bytes << [frame[:weight] - 1].pack(UINT8)
|
212
205
|
length += 5
|
@@ -216,9 +209,7 @@ module HTTP2
|
|
216
209
|
length += 4
|
217
210
|
|
218
211
|
when :settings
|
219
|
-
if (frame[:stream]).nonzero?
|
220
|
-
fail CompressionError, "Invalid stream ID (#{frame[:stream]})"
|
221
|
-
end
|
212
|
+
raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
222
213
|
|
223
214
|
frame[:payload].each do |(k, v)|
|
224
215
|
if k.is_a? Integer
|
@@ -226,7 +217,7 @@ module HTTP2
|
|
226
217
|
else
|
227
218
|
k = DEFINED_SETTINGS[k]
|
228
219
|
|
229
|
-
|
220
|
+
raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
|
230
221
|
end
|
231
222
|
|
232
223
|
bytes << [k].pack(UINT16)
|
@@ -241,8 +232,9 @@ module HTTP2
|
|
241
232
|
|
242
233
|
when :ping
|
243
234
|
if frame[:payload].bytesize != 8
|
244
|
-
|
235
|
+
raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
|
245
236
|
end
|
237
|
+
|
246
238
|
bytes << frame[:payload]
|
247
239
|
length += 8
|
248
240
|
|
@@ -268,7 +260,8 @@ module HTTP2
|
|
268
260
|
bytes << [frame[:max_age], frame[:port]].pack(UINT32 + UINT16)
|
269
261
|
length += 6
|
270
262
|
if frame[:proto]
|
271
|
-
|
263
|
+
raise CompressionError, 'Proto too long' if frame[:proto].bytesize > 255
|
264
|
+
|
272
265
|
bytes << [frame[:proto].bytesize].pack(UINT8)
|
273
266
|
bytes << frame[:proto].force_encoding(Encoding::BINARY)
|
274
267
|
length += 1 + frame[:proto].bytesize
|
@@ -277,7 +270,8 @@ module HTTP2
|
|
277
270
|
length += 1
|
278
271
|
end
|
279
272
|
if frame[:host]
|
280
|
-
|
273
|
+
raise CompressionError, 'Host too long' if frame[:host].bytesize > 255
|
274
|
+
|
281
275
|
bytes << [frame[:host].bytesize].pack(UINT8)
|
282
276
|
bytes << frame[:host].force_encoding(Encoding::BINARY)
|
283
277
|
length += 1 + frame[:host].bytesize
|
@@ -296,13 +290,13 @@ module HTTP2
|
|
296
290
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.1
|
297
291
|
if frame[:padding]
|
298
292
|
unless FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
299
|
-
|
293
|
+
raise CompressionError, "Invalid padding flag for #{frame[:type]}"
|
300
294
|
end
|
301
295
|
|
302
296
|
padlen = frame[:padding]
|
303
297
|
|
304
298
|
if padlen <= 0 || padlen > 256 || padlen + length > @max_frame_size
|
305
|
-
|
299
|
+
raise CompressionError, "Invalid padding #{padlen}"
|
306
300
|
end
|
307
301
|
|
308
302
|
length += padlen
|
@@ -312,7 +306,7 @@ module HTTP2
|
|
312
306
|
# Padding: Padding octets that contain no application semantic value.
|
313
307
|
# Padding octets MUST be set to zero when sending and ignored when
|
314
308
|
# receiving.
|
315
|
-
bytes << "\0" * padlen
|
309
|
+
bytes << ("\0" * padlen)
|
316
310
|
end
|
317
311
|
|
318
312
|
frame[:length] = length
|
@@ -325,10 +319,11 @@ module HTTP2
|
|
325
319
|
# @param buf [Buffer]
|
326
320
|
def parse(buf)
|
327
321
|
return nil if buf.size < 9
|
322
|
+
|
328
323
|
frame = read_common_header(buf)
|
329
324
|
return nil if buf.size < 9 + frame[:length]
|
330
325
|
|
331
|
-
|
326
|
+
raise ProtocolError, 'payload too large' if frame[:length] > DEFAULT_MAX_FRAME_SIZE
|
332
327
|
|
333
328
|
buf.read(9)
|
334
329
|
payload = buf.read(frame[:length])
|
@@ -343,10 +338,11 @@ module HTTP2
|
|
343
338
|
if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
344
339
|
padded = frame[:flags].include?(:padded)
|
345
340
|
if padded
|
346
|
-
padlen = payload.read(1).
|
341
|
+
padlen = payload.read(1).unpack1(UINT8)
|
347
342
|
frame[:padding] = padlen + 1
|
348
|
-
|
349
|
-
|
343
|
+
raise ProtocolError, 'padding too long' if padlen > payload.bytesize
|
344
|
+
|
345
|
+
payload.slice!(-padlen, padlen) if padlen.positive?
|
350
346
|
frame[:length] -= frame[:padding]
|
351
347
|
frame[:flags].delete(:padded)
|
352
348
|
end
|
@@ -375,21 +371,17 @@ module HTTP2
|
|
375
371
|
# NOTE: frame[:length] might not match the number of frame[:payload]
|
376
372
|
# because unknown extensions are ignored.
|
377
373
|
frame[:payload] = []
|
378
|
-
unless (frame[:length] % 6).zero?
|
379
|
-
fail ProtocolError, 'Invalid settings payload length'
|
380
|
-
end
|
374
|
+
raise ProtocolError, 'Invalid settings payload length' unless (frame[:length] % 6).zero?
|
381
375
|
|
382
|
-
if (frame[:stream]).nonzero?
|
383
|
-
fail ProtocolError, "Invalid stream ID (#{frame[:stream]})"
|
384
|
-
end
|
376
|
+
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
385
377
|
|
386
378
|
(frame[:length] / 6).times do
|
387
|
-
id = payload.read(2).
|
379
|
+
id = payload.read(2).unpack1(UINT16)
|
388
380
|
val = payload.read_uint32
|
389
381
|
|
390
382
|
# Unsupported or unrecognized settings MUST be ignored.
|
391
383
|
# Here we send it along.
|
392
|
-
name,
|
384
|
+
name, = DEFINED_SETTINGS.find { |_name, v| v == id }
|
393
385
|
frame[:payload] << [name, val] if name
|
394
386
|
end
|
395
387
|
when :push_promise
|
@@ -402,7 +394,7 @@ module HTTP2
|
|
402
394
|
frame[:error] = unpack_error payload.read_uint32
|
403
395
|
|
404
396
|
size = frame[:length] - 8 # for last_stream and error
|
405
|
-
frame[:payload] = payload.read(size) if size
|
397
|
+
frame[:payload] = payload.read(size) if size.positive?
|
406
398
|
when :window_update
|
407
399
|
frame[:increment] = payload.read_uint32 & RBIT
|
408
400
|
when :continuation
|
@@ -411,12 +403,12 @@ module HTTP2
|
|
411
403
|
frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
|
412
404
|
|
413
405
|
len = payload.getbyte
|
414
|
-
frame[:proto] = payload.read(len) if len
|
406
|
+
frame[:proto] = payload.read(len) if len.positive?
|
415
407
|
|
416
408
|
len = payload.getbyte
|
417
|
-
frame[:host] = payload.read(len) if len
|
409
|
+
frame[:host] = payload.read(len) if len.positive?
|
418
410
|
|
419
|
-
frame[:origin] = payload.read(payload.size)
|
411
|
+
frame[:origin] = payload.read(payload.size) unless payload.empty?
|
420
412
|
# else # Unknown frame type is explicitly allowed
|
421
413
|
end
|
422
414
|
|
@@ -427,9 +419,7 @@ module HTTP2
|
|
427
419
|
|
428
420
|
def pack_error(e)
|
429
421
|
unless e.is_a? Integer
|
430
|
-
if DEFINED_ERRORS[e].nil?
|
431
|
-
fail CompressionError, "Unknown error ID for #{e}"
|
432
|
-
end
|
422
|
+
raise CompressionError, "Unknown error ID for #{e}" if DEFINED_ERRORS[e].nil?
|
433
423
|
|
434
424
|
e = DEFINED_ERRORS[e]
|
435
425
|
end
|
@@ -438,9 +428,8 @@ module HTTP2
|
|
438
428
|
end
|
439
429
|
|
440
430
|
def unpack_error(e)
|
441
|
-
name,
|
431
|
+
name, = DEFINED_ERRORS.find { |_name, v| v == e }
|
442
432
|
name || error
|
443
433
|
end
|
444
434
|
end
|
445
|
-
# rubocop:enable ClassLength
|
446
435
|
end
|
data/lib/http/2/huffman.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'error'
|
2
4
|
|
3
5
|
module HTTP2
|
@@ -20,7 +22,7 @@ module HTTP2
|
|
20
22
|
# @return [String] binary string
|
21
23
|
def encode(str)
|
22
24
|
bitstring = str.each_byte.map { |chr| ENCODE_TABLE[chr] }.join
|
23
|
-
bitstring << '1' * ((8 - bitstring.size) % 8)
|
25
|
+
bitstring << ('1' * ((8 - bitstring.size) % 8))
|
24
26
|
[bitstring].pack('B*')
|
25
27
|
end
|
26
28
|
|
@@ -30,12 +32,12 @@ module HTTP2
|
|
30
32
|
# @return [String] binary string
|
31
33
|
# @raise [CompressionError] when Huffman coded string is malformed
|
32
34
|
def decode(buf)
|
33
|
-
emit =
|
35
|
+
emit = String.new
|
34
36
|
state = 0 # start state
|
35
37
|
|
36
38
|
mask = (1 << BITS_AT_ONCE) - 1
|
37
39
|
buf.each_byte do |chr|
|
38
|
-
(8 / BITS_AT_ONCE - 1).downto(0) do |shift|
|
40
|
+
((8 / BITS_AT_ONCE) - 1).downto(0) do |shift|
|
39
41
|
branch = (chr >> (shift * BITS_AT_ONCE)) & mask
|
40
42
|
# MACHINE[state] = [final, [transitions]]
|
41
43
|
# [final] unfinished bits so far are prefix of the EOS code.
|
@@ -43,15 +45,15 @@ module HTTP2
|
|
43
45
|
# [emit] character to be emitted on this transition, empty string, or EOS.
|
44
46
|
# [next] next state number.
|
45
47
|
trans = MACHINE[state][branch]
|
46
|
-
|
48
|
+
raise CompressionError, 'Huffman decode error (EOS found)' if trans.first == EOS
|
49
|
+
|
47
50
|
emit << trans.first.chr if trans.first
|
48
51
|
state = trans.last
|
49
52
|
end
|
50
53
|
end
|
51
54
|
# Check whether partial input is correctly filled
|
52
|
-
unless state <= MAX_FINAL_STATE
|
53
|
-
|
54
|
-
end
|
55
|
+
raise CompressionError, 'Huffman decode error (EOS invalid)' unless state <= MAX_FINAL_STATE
|
56
|
+
|
55
57
|
emit.force_encoding(Encoding::BINARY)
|
56
58
|
end
|
57
59
|
|
@@ -153,23 +155,23 @@ module HTTP2
|
|
153
155
|
[0x7fff0, 19],
|
154
156
|
[0x1ffc, 13],
|
155
157
|
[0x3ffc, 14],
|
156
|
-
[0x22,
|
158
|
+
[0x22, 6],
|
157
159
|
[0x7ffd, 15],
|
158
|
-
[0x3,
|
159
|
-
[0x23,
|
160
|
-
[0x4,
|
161
|
-
[0x24,
|
162
|
-
[0x5,
|
160
|
+
[0x3, 5],
|
161
|
+
[0x23, 6],
|
162
|
+
[0x4, 5],
|
163
|
+
[0x24, 6],
|
164
|
+
[0x5, 5],
|
163
165
|
[0x25, 6],
|
164
166
|
[0x26, 6],
|
165
167
|
[0x27, 6],
|
166
|
-
[0x6,
|
168
|
+
[0x6, 5],
|
167
169
|
[0x74, 7],
|
168
170
|
[0x75, 7],
|
169
171
|
[0x28, 6],
|
170
172
|
[0x29, 6],
|
171
173
|
[0x2a, 6],
|
172
|
-
[0x7,
|
174
|
+
[0x7, 5],
|
173
175
|
[0x2b, 6],
|
174
176
|
[0x76, 7],
|
175
177
|
[0x2c, 6],
|
@@ -314,10 +316,10 @@ module HTTP2
|
|
314
316
|
[0x7ffffef, 27],
|
315
317
|
[0x7fffff0, 27],
|
316
318
|
[0x3ffffee, 26],
|
317
|
-
[0x3fffffff, 30]
|
319
|
+
[0x3fffffff, 30]
|
318
320
|
].each(&:freeze).freeze
|
319
321
|
|
320
|
-
ENCODE_TABLE = CODES.map { |c, l| [c].pack('N').
|
322
|
+
ENCODE_TABLE = CODES.map { |c, l| [c].pack('N').unpack1('B*')[-l..-1] }.each(&:freeze).freeze
|
321
323
|
end
|
322
324
|
end
|
323
325
|
end
|
data/lib/http/2/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
module HTTP2
|
3
5
|
# HTTP 2.0 server connection class that implements appropriate header
|
@@ -77,7 +79,7 @@ module HTTP2
|
|
77
79
|
length: buf.bytesize,
|
78
80
|
type: :settings,
|
79
81
|
stream: 0,
|
80
|
-
flags: []
|
82
|
+
flags: []
|
81
83
|
)
|
82
84
|
buf.prepend(header)
|
83
85
|
receive(buf)
|
@@ -87,12 +89,12 @@ module HTTP2
|
|
87
89
|
emit(:stream, stream)
|
88
90
|
|
89
91
|
headers_frame = {
|
90
|
-
type:
|
91
|
-
stream:
|
92
|
-
weight:
|
92
|
+
type: :headers,
|
93
|
+
stream: 1,
|
94
|
+
weight: DEFAULT_WEIGHT,
|
93
95
|
dependency: 0,
|
94
|
-
exclusive:
|
95
|
-
payload: headers
|
96
|
+
exclusive: false,
|
97
|
+
payload: headers
|
96
98
|
}
|
97
99
|
|
98
100
|
if body.empty?
|
@@ -124,7 +126,7 @@ module HTTP2
|
|
124
126
|
flags: flags,
|
125
127
|
stream: parent.id,
|
126
128
|
promise_stream: promise.id,
|
127
|
-
payload: headers.to_a
|
129
|
+
payload: headers.to_a
|
128
130
|
)
|
129
131
|
|
130
132
|
callback.call(promise)
|