http-2 1.1.2 → 1.2.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/lib/http/2/client.rb +1 -1
- data/lib/http/2/connection.rb +79 -62
- data/lib/http/2/extensions.rb +16 -10
- data/lib/http/2/flow_buffer.rb +12 -7
- data/lib/http/2/framer.rb +98 -98
- data/lib/http/2/header/compressor.rb +15 -4
- data/lib/http/2/header/decompressor.rb +11 -10
- data/lib/http/2/header/encoding_context.rb +18 -6
- data/lib/http/2/header/huffman.rb +6 -4
- data/lib/http/2/server.rb +12 -18
- data/lib/http/2/settings.rb +42 -0
- data/lib/http/2/stream.rb +19 -12
- data/lib/http/2/version.rb +1 -1
- data/sig/2.rbs +64 -15
- data/sig/connection.rbs +11 -9
- data/sig/flow_buffer.rbs +2 -2
- data/sig/frame_buffer.rbs +6 -2
- data/sig/framer.rbs +8 -4
- data/sig/header/compressor.rbs +1 -0
- data/sig/header/encoding_context.rbs +4 -0
- data/sig/header/huffman.rbs +2 -0
- data/sig/server.rbs +1 -1
- data/sig/stream.rbs +7 -8
- metadata +2 -1
data/lib/http/2/framer.rb
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTP2
|
|
4
|
+
# Frame flags as defined by the spec (max 255 bits)
|
|
5
|
+
# DATA: ( X X COMPRESSED X PADDED X X END_STREAM )
|
|
6
|
+
# HEADERS: ( X X PRIORITY X PADDED END_HEADERS X END_STREAM )
|
|
7
|
+
# PRIORITY: ( X X X X X X X X )
|
|
8
|
+
# RST_STREAM: ( X X X X X X X X )
|
|
9
|
+
# SETTINGS: ( X X X X X X X ACK )
|
|
10
|
+
# PUSH_PROMISE: ( X X X X PADDED END_HEADERS X X )
|
|
11
|
+
# PING: ( X X X X X X X ACK )
|
|
12
|
+
# GOAWAY: ( X X X X X X X X )
|
|
13
|
+
# WINDOW_UPDATE: ( X X X X X X X X )
|
|
14
|
+
# CONTINUATION: ( X X X X X END_HEADERS X X )
|
|
15
|
+
# ALTSVC: ( X X X X X X X X )
|
|
16
|
+
# ORIGIN: ( RESERVED4 X X RESERVED3 X RESERVED2 RESERVED X )
|
|
17
|
+
END_STREAM = ACK = 0b0001 # 1
|
|
18
|
+
RESERVED = 0b0010 # 2
|
|
19
|
+
END_HEADERS = 0b0100 # 4
|
|
20
|
+
PADDED = 0b1000 # 8
|
|
21
|
+
PRIORITY = 0b0010_0000 # 32
|
|
22
|
+
|
|
4
23
|
# Performs encoding, decoding, and validation of binary HTTP/2 frames.
|
|
5
24
|
#
|
|
6
25
|
class Framer
|
|
@@ -40,39 +59,6 @@ module HTTP2
|
|
|
40
59
|
|
|
41
60
|
FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
|
|
42
61
|
|
|
43
|
-
# Per frame flags as defined by the spec
|
|
44
|
-
FRAME_FLAGS = {
|
|
45
|
-
data: {
|
|
46
|
-
end_stream: 0,
|
|
47
|
-
padded: 3,
|
|
48
|
-
compressed: 5
|
|
49
|
-
},
|
|
50
|
-
headers: {
|
|
51
|
-
end_stream: 0,
|
|
52
|
-
end_headers: 2,
|
|
53
|
-
padded: 3,
|
|
54
|
-
priority: 5
|
|
55
|
-
},
|
|
56
|
-
priority: {},
|
|
57
|
-
rst_stream: {},
|
|
58
|
-
settings: { ack: 0 },
|
|
59
|
-
push_promise: {
|
|
60
|
-
end_headers: 2,
|
|
61
|
-
padded: 3
|
|
62
|
-
},
|
|
63
|
-
ping: { ack: 0 },
|
|
64
|
-
goaway: {},
|
|
65
|
-
window_update: {},
|
|
66
|
-
continuation: { end_headers: 2 },
|
|
67
|
-
altsvc: {},
|
|
68
|
-
origin: {
|
|
69
|
-
reserved: 1,
|
|
70
|
-
reserved2: 2,
|
|
71
|
-
reserved3: 4,
|
|
72
|
-
reserved4: 8
|
|
73
|
-
}
|
|
74
|
-
}.each_value(&:freeze).freeze
|
|
75
|
-
|
|
76
62
|
# Default settings as defined by the spec
|
|
77
63
|
DEFINED_SETTINGS = {
|
|
78
64
|
settings_header_table_size: 1,
|
|
@@ -83,23 +69,25 @@ module HTTP2
|
|
|
83
69
|
settings_max_header_list_size: 6
|
|
84
70
|
}.freeze
|
|
85
71
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
72
|
+
DEFINED_SETTINGS_BY_ID = DEFINED_SETTINGS.invert.freeze
|
|
73
|
+
|
|
74
|
+
# Default error types as defined by the spec (the code is the array index)
|
|
75
|
+
DEFINED_ERRORS = %i[
|
|
76
|
+
no_error
|
|
77
|
+
protocol_error
|
|
78
|
+
internal_error
|
|
79
|
+
flow_control_error
|
|
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
|
|
88
|
+
inadequate_security
|
|
89
|
+
http_1_1_required
|
|
90
|
+
].freeze
|
|
103
91
|
|
|
104
92
|
RBIT = 0x7fffffff
|
|
105
93
|
RBYTE = 0x0fffffff
|
|
@@ -108,10 +96,12 @@ module HTTP2
|
|
|
108
96
|
UINT16 = "n"
|
|
109
97
|
UINT8 = "C"
|
|
110
98
|
HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
|
|
99
|
+
PRIORITYPACK = (UINT32 + UINT8).freeze
|
|
100
|
+
ALTSVCPACK = (UINT32 + UINT16).freeze
|
|
111
101
|
FRAME_LENGTH_HISHIFT = 16
|
|
112
102
|
FRAME_LENGTH_LOMASK = 0xFFFF
|
|
113
103
|
|
|
114
|
-
private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :UINT32, :UINT16, :UINT8
|
|
104
|
+
private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :PRIORITYPACK, :UINT32, :UINT16, :UINT8
|
|
115
105
|
|
|
116
106
|
# Initializes new framer object.
|
|
117
107
|
#
|
|
@@ -146,6 +136,10 @@ module HTTP2
|
|
|
146
136
|
raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
|
|
147
137
|
end
|
|
148
138
|
|
|
139
|
+
flags = frame[:flags]
|
|
140
|
+
|
|
141
|
+
raise CompressionError, "Invalid frame flag (#{flags}) for #{type}" unless flags.between?(0, 255)
|
|
142
|
+
|
|
149
143
|
header = buffer
|
|
150
144
|
|
|
151
145
|
# make sure the buffer is binary and unfrozen
|
|
@@ -160,12 +154,7 @@ module HTTP2
|
|
|
160
154
|
(length >> FRAME_LENGTH_HISHIFT),
|
|
161
155
|
(length & FRAME_LENGTH_LOMASK),
|
|
162
156
|
FRAME_TYPES[type],
|
|
163
|
-
|
|
164
|
-
position = FRAME_FLAGS[type][f]
|
|
165
|
-
raise CompressionError, "Invalid frame flag (#{f}) for #{type}" unless position
|
|
166
|
-
|
|
167
|
-
acc | (1 << position)
|
|
168
|
-
end,
|
|
157
|
+
flags,
|
|
169
158
|
stream_id
|
|
170
159
|
], HEADERPACK, buffer: header, offset: 0) # 8+16,8,8,32
|
|
171
160
|
end
|
|
@@ -184,9 +173,7 @@ module HTTP2
|
|
|
184
173
|
|
|
185
174
|
{
|
|
186
175
|
type: type,
|
|
187
|
-
flags:
|
|
188
|
-
name if flags.anybits?(1 << pos)
|
|
189
|
-
end,
|
|
176
|
+
flags: flags,
|
|
190
177
|
length: length,
|
|
191
178
|
stream: stream & RBIT
|
|
192
179
|
}
|
|
@@ -198,10 +185,11 @@ module HTTP2
|
|
|
198
185
|
# @param frame [Hash]
|
|
199
186
|
def generate(frame)
|
|
200
187
|
length = 0
|
|
201
|
-
frame[:flags] ||=
|
|
188
|
+
frame[:flags] ||= 0
|
|
202
189
|
|
|
203
190
|
case frame[:type]
|
|
204
191
|
when :data, :continuation
|
|
192
|
+
# @type var frame: data_frame | continuation_frame
|
|
205
193
|
bytes = frame[:payload]
|
|
206
194
|
length = bytes.bytesize
|
|
207
195
|
|
|
@@ -213,14 +201,16 @@ module HTTP2
|
|
|
213
201
|
raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
|
|
214
202
|
end
|
|
215
203
|
|
|
216
|
-
frame[:flags]
|
|
204
|
+
frame[:flags] |= PRIORITY
|
|
217
205
|
end
|
|
218
206
|
|
|
219
|
-
if frame[:flags].
|
|
207
|
+
if frame[:flags].anybits?(PRIORITY)
|
|
220
208
|
length = 5 + headers.bytesize
|
|
221
209
|
bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
|
|
222
|
-
pack(
|
|
223
|
-
|
|
210
|
+
pack(
|
|
211
|
+
[(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT), frame[:weight] - 1],
|
|
212
|
+
PRIORITYPACK, buffer: bytes
|
|
213
|
+
)
|
|
224
214
|
append_str(bytes, headers)
|
|
225
215
|
else
|
|
226
216
|
length = headers.bytesize
|
|
@@ -234,8 +224,10 @@ module HTTP2
|
|
|
234
224
|
|
|
235
225
|
length = 5
|
|
236
226
|
bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
|
|
237
|
-
pack(
|
|
238
|
-
|
|
227
|
+
pack(
|
|
228
|
+
[(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT), frame[:weight] - 1],
|
|
229
|
+
PRIORITYPACK, buffer: bytes
|
|
230
|
+
)
|
|
239
231
|
|
|
240
232
|
when :rst_stream
|
|
241
233
|
length = 4
|
|
@@ -248,20 +240,27 @@ module HTTP2
|
|
|
248
240
|
end
|
|
249
241
|
|
|
250
242
|
settings = frame[:payload]
|
|
251
|
-
bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
|
|
252
243
|
|
|
253
|
-
settings
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
244
|
+
case settings
|
|
245
|
+
when String
|
|
246
|
+
length = settings.bytesize
|
|
247
|
+
bytes = settings
|
|
248
|
+
else
|
|
249
|
+
bytes = String.new("", encoding: Encoding::BINARY, capacity: settings.size * 6)
|
|
250
|
+
|
|
251
|
+
settings.each do |(k, v)|
|
|
252
|
+
if k.is_a? Integer # rubocop:disable Style/GuardClause
|
|
253
|
+
DEFINED_SETTINGS.value?(k) || next
|
|
254
|
+
else
|
|
255
|
+
k = DEFINED_SETTINGS[k]
|
|
258
256
|
|
|
259
|
-
|
|
260
|
-
|
|
257
|
+
raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
|
|
258
|
+
end
|
|
261
259
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
pack([k], UINT16, buffer: bytes)
|
|
261
|
+
pack([v], UINT32, buffer: bytes)
|
|
262
|
+
length += 6
|
|
263
|
+
end
|
|
265
264
|
end
|
|
266
265
|
|
|
267
266
|
when :push_promise
|
|
@@ -295,7 +294,7 @@ module HTTP2
|
|
|
295
294
|
when :altsvc
|
|
296
295
|
length = 6
|
|
297
296
|
bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
|
|
298
|
-
pack([frame[:max_age], frame[:port]],
|
|
297
|
+
pack([frame[:max_age], frame[:port]], ALTSVCPACK, buffer: bytes)
|
|
299
298
|
if frame[:proto]
|
|
300
299
|
raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
|
|
301
300
|
|
|
@@ -354,7 +353,7 @@ module HTTP2
|
|
|
354
353
|
|
|
355
354
|
length += padlen
|
|
356
355
|
pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
|
|
357
|
-
frame[:flags]
|
|
356
|
+
frame[:flags] |= PADDED
|
|
358
357
|
|
|
359
358
|
# Padding: Padding octets that contain no application semantic value.
|
|
360
359
|
# Padding octets MUST be set to zero when sending and ignored when
|
|
@@ -375,9 +374,9 @@ module HTTP2
|
|
|
375
374
|
|
|
376
375
|
frame = read_common_header(buf)
|
|
377
376
|
|
|
378
|
-
type = frame[:type]
|
|
379
|
-
length = frame[:length]
|
|
380
|
-
flags = frame[:flags]
|
|
377
|
+
type = frame[:type] #: Symbol
|
|
378
|
+
length = frame[:length] #: Integer
|
|
379
|
+
flags = frame[:flags] #: Integer
|
|
381
380
|
|
|
382
381
|
return if buf.size < 9 + length
|
|
383
382
|
|
|
@@ -394,15 +393,16 @@ module HTTP2
|
|
|
394
393
|
# Process padding
|
|
395
394
|
padlen = 0
|
|
396
395
|
if FRAME_TYPES_WITH_PADDING.include?(type)
|
|
397
|
-
padded = flags.
|
|
396
|
+
padded = flags.anybits?(PADDED)
|
|
398
397
|
if padded
|
|
399
398
|
padlen = read_str(payload, 1).unpack1(UINT8)
|
|
400
|
-
frame[:padding] = padlen + 1
|
|
401
399
|
raise ProtocolError, "padding too long" if padlen > payload.bytesize
|
|
402
400
|
|
|
401
|
+
frame[:padding] = padlen + 1
|
|
402
|
+
|
|
403
403
|
payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
|
|
404
404
|
frame[:length] -= frame[:padding]
|
|
405
|
-
flags
|
|
405
|
+
frame[:flags] ^= PADDED
|
|
406
406
|
end
|
|
407
407
|
end
|
|
408
408
|
|
|
@@ -410,11 +410,11 @@ module HTTP2
|
|
|
410
410
|
when :data, :ping, :continuation
|
|
411
411
|
frame[:payload] = read_str(payload, length)
|
|
412
412
|
when :headers
|
|
413
|
-
if flags.
|
|
413
|
+
if flags.anybits?(PRIORITY)
|
|
414
414
|
e_sd = read_uint32(payload)
|
|
415
415
|
frame[:dependency] = e_sd & RBIT
|
|
416
416
|
frame[:exclusive] = e_sd.anybits?(EBIT)
|
|
417
|
-
weight = payload.
|
|
417
|
+
weight = payload.getbyte(0) + 1
|
|
418
418
|
frame[:weight] = weight
|
|
419
419
|
payload = payload.byteslice(1..-1)
|
|
420
420
|
end
|
|
@@ -425,7 +425,7 @@ module HTTP2
|
|
|
425
425
|
e_sd = read_uint32(payload)
|
|
426
426
|
frame[:dependency] = e_sd & RBIT
|
|
427
427
|
frame[:exclusive] = e_sd.anybits?(EBIT)
|
|
428
|
-
weight = payload.
|
|
428
|
+
weight = payload.getbyte(0) + 1
|
|
429
429
|
frame[:weight] = weight
|
|
430
430
|
payload = payload.byteslice(1..-1)
|
|
431
431
|
when :rst_stream
|
|
@@ -436,19 +436,19 @@ module HTTP2
|
|
|
436
436
|
when :settings
|
|
437
437
|
# NOTE: frame[:length] might not match the number of frame[:payload]
|
|
438
438
|
# because unknown extensions are ignored.
|
|
439
|
-
frame[:payload] = []
|
|
440
439
|
raise ProtocolError, "Invalid settings payload length" unless (length % 6).zero?
|
|
441
440
|
|
|
442
441
|
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if frame[:stream].nonzero?
|
|
443
442
|
|
|
444
|
-
(frame[:length] / 6).times do
|
|
443
|
+
frame[:payload] = (frame[:length] / 6).times.filter_map do
|
|
445
444
|
id = read_str(payload, 2).unpack1(UINT16)
|
|
446
445
|
val = read_uint32(payload)
|
|
447
446
|
|
|
448
447
|
# Unsupported or unrecognized settings MUST be ignored.
|
|
449
448
|
# Here we send it along.
|
|
450
|
-
name
|
|
451
|
-
|
|
449
|
+
if (name = DEFINED_SETTINGS_BY_ID[id])
|
|
450
|
+
[name, val]
|
|
451
|
+
end
|
|
452
452
|
end
|
|
453
453
|
when :push_promise
|
|
454
454
|
frame[:promise_stream] = read_uint32(payload) & RBIT
|
|
@@ -464,13 +464,13 @@ module HTTP2
|
|
|
464
464
|
|
|
465
465
|
frame[:increment] = read_uint32(payload) & RBIT
|
|
466
466
|
when :altsvc
|
|
467
|
-
frame[:max_age], frame[:port] = read_str(payload, 6).unpack(
|
|
467
|
+
frame[:max_age], frame[:port] = read_str(payload, 6).unpack(ALTSVCPACK)
|
|
468
468
|
|
|
469
|
-
len = payload.
|
|
469
|
+
len = payload.getbyte(0)
|
|
470
470
|
payload = payload.byteslice(1..-1)
|
|
471
471
|
frame[:proto] = read_str(payload, len) if len > 0
|
|
472
472
|
|
|
473
|
-
len = payload.
|
|
473
|
+
len = payload.getbyte(0)
|
|
474
474
|
payload = payload.byteslice(1..-1)
|
|
475
475
|
frame[:host] = read_str(payload, len) if len > 0
|
|
476
476
|
|
|
@@ -495,7 +495,7 @@ module HTTP2
|
|
|
495
495
|
|
|
496
496
|
def pack_error(error, buffer:)
|
|
497
497
|
unless error.is_a? Integer
|
|
498
|
-
error = DEFINED_ERRORS
|
|
498
|
+
error = DEFINED_ERRORS.index(error)
|
|
499
499
|
|
|
500
500
|
raise CompressionError, "Unknown error ID for #{error}" unless error
|
|
501
501
|
end
|
|
@@ -504,7 +504,7 @@ module HTTP2
|
|
|
504
504
|
end
|
|
505
505
|
|
|
506
506
|
def unpack_error(error)
|
|
507
|
-
DEFINED_ERRORS.
|
|
507
|
+
DEFINED_ERRORS.fetch(error, error)
|
|
508
508
|
end
|
|
509
509
|
end
|
|
510
510
|
end
|
|
@@ -83,8 +83,10 @@ module HTTP2
|
|
|
83
83
|
huffman = Huffman.encode(str)
|
|
84
84
|
if huffman.bytesize < str.bytesize
|
|
85
85
|
huffman_offset = buffer.bytesize
|
|
86
|
+
integer(huffman.bytesize, 7, buffer: buffer)
|
|
87
|
+
buffer.setbyte(huffman_offset, buffer.getbyte(huffman_offset) | 0x80)
|
|
86
88
|
append_str(buffer, huffman)
|
|
87
|
-
|
|
89
|
+
buffer
|
|
88
90
|
else
|
|
89
91
|
plain_string(str, buffer)
|
|
90
92
|
end
|
|
@@ -119,7 +121,7 @@ module HTTP2
|
|
|
119
121
|
end
|
|
120
122
|
|
|
121
123
|
# set header representation pattern on first byte
|
|
122
|
-
fb = buffer
|
|
124
|
+
fb = buffer.getbyte(offset) | rep[:pattern]
|
|
123
125
|
buffer.setbyte(offset, fb)
|
|
124
126
|
|
|
125
127
|
buffer
|
|
@@ -147,8 +149,17 @@ module HTTP2
|
|
|
147
149
|
# @return [String] binary string
|
|
148
150
|
def huffman_string(str, buffer = "".b)
|
|
149
151
|
huffman_offset = buffer.bytesize
|
|
152
|
+
buffer << "\x00".b
|
|
150
153
|
Huffman.encode(str, buffer)
|
|
151
|
-
|
|
154
|
+
size = buffer.bytesize - huffman_offset - 1
|
|
155
|
+
|
|
156
|
+
if size < 127
|
|
157
|
+
buffer.setbyte(huffman_offset, 0x80 | size)
|
|
158
|
+
else
|
|
159
|
+
buffer.slice!(huffman_offset, 1)
|
|
160
|
+
set_huffman_size(buffer, huffman_offset)
|
|
161
|
+
end
|
|
162
|
+
buffer
|
|
152
163
|
end
|
|
153
164
|
|
|
154
165
|
# @param str [String]
|
|
@@ -165,7 +176,7 @@ module HTTP2
|
|
|
165
176
|
# @return [String] binary string
|
|
166
177
|
def set_huffman_size(buffer, huffman_offset)
|
|
167
178
|
integer(buffer.bytesize - huffman_offset, 7, buffer: buffer, offset: huffman_offset)
|
|
168
|
-
buffer.setbyte(huffman_offset, buffer
|
|
179
|
+
buffer.setbyte(huffman_offset, buffer.getbyte(huffman_offset) | 0x80)
|
|
169
180
|
buffer
|
|
170
181
|
end
|
|
171
182
|
end
|
|
@@ -33,24 +33,25 @@ module HTTP2
|
|
|
33
33
|
# @return [Integer]
|
|
34
34
|
def integer(buf, n)
|
|
35
35
|
limit = (1 << n) - 1
|
|
36
|
-
|
|
36
|
+
if n.zero?
|
|
37
|
+
i = 0
|
|
38
|
+
consumed = 0
|
|
39
|
+
else
|
|
40
|
+
i = buf.getbyte(0) & limit
|
|
41
|
+
consumed = 1
|
|
42
|
+
end
|
|
37
43
|
|
|
38
|
-
m = 0
|
|
39
44
|
if i == limit
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
buf.each_byte.with_index do |byte, idx|
|
|
43
|
-
offset = idx
|
|
44
|
-
# while (byte = shift_byte(buf))
|
|
45
|
+
m = 0
|
|
46
|
+
while (byte = buf.getbyte(consumed))
|
|
45
47
|
i += ((byte & 127) << m)
|
|
46
48
|
m += 7
|
|
47
|
-
|
|
49
|
+
consumed += 1
|
|
48
50
|
break if byte.nobits?(128)
|
|
49
51
|
end
|
|
50
|
-
|
|
51
|
-
read_str(buf, offset + 1)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
read_str(buf, consumed)
|
|
54
55
|
i
|
|
55
56
|
end
|
|
56
57
|
|
|
@@ -118,6 +118,8 @@ module HTTP2
|
|
|
118
118
|
# :index Symbol :all, :static, :never
|
|
119
119
|
def initialize(options = {})
|
|
120
120
|
@table = []
|
|
121
|
+
@table_by_field = Hash.new { |hs, k| hs[k] = [] }
|
|
122
|
+
@unshifts = 0
|
|
121
123
|
@options = DEFAULT_OPTIONS.merge(options)
|
|
122
124
|
@limit = @options[:table_size]
|
|
123
125
|
@_table_updated = false
|
|
@@ -129,9 +131,13 @@ module HTTP2
|
|
|
129
131
|
def dup
|
|
130
132
|
other = EncodingContext.new(@options)
|
|
131
133
|
t = @table
|
|
134
|
+
tbf = @table_by_field.transform_values(&:dup)
|
|
135
|
+
unshifts = @unshifts
|
|
132
136
|
l = @limit
|
|
133
137
|
other.instance_eval do
|
|
134
138
|
@table = t.dup # shallow copy
|
|
139
|
+
@table_by_field = tbf
|
|
140
|
+
@unshifts = unshifts
|
|
135
141
|
@limit = l
|
|
136
142
|
end
|
|
137
143
|
other
|
|
@@ -213,6 +219,8 @@ module HTTP2
|
|
|
213
219
|
# add to table
|
|
214
220
|
if type == :incremental && size_check?(name.bytesize + value.bytesize + 32)
|
|
215
221
|
@table.unshift(emit)
|
|
222
|
+
@unshifts += 1
|
|
223
|
+
@table_by_field[name].unshift([value, @unshifts])
|
|
216
224
|
@current_table_size += name.bytesize + value.bytesize + 32
|
|
217
225
|
@_table_updated = true
|
|
218
226
|
end
|
|
@@ -279,14 +287,14 @@ module HTTP2
|
|
|
279
287
|
end
|
|
280
288
|
|
|
281
289
|
if index_type == :all && !exact
|
|
282
|
-
|
|
283
|
-
next unless field == hfield
|
|
290
|
+
field_entries = @table_by_field[field]
|
|
284
291
|
|
|
292
|
+
field_entries&.each do |hvalue, unshift_id|
|
|
293
|
+
abs_index = (@unshifts - unshift_id) + STATIC_TABLE_SIZE
|
|
294
|
+
name_only ||= abs_index
|
|
285
295
|
if value == hvalue
|
|
286
|
-
exact =
|
|
296
|
+
exact = abs_index
|
|
287
297
|
break
|
|
288
|
-
else
|
|
289
|
-
name_only ||= i + STATIC_TABLE_SIZE
|
|
290
298
|
end
|
|
291
299
|
end
|
|
292
300
|
end
|
|
@@ -317,9 +325,13 @@ module HTTP2
|
|
|
317
325
|
return if @table.empty?
|
|
318
326
|
|
|
319
327
|
while @current_table_size + cmdsize > @limit
|
|
320
|
-
|
|
321
328
|
name, value = @table.pop
|
|
322
329
|
@current_table_size -= name.bytesize + value.bytesize + 32
|
|
330
|
+
|
|
331
|
+
field_arr = @table_by_field[name]
|
|
332
|
+
field_arr.pop
|
|
333
|
+
@table_by_field.delete(name) if field_arr.empty?
|
|
334
|
+
|
|
323
335
|
break if @table.empty?
|
|
324
336
|
|
|
325
337
|
end
|
|
@@ -20,6 +20,8 @@ module HTTP2
|
|
|
20
20
|
EOS = 256
|
|
21
21
|
private_constant :EOS
|
|
22
22
|
|
|
23
|
+
EOS_PADDING = (0..7).map { |n| ("1" * n).b.freeze }.freeze
|
|
24
|
+
|
|
23
25
|
# Encodes provided value via huffman encoding.
|
|
24
26
|
# Length is not encoded in this method.
|
|
25
27
|
#
|
|
@@ -29,7 +31,7 @@ module HTTP2
|
|
|
29
31
|
def encode(str, buffer = "".b)
|
|
30
32
|
bitstring = String.new("", encoding: Encoding::BINARY, capacity: (str.bytesize * 30) + ((8 - str.size) % 8))
|
|
31
33
|
str.each_byte { |chr| append_str(bitstring, ENCODE_TABLE[chr]) }
|
|
32
|
-
append_str(bitstring,
|
|
34
|
+
append_str(bitstring, EOS_PADDING[(8 - bitstring.size) % 8])
|
|
33
35
|
pack([bitstring], "B*", buffer: buffer)
|
|
34
36
|
end
|
|
35
37
|
|
|
@@ -54,13 +56,13 @@ module HTTP2
|
|
|
54
56
|
first, state = MACHINE.dig(state, branch)
|
|
55
57
|
raise CompressionError, "Huffman decode error (EOS found)" if first == EOS
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
emit << first if first
|
|
58
60
|
end
|
|
59
61
|
end
|
|
60
62
|
# Check whether partial input is correctly filled
|
|
61
63
|
raise CompressionError, "Huffman decode error (EOS invalid)" unless state <= MAX_FINAL_STATE
|
|
62
64
|
|
|
63
|
-
emit
|
|
65
|
+
emit
|
|
64
66
|
end
|
|
65
67
|
|
|
66
68
|
# Huffman table as specified in
|
|
@@ -325,7 +327,7 @@ module HTTP2
|
|
|
325
327
|
[0x3fffffff, 30]
|
|
326
328
|
].each(&:freeze).freeze
|
|
327
329
|
|
|
328
|
-
ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1] }.
|
|
330
|
+
ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1].b.freeze }.freeze
|
|
329
331
|
end
|
|
330
332
|
end
|
|
331
333
|
end
|
data/lib/http/2/server.rb
CHANGED
|
@@ -78,18 +78,12 @@ module HTTP2
|
|
|
78
78
|
receive(CONNECTION_PREFACE_MAGIC)
|
|
79
79
|
|
|
80
80
|
# Process received HTTP2-Settings payload
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
stream: 0,
|
|
88
|
-
flags: []
|
|
89
|
-
},
|
|
90
|
-
buffer: buf
|
|
91
|
-
)
|
|
92
|
-
receive(buf)
|
|
81
|
+
receive(@framer.generate(
|
|
82
|
+
type: :settings,
|
|
83
|
+
stream: 0,
|
|
84
|
+
flags: 0,
|
|
85
|
+
payload: Base64.urlsafe_decode64(settings.to_s)
|
|
86
|
+
))
|
|
93
87
|
|
|
94
88
|
# Activate stream (id: 1) with on HTTP/1.1 request parameters
|
|
95
89
|
stream = activate_stream(id: 1)
|
|
@@ -97,7 +91,7 @@ module HTTP2
|
|
|
97
91
|
|
|
98
92
|
headers_frame = {
|
|
99
93
|
type: :headers,
|
|
100
|
-
flags:
|
|
94
|
+
flags: END_HEADERS,
|
|
101
95
|
stream: 1,
|
|
102
96
|
weight: DEFAULT_WEIGHT,
|
|
103
97
|
dependency: 0,
|
|
@@ -106,11 +100,11 @@ module HTTP2
|
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
if body.empty?
|
|
109
|
-
headers_frame[:flags]
|
|
103
|
+
headers_frame[:flags] |= END_HEADERS
|
|
110
104
|
stream << headers_frame
|
|
111
105
|
else
|
|
112
106
|
stream << headers_frame
|
|
113
|
-
stream << { type: :data, stream: 1, payload: body, flags:
|
|
107
|
+
stream << { type: :data, stream: 1, payload: body, flags: END_STREAM }
|
|
114
108
|
end
|
|
115
109
|
|
|
116
110
|
# Mark h2c upgrade as finished
|
|
@@ -135,7 +129,7 @@ module HTTP2
|
|
|
135
129
|
|
|
136
130
|
def connection_settings(frame)
|
|
137
131
|
super
|
|
138
|
-
return unless frame[:flags].
|
|
132
|
+
return unless frame[:flags].anybits?(ACK) && !@origins_sent
|
|
139
133
|
|
|
140
134
|
send(type: :origin, stream: 0, payload: @origin_set)
|
|
141
135
|
end
|
|
@@ -148,7 +142,7 @@ module HTTP2
|
|
|
148
142
|
#
|
|
149
143
|
# @param parent [Stream]
|
|
150
144
|
# @param headers [Enumerable[String, String]]
|
|
151
|
-
# @param flags
|
|
145
|
+
# @param flags Integer
|
|
152
146
|
# @param callback [Proc]
|
|
153
147
|
def promise(parent, headers, flags)
|
|
154
148
|
promise = new_stream(parent: parent)
|
|
@@ -157,7 +151,7 @@ module HTTP2
|
|
|
157
151
|
flags: flags,
|
|
158
152
|
stream: parent.id,
|
|
159
153
|
promise_stream: promise.id,
|
|
160
|
-
payload: headers
|
|
154
|
+
payload: headers
|
|
161
155
|
)
|
|
162
156
|
|
|
163
157
|
yield(promise)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTP2
|
|
4
|
+
# Default values for SETTINGS frame, as defined by the spec.
|
|
5
|
+
SPEC_DEFAULT_CONNECTION_SETTINGS = {
|
|
6
|
+
settings_header_table_size: 4096,
|
|
7
|
+
settings_enable_push: 1, # enabled for servers
|
|
8
|
+
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
|
|
9
|
+
settings_initial_window_size: 65_535,
|
|
10
|
+
settings_max_frame_size: 16_384,
|
|
11
|
+
settings_max_header_list_size: (2 << 30) - 1 # unlimited
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
Settings = Struct.new(
|
|
15
|
+
:settings_header_table_size,
|
|
16
|
+
:settings_enable_push,
|
|
17
|
+
:settings_max_concurrent_streams,
|
|
18
|
+
:settings_initial_window_size,
|
|
19
|
+
:settings_max_frame_size,
|
|
20
|
+
:settings_max_header_list_size,
|
|
21
|
+
keyword_init: true
|
|
22
|
+
) do
|
|
23
|
+
def initialize(
|
|
24
|
+
settings_header_table_size: 4096,
|
|
25
|
+
settings_enable_push: 1,
|
|
26
|
+
settings_max_concurrent_streams: 100,
|
|
27
|
+
settings_initial_window_size: 65_535,
|
|
28
|
+
settings_max_frame_size: 16_384,
|
|
29
|
+
settings_max_header_list_size: (2 << 30) - 1
|
|
30
|
+
)
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each_setting
|
|
35
|
+
each_pair do |k, v|
|
|
36
|
+
next if v == SPEC_DEFAULT_CONNECTION_SETTINGS[k]
|
|
37
|
+
|
|
38
|
+
yield k, v
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|