http-2 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/http/2/connection.rb +2 -3
- 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] }
|
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:
|