http-2 1.0.0 → 1.0.2
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/connection.rb +14 -6
- data/lib/http/2/extensions.rb +13 -15
- data/lib/http/2/framer.rb +26 -27
- data/lib/http/2/header/decompressor.rb +6 -6
- data/lib/http/2/header/encoding_context.rb +8 -6
- data/lib/http/2/header/huffman.rb +0 -2
- data/lib/http/2/header/huffman_statemachine.rb +1 -1
- data/lib/http/2/version.rb +1 -1
- data/sig/connection.rbs +1 -0
- data/sig/extensions.rbs +8 -0
- data/sig/framer.rbs +1 -0
- data/sig/header/decompressor.rbs +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c96822381e28e32127984c2dbb7b25b3ad5dd87d69283a752378a9f8778c72ae
|
4
|
+
data.tar.gz: beb68a1eb2670d1d0b742224b5ad65bd2bc299656433b120ff31299a481297ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 173e0163e2b05ff3eefdfcc420f8c3913c97702873964ef6c653f0329726443c4c10d73d7ffff31583b06567ef12c8f6545eae0f3d32c52d9b13d98322c09e32
|
7
|
+
data.tar.gz: 43b38b2bdd3ed6ac90382b98c96b03fd76edefbbf4a385c67c3c8db67e6f755382f09c8a3af848a1d8ed55335ad2925d05793ad71c16c5bb831b07f86e0228be
|
data/lib/http/2/connection.rb
CHANGED
@@ -52,8 +52,7 @@ module HTTP2
|
|
52
52
|
include FlowBuffer
|
53
53
|
include Emitter
|
54
54
|
include Error
|
55
|
-
|
56
|
-
using StringExtensions
|
55
|
+
include BufferUtils
|
57
56
|
|
58
57
|
# Connection state (:new, :closed).
|
59
58
|
attr_reader :state
|
@@ -202,7 +201,7 @@ module HTTP2
|
|
202
201
|
raise HandshakeError unless CONNECTION_PREFACE_MAGIC.start_with? @recv_buffer
|
203
202
|
|
204
203
|
return # maybe next time
|
205
|
-
elsif @recv_buffer
|
204
|
+
elsif read_str(@recv_buffer, 24) == CONNECTION_PREFACE_MAGIC
|
206
205
|
# MAGIC is OK. Send our settings
|
207
206
|
@state = :waiting_connection_preface
|
208
207
|
payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
@@ -294,13 +293,13 @@ module HTTP2
|
|
294
293
|
if stream.nil?
|
295
294
|
verify_pseudo_headers(frame)
|
296
295
|
|
296
|
+
verify_stream_order(frame[:stream])
|
297
297
|
stream = activate_stream(
|
298
298
|
id: frame[:stream],
|
299
299
|
weight: frame[:weight] || DEFAULT_WEIGHT,
|
300
300
|
dependency: frame[:dependency] || 0,
|
301
301
|
exclusive: frame[:exclusive] || false
|
302
302
|
)
|
303
|
-
verify_stream_order(stream.id)
|
304
303
|
emit(:stream, stream)
|
305
304
|
end
|
306
305
|
|
@@ -354,8 +353,8 @@ module HTTP2
|
|
354
353
|
end
|
355
354
|
|
356
355
|
_verify_pseudo_headers(frame, REQUEST_MANDATORY_HEADERS)
|
356
|
+
verify_stream_order(pid)
|
357
357
|
stream = activate_stream(id: pid, parent: parent)
|
358
|
-
verify_stream_order(stream.id)
|
359
358
|
emit(:promise, stream)
|
360
359
|
stream << frame
|
361
360
|
else
|
@@ -391,6 +390,13 @@ module HTTP2
|
|
391
390
|
stream = @streams_recently_closed[frame[:stream]]
|
392
391
|
connection_error(:protocol_error, msg: "sent window update on idle stream") unless stream
|
393
392
|
process_window_update(frame: frame, encode: true)
|
393
|
+
# Endpoints MUST ignore
|
394
|
+
# WINDOW_UPDATE or RST_STREAM frames received in this state (closed), though
|
395
|
+
# endpoints MAY choose to treat frames that arrive a significant
|
396
|
+
# time after sending END_STREAM as a connection error.
|
397
|
+
when :rst_stream
|
398
|
+
stream = @streams_recently_closed[frame[:stream]]
|
399
|
+
connection_error(:protocol_error, msg: "sent window update on idle stream") unless stream
|
394
400
|
else
|
395
401
|
# An endpoint that receives an unexpected stream identifier
|
396
402
|
# MUST respond with a connection error of type PROTOCOL_ERROR.
|
@@ -744,6 +750,8 @@ module HTTP2
|
|
744
750
|
stream = Stream.new(connection: self, id: id, **args)
|
745
751
|
|
746
752
|
stream.once(:close) do
|
753
|
+
@streams.delete(id)
|
754
|
+
|
747
755
|
# Store a reference to the closed stream, such that we can respond
|
748
756
|
# to any in-flight frames while close is registered on both sides.
|
749
757
|
# References to such streams will be purged whenever another stream
|
@@ -767,7 +775,7 @@ module HTTP2
|
|
767
775
|
def verify_stream_order(id)
|
768
776
|
return unless id.odd?
|
769
777
|
|
770
|
-
connection_error(msg: "Stream ID smaller than previous") if @last_stream_id
|
778
|
+
connection_error(msg: "Stream ID smaller than previous") if @last_stream_id >= id
|
771
779
|
@last_stream_id = id
|
772
780
|
end
|
773
781
|
|
data/lib/http/2/extensions.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTP2
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
return "".b if n == 0
|
4
|
+
module BufferUtils
|
5
|
+
def read_str(str, n)
|
6
|
+
return "".b if n == 0
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
chunk = str.byteslice(0..n - 1)
|
9
|
+
remaining = str.byteslice(n..-1)
|
10
|
+
remaining ? str.replace(remaining) : str.clear
|
11
|
+
chunk
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def read_uint32(str)
|
15
|
+
read_str(str, 4).unpack1("N")
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
18
|
+
def shift_byte(str)
|
19
|
+
read_str(str, 1).ord
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
data/lib/http/2/framer.rb
CHANGED
@@ -4,10 +4,9 @@ module HTTP2
|
|
4
4
|
# Performs encoding, decoding, and validation of binary HTTP/2 frames.
|
5
5
|
#
|
6
6
|
class Framer
|
7
|
-
using StringExtensions
|
8
|
-
|
9
7
|
include Error
|
10
8
|
include PackingExtensions
|
9
|
+
include BufferUtils
|
11
10
|
|
12
11
|
# Default value of max frame size (16384 bytes)
|
13
12
|
DEFAULT_MAX_FRAME_SIZE = 2 << 13
|
@@ -165,7 +164,7 @@ module HTTP2
|
|
165
164
|
frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
|
166
165
|
if frame[:type]
|
167
166
|
frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
|
168
|
-
acc << name if
|
167
|
+
acc << name if flags.anybits?((1 << pos))
|
169
168
|
end
|
170
169
|
end
|
171
170
|
|
@@ -344,8 +343,8 @@ module HTTP2
|
|
344
343
|
|
345
344
|
raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
|
346
345
|
|
347
|
-
buf
|
348
|
-
payload = buf
|
346
|
+
read_str(buf, 9)
|
347
|
+
payload = read_str(buf, frame[:length])
|
349
348
|
|
350
349
|
# Implementations MUST discard frames
|
351
350
|
# that have unknown or unsupported types.
|
@@ -357,7 +356,7 @@ module HTTP2
|
|
357
356
|
if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
358
357
|
padded = frame[:flags].include?(:padded)
|
359
358
|
if padded
|
360
|
-
padlen = payload
|
359
|
+
padlen = read_str(payload, 1).unpack1(UINT8)
|
361
360
|
frame[:padding] = padlen + 1
|
362
361
|
raise ProtocolError, "padding too long" if padlen > payload.bytesize
|
363
362
|
|
@@ -369,30 +368,30 @@ module HTTP2
|
|
369
368
|
|
370
369
|
case frame[:type]
|
371
370
|
when :data, :ping, :continuation
|
372
|
-
frame[:payload] = payload
|
371
|
+
frame[:payload] = read_str(payload, frame[:length])
|
373
372
|
when :headers
|
374
373
|
if frame[:flags].include? :priority
|
375
|
-
e_sd = payload
|
374
|
+
e_sd = read_uint32(payload)
|
376
375
|
frame[:dependency] = e_sd & RBIT
|
377
|
-
frame[:exclusive] = (
|
376
|
+
frame[:exclusive] = e_sd.anybits?(EBIT)
|
378
377
|
weight = payload.byteslice(0, 1).ord + 1
|
379
378
|
frame[:weight] = weight
|
380
379
|
payload = payload.byteslice(1..-1)
|
381
380
|
end
|
382
|
-
frame[:payload] = payload
|
381
|
+
frame[:payload] = read_str(payload, frame[:length])
|
383
382
|
when :priority
|
384
383
|
raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
|
385
384
|
|
386
|
-
e_sd = payload
|
385
|
+
e_sd = read_uint32(payload)
|
387
386
|
frame[:dependency] = e_sd & RBIT
|
388
|
-
frame[:exclusive] = (
|
387
|
+
frame[:exclusive] = e_sd.anybits?(EBIT)
|
389
388
|
weight = payload.byteslice(0, 1).ord + 1
|
390
389
|
frame[:weight] = weight
|
391
390
|
payload = payload.byteslice(1..-1)
|
392
391
|
when :rst_stream
|
393
392
|
raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
|
394
393
|
|
395
|
-
frame[:error] = unpack_error payload
|
394
|
+
frame[:error] = unpack_error read_uint32(payload)
|
396
395
|
|
397
396
|
when :settings
|
398
397
|
# NOTE: frame[:length] might not match the number of frame[:payload]
|
@@ -403,8 +402,8 @@ module HTTP2
|
|
403
402
|
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
404
403
|
|
405
404
|
(frame[:length] / 6).times do
|
406
|
-
id = payload
|
407
|
-
val = payload
|
405
|
+
id = read_str(payload, 2).unpack1(UINT16)
|
406
|
+
val = read_uint32(payload)
|
408
407
|
|
409
408
|
# Unsupported or unrecognized settings MUST be ignored.
|
410
409
|
# Here we send it along.
|
@@ -412,39 +411,39 @@ module HTTP2
|
|
412
411
|
frame[:payload] << [name, val] if name
|
413
412
|
end
|
414
413
|
when :push_promise
|
415
|
-
frame[:promise_stream] = payload
|
416
|
-
frame[:payload] = payload
|
414
|
+
frame[:promise_stream] = read_uint32(payload) & RBIT
|
415
|
+
frame[:payload] = read_str(payload, frame[:length])
|
417
416
|
when :goaway
|
418
|
-
frame[:last_stream] = payload
|
419
|
-
frame[:error] = unpack_error payload
|
417
|
+
frame[:last_stream] = read_uint32(payload) & RBIT
|
418
|
+
frame[:error] = unpack_error read_uint32(payload)
|
420
419
|
|
421
420
|
size = frame[:length] - 8 # for last_stream and error
|
422
|
-
frame[:payload] = payload
|
421
|
+
frame[:payload] = read_str(payload, size) if size > 0
|
423
422
|
when :window_update
|
424
423
|
if frame[:length] % 4 != 0
|
425
424
|
raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)"
|
426
425
|
end
|
427
426
|
|
428
|
-
frame[:increment] = payload
|
427
|
+
frame[:increment] = read_uint32(payload) & RBIT
|
429
428
|
when :altsvc
|
430
|
-
frame[:max_age], frame[:port] = payload
|
429
|
+
frame[:max_age], frame[:port] = read_str(payload, 6).unpack(UINT32 + UINT16)
|
431
430
|
|
432
431
|
len = payload.byteslice(0, 1).ord
|
433
432
|
payload = payload.byteslice(1..-1)
|
434
|
-
frame[:proto] = payload
|
433
|
+
frame[:proto] = read_str(payload, len) if len > 0
|
435
434
|
|
436
435
|
len = payload.byteslice(0, 1).ord
|
437
436
|
payload = payload.byteslice(1..-1)
|
438
|
-
frame[:host] = payload
|
437
|
+
frame[:host] = read_str(payload, len) if len > 0
|
439
438
|
|
440
|
-
frame[:origin] =
|
439
|
+
frame[:origin] = read_str(payload, payload.size) unless payload.empty?
|
441
440
|
|
442
441
|
when :origin
|
443
442
|
origins = []
|
444
443
|
|
445
444
|
until payload.empty?
|
446
|
-
len = payload
|
447
|
-
origins << payload
|
445
|
+
len = read_str(payload, 2).unpack1(UINT16)
|
446
|
+
origins << read_str(payload, len)
|
448
447
|
end
|
449
448
|
|
450
449
|
frame[:payload] = origins
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module HTTP2
|
4
4
|
module Header
|
5
|
-
using StringExtensions
|
6
5
|
# Responsible for decoding received headers and maintaining compression
|
7
6
|
# context of the opposing peer. Decompressor must be initialized with
|
8
7
|
# appropriate starting context based on local role: client or server.
|
@@ -12,6 +11,7 @@ module HTTP2
|
|
12
11
|
# client_role = Decompressor.new(:response)
|
13
12
|
class Decompressor
|
14
13
|
include Error
|
14
|
+
include BufferUtils
|
15
15
|
|
16
16
|
# @param options [Hash] decoding options. Only :table_size is effective.
|
17
17
|
def initialize(options = {})
|
@@ -31,15 +31,15 @@ module HTTP2
|
|
31
31
|
# @return [Integer]
|
32
32
|
def integer(buf, n)
|
33
33
|
limit = (2**n) - 1
|
34
|
-
i = n.zero? ? 0 : (buf
|
34
|
+
i = n.zero? ? 0 : (shift_byte(buf) & limit)
|
35
35
|
|
36
36
|
m = 0
|
37
37
|
if i == limit
|
38
|
-
while (byte = buf
|
38
|
+
while (byte = shift_byte(buf))
|
39
39
|
i += ((byte & 127) << m)
|
40
40
|
m += 7
|
41
41
|
|
42
|
-
break if (
|
42
|
+
break if byte.nobits?(128)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -54,9 +54,9 @@ module HTTP2
|
|
54
54
|
def string(buf)
|
55
55
|
raise CompressionError, "invalid header block fragment" if buf.empty?
|
56
56
|
|
57
|
-
huffman =
|
57
|
+
huffman = buf.getbyte(0).allbits?(0x80)
|
58
58
|
len = integer(buf, 7)
|
59
|
-
str = buf
|
59
|
+
str = read_str(buf, len)
|
60
60
|
raise CompressionError, "string too short" unless str.bytesize == len
|
61
61
|
|
62
62
|
str = Huffman.new.decode(str) if huffman
|
@@ -75,12 +75,14 @@ module HTTP2
|
|
75
75
|
["vary", ""],
|
76
76
|
["via", ""],
|
77
77
|
["www-authenticate", ""]
|
78
|
-
].each
|
79
|
-
|
80
|
-
STATIC_TABLE_BY_FIELD =
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
].each(&:freeze).freeze
|
79
|
+
|
80
|
+
STATIC_TABLE_BY_FIELD =
|
81
|
+
STATIC_TABLE
|
82
|
+
.each_with_object({})
|
83
|
+
.with_index { |((field, value), hs), idx| (hs[field] ||= []) << [idx, value].freeze }
|
84
|
+
.each_value(&:freeze)
|
85
|
+
.freeze
|
84
86
|
|
85
87
|
STATIC_TABLE_SIZE = STATIC_TABLE.size
|
86
88
|
|
@@ -268,7 +268,7 @@ module HTTP2
|
|
268
268
|
[[28, 29], [28, 5], [29, 29], [29, 5], [30, 29], [30, 5], [31, 29], [31, 5], [127, 29], [127, 5], [220, 29], [220, 5], [249, 29], [249, 5], [nil, 254], [nil, 255]],
|
269
269
|
[[10, 17], [10, 18], [10, 19], [10, 20], [10, 21], [10, 22], [10, 23], [10, 7], [13, 17], [13, 18], [13, 19], [13, 20], [13, 21], [13, 22], [13, 23], [13, 7]],
|
270
270
|
[[22, 17], [22, 18], [22, 19], [22, 20], [22, 21], [22, 22], [22, 23], [22, 7], [256, 17], [256, 18], [256, 19], [256, 20], [256, 21], [256, 22], [256, 23], [256, 7]],
|
271
|
-
].each { |arr| arr.each { |subarr| subarr.
|
271
|
+
].each { |arr| arr.each { |subarr| subarr.freeze }.freeze }.freeze
|
272
272
|
end
|
273
273
|
end
|
274
274
|
end
|
data/lib/http/2/version.rb
CHANGED
data/sig/connection.rbs
CHANGED
data/sig/extensions.rbs
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
module HTTP2
|
2
|
+
module BufferUtils
|
3
|
+
def read_str: (String str, Integer n) -> String
|
4
|
+
|
5
|
+
def read_uint32: (String str) -> Integer
|
6
|
+
|
7
|
+
def shift_byte: (String str) -> Integer
|
8
|
+
end
|
9
|
+
|
2
10
|
module PackingExtensions
|
3
11
|
def pack: (Array[Integer] array_to_pack, String template, buffer: String, ?offset: Integer) -> String
|
4
12
|
end
|
data/sig/framer.rbs
CHANGED
data/sig/header/decompressor.rbs
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http-2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-11-05 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: Pure-ruby HTTP 2.0 protocol implementation
|
16
16
|
email:
|