protocol-http2 0.19.4 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/http2/client.rb +4 -0
- data/lib/protocol/http2/connection.rb +10 -36
- data/lib/protocol/http2/flow_controlled.rb +1 -1
- data/lib/protocol/http2/frame.rb +2 -2
- data/lib/protocol/http2/framer.rb +1 -48
- data/lib/protocol/http2/headers_frame.rb +3 -11
- data/lib/protocol/http2/server.rb +4 -0
- data/lib/protocol/http2/settings_frame.rb +1 -0
- data/lib/protocol/http2/stream.rb +9 -31
- data/lib/protocol/http2/version.rb +1 -1
- data/lib/protocol/http2/window.rb +1 -1
- data/lib/traces/provider/protocol/http2/framer.rb +52 -0
- data/lib/traces/provider/protocol/http2.rb +6 -0
- data/readme.md +1 -1
- data/releases.md +7 -0
- data.tar.gz.sig +0 -0
- metadata +6 -19
- metadata.gz.sig +0 -0
- data/lib/protocol/http2/dependency.rb +0 -218
- data/lib/protocol/http2/priority_frame.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90e7e77b799044d635c41f870e4d852fb8b9c3e51e5e637e70400420fa318c4f
|
4
|
+
data.tar.gz: f2d9106c992e4503e36c5f3b4d57859744cd0281bcd8f261b7d902b7574e7cfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b181f107bb843771deeb1d546a58315643faea3abcfa8f5a37198e2bf301a25f33d58d140879e676ac3928f932ed9b4a64325613b62e6b9e1297cc31264ef17
|
7
|
+
data.tar.gz: 89ed0a08306f0e6a1835afc1867fac8c9f0c01c28aeb4828eec3f2611f90244ddbf2be01faaf15f775ed30457b044f7a8956bede2cb0f83de913d1945524eccf
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -28,6 +28,10 @@ module Protocol
|
|
28
28
|
if @state == :new
|
29
29
|
@framer.write_connection_preface
|
30
30
|
|
31
|
+
# We don't support RFC7540 priorities:
|
32
|
+
settings = settings.to_a
|
33
|
+
settings << [Settings::NO_RFC7540_PRIORITIES, 1]
|
34
|
+
|
31
35
|
send_settings(settings)
|
32
36
|
|
33
37
|
yield if block_given?
|
@@ -5,7 +5,6 @@
|
|
5
5
|
# Copyright, 2023, by Marco Concetto Rudilosso.
|
6
6
|
|
7
7
|
require_relative "framer"
|
8
|
-
require_relative "dependency"
|
9
8
|
require_relative "flow_controlled"
|
10
9
|
|
11
10
|
require "protocol/hpack"
|
@@ -23,10 +22,6 @@ module Protocol
|
|
23
22
|
# Hash(Integer, Stream)
|
24
23
|
@streams = {}
|
25
24
|
|
26
|
-
# Hash(Integer, Dependency)
|
27
|
-
@dependency = Dependency.new(self, 0)
|
28
|
-
@dependencies = {0 => @dependency}
|
29
|
-
|
30
25
|
@framer = framer
|
31
26
|
|
32
27
|
# The next stream id to use:
|
@@ -62,7 +57,9 @@ module Protocol
|
|
62
57
|
@remote_settings.maximum_frame_size
|
63
58
|
end
|
64
59
|
|
65
|
-
# The maximum number of concurrent streams that this connection can initiate
|
60
|
+
# The maximum number of concurrent streams that this connection can initiate. This is a setting that can be changed by the remote peer.
|
61
|
+
#
|
62
|
+
# It is not the same as the number of streams that can be accepted by the connection. The number of streams that can be accepted is determined by the local settings, and the number of streams that can be initiated is determined by the remote settings.
|
66
63
|
def maximum_concurrent_streams
|
67
64
|
@remote_settings.maximum_concurrent_streams
|
68
65
|
end
|
@@ -93,7 +90,6 @@ module Protocol
|
|
93
90
|
|
94
91
|
def delete(id)
|
95
92
|
@streams.delete(id)
|
96
|
-
@dependencies[id]&.delete!
|
97
93
|
end
|
98
94
|
|
99
95
|
# Close the underlying framer and all streams.
|
@@ -400,22 +396,6 @@ module Protocol
|
|
400
396
|
end
|
401
397
|
end
|
402
398
|
|
403
|
-
def send_priority(stream_id, priority)
|
404
|
-
frame = PriorityFrame.new(stream_id)
|
405
|
-
frame.pack(priority)
|
406
|
-
|
407
|
-
write_frame(frame)
|
408
|
-
end
|
409
|
-
|
410
|
-
# Sets the priority for an incoming stream.
|
411
|
-
def receive_priority(frame)
|
412
|
-
if dependency = @dependencies[frame.stream_id]
|
413
|
-
dependency.receive_priority(frame)
|
414
|
-
elsif idle_stream_id?(frame.stream_id)
|
415
|
-
Dependency.create(self, frame.stream_id, frame.unpack)
|
416
|
-
end
|
417
|
-
end
|
418
|
-
|
419
399
|
def receive_push_promise(frame)
|
420
400
|
raise ProtocolError, "Unable to receive push promise!"
|
421
401
|
end
|
@@ -468,23 +448,17 @@ module Protocol
|
|
468
448
|
end
|
469
449
|
end
|
470
450
|
|
471
|
-
# Traverse active streams
|
472
|
-
# @
|
451
|
+
# Traverse active streams and allow them to consume the available flow-control window.
|
452
|
+
# @parameter amount [Integer] the amount of data to write. Defaults to the current window capacity.
|
473
453
|
def consume_window(size = self.available_size)
|
474
454
|
# Return if there is no window to consume:
|
475
455
|
return unless size > 0
|
476
456
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
# buffer.puts
|
483
|
-
#
|
484
|
-
# @dependency.print_hierarchy(buffer)
|
485
|
-
# end
|
486
|
-
|
487
|
-
@dependency.consume_window(size)
|
457
|
+
@streams.each_value do |stream|
|
458
|
+
if stream.active?
|
459
|
+
stream.window_updated(size)
|
460
|
+
end
|
461
|
+
end
|
488
462
|
end
|
489
463
|
|
490
464
|
def receive_window_update(frame)
|
@@ -78,7 +78,7 @@ module Protocol
|
|
78
78
|
end
|
79
79
|
|
80
80
|
# The window has been expanded by the given amount.
|
81
|
-
# @
|
81
|
+
# @parameter size [Integer] the maximum amount of data to send.
|
82
82
|
# @return [Boolean] whether the window update was used or not.
|
83
83
|
def window_updated(size)
|
84
84
|
return false
|
data/lib/protocol/http2/frame.rb
CHANGED
@@ -34,7 +34,7 @@ module Protocol
|
|
34
34
|
# The base class does not have any specific type index:
|
35
35
|
TYPE = nil
|
36
36
|
|
37
|
-
# @
|
37
|
+
# @parameter length [Integer] the length of the payload, or nil if the header has not been read yet.
|
38
38
|
def initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil)
|
39
39
|
@stream_id = stream_id
|
40
40
|
@flags = flags
|
@@ -134,7 +134,7 @@ module Protocol
|
|
134
134
|
|
135
135
|
# Decodes common 9-byte header.
|
136
136
|
#
|
137
|
-
# @
|
137
|
+
# @parameter buffer [String]
|
138
138
|
def self.parse_header(buffer)
|
139
139
|
length_hi, length_lo, type, flags, stream_id = buffer.unpack(HEADER_FORMAT)
|
140
140
|
length = (length_hi << LENGTH_HISHIFT) | length_lo
|
@@ -7,7 +7,6 @@ require_relative "error"
|
|
7
7
|
|
8
8
|
require_relative "data_frame"
|
9
9
|
require_relative "headers_frame"
|
10
|
-
require_relative "priority_frame"
|
11
10
|
require_relative "reset_stream_frame"
|
12
11
|
require_relative "settings_frame"
|
13
12
|
require_relative "push_promise_frame"
|
@@ -16,15 +15,13 @@ require_relative "goaway_frame"
|
|
16
15
|
require_relative "window_update_frame"
|
17
16
|
require_relative "continuation_frame"
|
18
17
|
|
19
|
-
require "traces/provider"
|
20
|
-
|
21
18
|
module Protocol
|
22
19
|
module HTTP2
|
23
20
|
# HTTP/2 frame type mapping as defined by the spec
|
24
21
|
FRAMES = [
|
25
22
|
DataFrame,
|
26
23
|
HeadersFrame,
|
27
|
-
PriorityFrame
|
24
|
+
nil, # PriorityFrame is deprecated / removed.
|
28
25
|
ResetStreamFrame,
|
29
26
|
SettingsFrame,
|
30
27
|
PushPromiseFrame,
|
@@ -109,50 +106,6 @@ module Protocol
|
|
109
106
|
|
110
107
|
raise EOFError, "Could not read frame header!"
|
111
108
|
end
|
112
|
-
|
113
|
-
Traces::Provider(self) do
|
114
|
-
def write_connection_preface
|
115
|
-
Traces.trace("protocol.http2.framer.write_connection_preface") do
|
116
|
-
super
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def read_connection_preface
|
121
|
-
Traces.trace("protocol.http2.framer.read_connection_preface") do
|
122
|
-
super
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def write_frame(frame)
|
127
|
-
attributes = {
|
128
|
-
"frame.length" => frame.length,
|
129
|
-
"frame.type" => frame.type,
|
130
|
-
"frame.flags" => frame.flags,
|
131
|
-
"frame.stream_id" => frame.stream_id,
|
132
|
-
}
|
133
|
-
|
134
|
-
Traces.trace("protocol.http2.framer.write_frame", attributes: attributes) do
|
135
|
-
super
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
|
140
|
-
Traces.trace("protocol.http2.framer.read_frame") do |span|
|
141
|
-
super.tap do |frame|
|
142
|
-
span["frame.length"] = frame.length
|
143
|
-
span["frame.type"] = frame.type
|
144
|
-
span["frame.flags"] = frame.flags
|
145
|
-
span["frame.stream_id"] = frame.stream_id
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def flush
|
151
|
-
Traces.trace("protocol.http2.framer.flush") do
|
152
|
-
super
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
109
|
end
|
157
110
|
end
|
158
111
|
end
|
@@ -6,7 +6,6 @@
|
|
6
6
|
require_relative "frame"
|
7
7
|
require_relative "padded"
|
8
8
|
require_relative "continuation_frame"
|
9
|
-
require_relative "priority_frame"
|
10
9
|
|
11
10
|
module Protocol
|
12
11
|
module HTTP2
|
@@ -41,23 +40,16 @@ module Protocol
|
|
41
40
|
data = super
|
42
41
|
|
43
42
|
if priority?
|
44
|
-
priority
|
43
|
+
# We no longer support priority frames, so strip the data:
|
45
44
|
data = data.byteslice(5, data.bytesize - 5)
|
46
45
|
end
|
47
46
|
|
48
|
-
return
|
47
|
+
return data
|
49
48
|
end
|
50
49
|
|
51
|
-
def pack(
|
50
|
+
def pack(data, *arguments, **options)
|
52
51
|
buffer = String.new.b
|
53
52
|
|
54
|
-
if priority
|
55
|
-
buffer << priority.pack
|
56
|
-
set_flags(PRIORITY)
|
57
|
-
else
|
58
|
-
clear_flags(PRIORITY)
|
59
|
-
end
|
60
|
-
|
61
53
|
buffer << data
|
62
54
|
|
63
55
|
super(buffer, *arguments, **options)
|
@@ -4,7 +4,6 @@
|
|
4
4
|
# Copyright, 2019-2024, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "connection"
|
7
|
-
require_relative "dependency"
|
8
7
|
|
9
8
|
module Protocol
|
10
9
|
module HTTP2
|
@@ -77,8 +76,6 @@ module Protocol
|
|
77
76
|
|
78
77
|
@local_window = Window.new(@connection.local_settings.initial_window_size)
|
79
78
|
@remote_window = Window.new(@connection.remote_settings.initial_window_size)
|
80
|
-
|
81
|
-
@dependency = Dependency.create(@connection, @id)
|
82
79
|
end
|
83
80
|
|
84
81
|
# The connection this stream belongs to.
|
@@ -90,27 +87,9 @@ module Protocol
|
|
90
87
|
# Stream state, e.g. `idle`, `closed`.
|
91
88
|
attr_accessor :state
|
92
89
|
|
93
|
-
attr :dependency
|
94
|
-
|
95
90
|
attr :local_window
|
96
91
|
attr :remote_window
|
97
92
|
|
98
|
-
def weight
|
99
|
-
@dependency.weight
|
100
|
-
end
|
101
|
-
|
102
|
-
def priority
|
103
|
-
@dependency.priority
|
104
|
-
end
|
105
|
-
|
106
|
-
def priority= priority
|
107
|
-
@dependency.priority = priority
|
108
|
-
end
|
109
|
-
|
110
|
-
def parent=(stream)
|
111
|
-
@dependency.parent = stream.dependency
|
112
|
-
end
|
113
|
-
|
114
93
|
def maximum_frame_size
|
115
94
|
@connection.available_frame_size
|
116
95
|
end
|
@@ -141,13 +120,13 @@ module Protocol
|
|
141
120
|
@state == :idle or @state == :reserved_local or @state == :open or @state == :half_closed_remote
|
142
121
|
end
|
143
122
|
|
144
|
-
private def write_headers(
|
123
|
+
private def write_headers(headers, flags = 0)
|
145
124
|
frame = HeadersFrame.new(@id, flags)
|
146
125
|
|
147
126
|
@connection.write_frames do |framer|
|
148
127
|
data = @connection.encode_headers(headers)
|
149
128
|
|
150
|
-
frame.pack(
|
129
|
+
frame.pack(data, maximum_size: @connection.maximum_frame_size)
|
151
130
|
|
152
131
|
framer.write_frame(frame)
|
153
132
|
end
|
@@ -157,6 +136,10 @@ module Protocol
|
|
157
136
|
|
158
137
|
# The HEADERS frame is used to open a stream, and additionally carries a header block fragment. HEADERS frames can be sent on a stream in the "idle", "reserved (local)", "open", or "half-closed (remote)" state.
|
159
138
|
def send_headers(*arguments)
|
139
|
+
if arguments.first.nil?
|
140
|
+
arguments.shift # Remove nil priority.
|
141
|
+
end
|
142
|
+
|
160
143
|
if @state == :idle
|
161
144
|
frame = write_headers(*arguments)
|
162
145
|
|
@@ -236,7 +219,7 @@ module Protocol
|
|
236
219
|
end
|
237
220
|
|
238
221
|
# Transition the stream into the closed state.
|
239
|
-
# @
|
222
|
+
# @parameter error_code [Integer] the error code if the stream was closed due to a stream reset.
|
240
223
|
def close!(error_code = nil)
|
241
224
|
@state = :closed
|
242
225
|
@connection.delete(@id)
|
@@ -265,11 +248,7 @@ module Protocol
|
|
265
248
|
|
266
249
|
def process_headers(frame)
|
267
250
|
# Receiving request headers:
|
268
|
-
|
269
|
-
|
270
|
-
if priority
|
271
|
-
@dependency.process_priority(priority)
|
272
|
-
end
|
251
|
+
data = frame.unpack
|
273
252
|
|
274
253
|
@connection.decode_headers(data)
|
275
254
|
end
|
@@ -401,7 +380,7 @@ module Protocol
|
|
401
380
|
end
|
402
381
|
|
403
382
|
# Server push is semantically equivalent to a server responding to a request; however, in this case, that request is also sent by the server, as a PUSH_PROMISE frame.
|
404
|
-
# @
|
383
|
+
# @parameter headers [Hash] contains a complete set of request header fields that the server attributes to the request.
|
405
384
|
def send_push_promise(headers)
|
406
385
|
if @state == :open or @state == :half_closed_remote
|
407
386
|
promised_stream = self.create_push_promise_stream(headers)
|
@@ -427,7 +406,6 @@ module Protocol
|
|
427
406
|
headers = @connection.decode_headers(data)
|
428
407
|
|
429
408
|
stream = self.accept_push_promise_stream(promised_stream_id, headers)
|
430
|
-
stream.parent = self
|
431
409
|
stream.reserved_remote!
|
432
410
|
|
433
411
|
return stream, headers
|
@@ -9,7 +9,7 @@ module Protocol
|
|
9
9
|
# When an HTTP/2 connection is first established, new streams are created with an initial flow-control window size of 65,535 octets. The connection flow-control window is also 65,535 octets.
|
10
10
|
DEFAULT_CAPACITY = 0xFFFF
|
11
11
|
|
12
|
-
# @
|
12
|
+
# @parameter capacity [Integer] The initial window size, typically from the settings.
|
13
13
|
def initialize(capacity = DEFAULT_CAPACITY)
|
14
14
|
# This is the main field required:
|
15
15
|
@available = capacity
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require "traces/provider"
|
7
|
+
require_relative "../../../../protocol/http2/framer"
|
8
|
+
|
9
|
+
Traces::Provider(Protocol::HTTP2::Framer) do
|
10
|
+
def write_connection_preface
|
11
|
+
Traces.trace("protocol.http2.framer.write_connection_preface") do
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def read_connection_preface
|
17
|
+
Traces.trace("protocol.http2.framer.read_connection_preface") do
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_frame(frame)
|
23
|
+
attributes = {
|
24
|
+
"frame.length" => frame.length,
|
25
|
+
"frame.class" => frame.class.name,
|
26
|
+
"frame.type" => frame.type,
|
27
|
+
"frame.flags" => frame.flags,
|
28
|
+
"frame.stream_id" => frame.stream_id,
|
29
|
+
}
|
30
|
+
|
31
|
+
Traces.trace("protocol.http2.framer.write_frame", attributes: attributes) do
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_frame(...)
|
37
|
+
Traces.trace("protocol.http2.framer.read_frame") do |span|
|
38
|
+
super.tap do |frame|
|
39
|
+
span["frame.length"] = frame.length
|
40
|
+
span["frame.type"] = frame.type
|
41
|
+
span["frame.flags"] = frame.flags
|
42
|
+
span["frame.stream_id"] = frame.stream_id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def flush
|
48
|
+
Traces.trace("protocol.http2.framer.flush") do
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/readme.md
CHANGED
@@ -58,7 +58,7 @@ Async do
|
|
58
58
|
]
|
59
59
|
|
60
60
|
puts "Sending request on stream id=#{stream.id} state=#{stream.state}..."
|
61
|
-
stream.send_headers(
|
61
|
+
stream.send_headers(headers, Protocol::HTTP2::END_STREAM)
|
62
62
|
|
63
63
|
puts "Waiting for response..."
|
64
64
|
$count = 0
|
data/releases.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Releases
|
2
|
+
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
### Remove Priority Frame and Dependency Tracking
|
6
|
+
|
7
|
+
HTTP/2 has deprecated the priority frame and stream dependency tracking. This feature has been effectively removed from the protocol. As a consequence, the internal implementation is greatly simplified. The `Protocol::HTTP2::Stream` class no longer tracks dependencies or priorities, and this includes `Stream#send_headers` which no longer takes `priority` as the first argument.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-http2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -40,7 +40,7 @@ cert_chain:
|
|
40
40
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
41
41
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
42
42
|
-----END CERTIFICATE-----
|
43
|
-
date: 2024-11-
|
43
|
+
date: 2024-11-28 00:00:00.000000000 Z
|
44
44
|
dependencies:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: protocol-hpack
|
@@ -70,20 +70,6 @@ dependencies:
|
|
70
70
|
- - "~>"
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: '0.18'
|
73
|
-
- !ruby/object:Gem::Dependency
|
74
|
-
name: traces
|
75
|
-
requirement: !ruby/object:Gem::Requirement
|
76
|
-
requirements:
|
77
|
-
- - ">="
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version: '0'
|
80
|
-
type: :runtime
|
81
|
-
prerelease: false
|
82
|
-
version_requirements: !ruby/object:Gem::Requirement
|
83
|
-
requirements:
|
84
|
-
- - ">="
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: '0'
|
87
73
|
description:
|
88
74
|
email:
|
89
75
|
executables: []
|
@@ -95,7 +81,6 @@ files:
|
|
95
81
|
- lib/protocol/http2/connection.rb
|
96
82
|
- lib/protocol/http2/continuation_frame.rb
|
97
83
|
- lib/protocol/http2/data_frame.rb
|
98
|
-
- lib/protocol/http2/dependency.rb
|
99
84
|
- lib/protocol/http2/error.rb
|
100
85
|
- lib/protocol/http2/flow_controlled.rb
|
101
86
|
- lib/protocol/http2/frame.rb
|
@@ -104,7 +89,6 @@ files:
|
|
104
89
|
- lib/protocol/http2/headers_frame.rb
|
105
90
|
- lib/protocol/http2/padded.rb
|
106
91
|
- lib/protocol/http2/ping_frame.rb
|
107
|
-
- lib/protocol/http2/priority_frame.rb
|
108
92
|
- lib/protocol/http2/push_promise_frame.rb
|
109
93
|
- lib/protocol/http2/reset_stream_frame.rb
|
110
94
|
- lib/protocol/http2/server.rb
|
@@ -113,8 +97,11 @@ files:
|
|
113
97
|
- lib/protocol/http2/version.rb
|
114
98
|
- lib/protocol/http2/window.rb
|
115
99
|
- lib/protocol/http2/window_update_frame.rb
|
100
|
+
- lib/traces/provider/protocol/http2.rb
|
101
|
+
- lib/traces/provider/protocol/http2/framer.rb
|
116
102
|
- license.md
|
117
103
|
- readme.md
|
104
|
+
- releases.md
|
118
105
|
homepage: https://github.com/socketry/protocol-http2
|
119
106
|
licenses:
|
120
107
|
- MIT
|
@@ -136,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
123
|
- !ruby/object:Gem::Version
|
137
124
|
version: '0'
|
138
125
|
requirements: []
|
139
|
-
rubygems_version: 3.5.
|
126
|
+
rubygems_version: 3.5.22
|
140
127
|
signing_key:
|
141
128
|
specification_version: 4
|
142
129
|
summary: A low level implementation of the HTTP/2 protocol.
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,218 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-2023, by Samuel Williams.
|
5
|
-
|
6
|
-
module Protocol
|
7
|
-
module HTTP2
|
8
|
-
DEFAULT_WEIGHT = 16
|
9
|
-
|
10
|
-
class Dependency
|
11
|
-
def self.create(connection, id, priority = nil)
|
12
|
-
weight = DEFAULT_WEIGHT
|
13
|
-
exclusive = false
|
14
|
-
|
15
|
-
if priority
|
16
|
-
if parent = connection.dependencies[priority.stream_dependency]
|
17
|
-
exclusive = priority.exclusive
|
18
|
-
end
|
19
|
-
|
20
|
-
weight = priority.weight
|
21
|
-
end
|
22
|
-
|
23
|
-
if parent.nil?
|
24
|
-
parent = connection.dependency
|
25
|
-
end
|
26
|
-
|
27
|
-
dependency = self.new(connection, id, weight)
|
28
|
-
|
29
|
-
connection.dependencies[id] = dependency
|
30
|
-
|
31
|
-
if exclusive
|
32
|
-
parent.exclusive_child(dependency)
|
33
|
-
else
|
34
|
-
parent.add_child(dependency)
|
35
|
-
end
|
36
|
-
|
37
|
-
return dependency
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(connection, id, weight = DEFAULT_WEIGHT)
|
41
|
-
@connection = connection
|
42
|
-
@id = id
|
43
|
-
|
44
|
-
@parent = nil
|
45
|
-
@children = nil
|
46
|
-
|
47
|
-
@weight = weight
|
48
|
-
|
49
|
-
# Cache of any associated stream:
|
50
|
-
@stream = nil
|
51
|
-
|
52
|
-
# Cache of children for window allocation:
|
53
|
-
@total_weight = 0
|
54
|
-
@ordered_children = nil
|
55
|
-
end
|
56
|
-
|
57
|
-
def <=> other
|
58
|
-
@weight <=> other.weight
|
59
|
-
end
|
60
|
-
|
61
|
-
# The connection this stream belongs to.
|
62
|
-
attr :connection
|
63
|
-
|
64
|
-
# Stream ID (odd for client initiated streams, even otherwise).
|
65
|
-
attr :id
|
66
|
-
|
67
|
-
# The parent dependency.
|
68
|
-
attr_accessor :parent
|
69
|
-
|
70
|
-
# The dependent children.
|
71
|
-
attr_accessor :children
|
72
|
-
|
73
|
-
# The weight of the stream relative to other siblings.
|
74
|
-
attr_accessor :weight
|
75
|
-
|
76
|
-
def stream
|
77
|
-
@stream ||= @connection.streams[@id]
|
78
|
-
end
|
79
|
-
|
80
|
-
def clear_cache!
|
81
|
-
@ordered_children = nil
|
82
|
-
end
|
83
|
-
|
84
|
-
def delete!
|
85
|
-
@connection.dependencies.delete(@id)
|
86
|
-
|
87
|
-
@parent.remove_child(self)
|
88
|
-
|
89
|
-
@children&.each do |id, child|
|
90
|
-
parent.add_child(child)
|
91
|
-
end
|
92
|
-
|
93
|
-
@connection = nil
|
94
|
-
@parent = nil
|
95
|
-
@children = nil
|
96
|
-
end
|
97
|
-
|
98
|
-
def add_child(dependency)
|
99
|
-
@children ||= {}
|
100
|
-
@children[dependency.id] = dependency
|
101
|
-
|
102
|
-
dependency.parent = self
|
103
|
-
|
104
|
-
self.clear_cache!
|
105
|
-
end
|
106
|
-
|
107
|
-
def remove_child(dependency)
|
108
|
-
@children&.delete(dependency.id)
|
109
|
-
|
110
|
-
self.clear_cache!
|
111
|
-
end
|
112
|
-
|
113
|
-
# An exclusive flag allows for the insertion of a new level of dependencies. The exclusive flag causes the stream to become the sole dependency of its parent stream, causing other dependencies to become dependent on the exclusive stream.
|
114
|
-
# @param parent [Dependency] the dependency which will be inserted, taking control of all current children.
|
115
|
-
def exclusive_child(parent)
|
116
|
-
parent.children = @children
|
117
|
-
|
118
|
-
@children&.each_value do |child|
|
119
|
-
child.parent = parent
|
120
|
-
end
|
121
|
-
|
122
|
-
parent.clear_cache!
|
123
|
-
|
124
|
-
@children = {parent.id => parent}
|
125
|
-
self.clear_cache!
|
126
|
-
|
127
|
-
parent.parent = self
|
128
|
-
end
|
129
|
-
|
130
|
-
def process_priority(priority)
|
131
|
-
dependent_id = priority.stream_dependency
|
132
|
-
|
133
|
-
if dependent_id == @id
|
134
|
-
raise ProtocolError, "Stream priority for stream id #{@id} cannot depend on itself!"
|
135
|
-
end
|
136
|
-
|
137
|
-
@weight = priority.weight
|
138
|
-
|
139
|
-
# We essentially ignore `dependent_id` if the dependency does not exist:
|
140
|
-
if parent = @connection.dependencies[dependent_id]
|
141
|
-
if priority.exclusive
|
142
|
-
@parent.remove_child(self)
|
143
|
-
|
144
|
-
parent.exclusive_child(self)
|
145
|
-
elsif !@parent.equal?(parent)
|
146
|
-
@parent.remove_child(self)
|
147
|
-
|
148
|
-
parent.add_child(self)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# Change the priority of the stream both locally and remotely.
|
154
|
-
def priority= priority
|
155
|
-
send_priority(priority)
|
156
|
-
process_priority(priority)
|
157
|
-
end
|
158
|
-
|
159
|
-
# The current local priority of the stream.
|
160
|
-
def priority(exclusive = false)
|
161
|
-
Priority.new(exclusive, @parent.id, @weight)
|
162
|
-
end
|
163
|
-
|
164
|
-
def send_priority(priority)
|
165
|
-
@connection.send_priority(@id, priority)
|
166
|
-
end
|
167
|
-
|
168
|
-
def receive_priority(frame)
|
169
|
-
self.process_priority(frame.unpack)
|
170
|
-
end
|
171
|
-
|
172
|
-
def total_weight
|
173
|
-
self.ordered_children
|
174
|
-
|
175
|
-
return @total_weight
|
176
|
-
end
|
177
|
-
|
178
|
-
def ordered_children
|
179
|
-
unless @ordered_children
|
180
|
-
if @children and !@children.empty?
|
181
|
-
@ordered_children = @children.values.sort
|
182
|
-
@total_weight = @ordered_children.sum(&:weight)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
return @ordered_children
|
187
|
-
end
|
188
|
-
|
189
|
-
# Traverse active streams in order of priority and allow them to consume the available flow-control window.
|
190
|
-
# @param amount [Integer] the amount of data to write. Defaults to the current window capacity.
|
191
|
-
def consume_window(size)
|
192
|
-
# If there is an associated stream, give it priority:
|
193
|
-
if stream = self.stream
|
194
|
-
return if stream.window_updated(size)
|
195
|
-
end
|
196
|
-
|
197
|
-
# Otherwise, allow the dependent children to use up the available window:
|
198
|
-
self.ordered_children&.each do |child|
|
199
|
-
# Compute the proportional allocation:
|
200
|
-
allocated = (child.weight * size) / @total_weight
|
201
|
-
|
202
|
-
child.consume_window(allocated) if allocated > 0
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def inspect
|
207
|
-
"\#<#{self.class} id=#{@id} parent id=#{@parent&.id} weight=#{@weight} #{@children&.size || 0} children>"
|
208
|
-
end
|
209
|
-
|
210
|
-
def print_hierarchy(output = $stderr, indent: 0)
|
211
|
-
output.puts "#{"\t" * indent}#{self.inspect}"
|
212
|
-
@children&.each_value do |child|
|
213
|
-
child.print_hierarchy(output, indent: indent+1)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-2024, by Samuel Williams.
|
5
|
-
|
6
|
-
require_relative "frame"
|
7
|
-
|
8
|
-
module Protocol
|
9
|
-
module HTTP2
|
10
|
-
VALID_WEIGHT = (1..256)
|
11
|
-
|
12
|
-
# Stream Dependency: A 31-bit stream identifier for the stream that
|
13
|
-
# this stream depends on (see Section 5.3). This field is only
|
14
|
-
# present if the PRIORITY flag is set.
|
15
|
-
class Priority < Struct.new(:exclusive, :stream_dependency, :weight)
|
16
|
-
FORMAT = "NC".freeze
|
17
|
-
EXCLUSIVE = 1 << 31
|
18
|
-
|
19
|
-
# All streams are initially assigned a non-exclusive dependency on stream 0x0. Pushed streams (Section 8.2) initially depend on their associated stream. In both cases, streams are assigned a default weight of 16.
|
20
|
-
def self.default(stream_dependency = 0, weight = 16)
|
21
|
-
self.new(false, stream_dependency, weight)
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.unpack(data)
|
25
|
-
stream_dependency, weight = data.unpack(FORMAT)
|
26
|
-
|
27
|
-
# Weight: An unsigned 8-bit integer representing a priority weight for the stream (see Section 5.3). Add one to the value to obtain a weight between 1 and 256. This field is only present if the PRIORITY flag is set.
|
28
|
-
return self.new(stream_dependency & EXCLUSIVE != 0, stream_dependency & ~EXCLUSIVE, weight + 1)
|
29
|
-
end
|
30
|
-
|
31
|
-
def pack
|
32
|
-
if exclusive
|
33
|
-
stream_dependency = self.stream_dependency | EXCLUSIVE
|
34
|
-
else
|
35
|
-
stream_dependency = self.stream_dependency
|
36
|
-
end
|
37
|
-
|
38
|
-
return [stream_dependency, self.weight - 1].pack(FORMAT)
|
39
|
-
end
|
40
|
-
|
41
|
-
def weight= value
|
42
|
-
if VALID_WEIGHT.include?(value)
|
43
|
-
super
|
44
|
-
else
|
45
|
-
raise ArgumentError, "Weight #{value} must be between 1-256!"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# The PRIORITY frame specifies the sender-advised priority of a stream. It can be sent in any stream state, including idle or closed streams.
|
51
|
-
#
|
52
|
-
# +-+-------------------------------------------------------------+
|
53
|
-
# |E| Stream Dependency (31) |
|
54
|
-
# +-+-------------+-----------------------------------------------+
|
55
|
-
# | Weight (8) |
|
56
|
-
# +-+-------------+
|
57
|
-
#
|
58
|
-
class PriorityFrame < Frame
|
59
|
-
TYPE = 0x2
|
60
|
-
|
61
|
-
def pack priority
|
62
|
-
super priority.pack
|
63
|
-
end
|
64
|
-
|
65
|
-
def unpack
|
66
|
-
Priority.unpack(super)
|
67
|
-
end
|
68
|
-
|
69
|
-
def apply(connection)
|
70
|
-
connection.receive_priority(self)
|
71
|
-
end
|
72
|
-
|
73
|
-
def read_payload(stream)
|
74
|
-
super
|
75
|
-
|
76
|
-
if @length != 5
|
77
|
-
raise FrameSizeError, "Invalid frame length"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|