protocol-http2 0.20.0 → 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 +7 -35
- data/lib/protocol/http2/flow_controlled.rb +1 -1
- data/lib/protocol/http2/frame.rb +2 -2
- data/lib/protocol/http2/framer.rb +1 -2
- 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/readme.md +1 -1
- data/releases.md +7 -0
- data.tar.gz.sig +0 -0
- metadata +3 -4
- metadata.gz.sig +0 -0
- data/lib/protocol/http2/dependency.rb +0 -245
- 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:
|
@@ -95,7 +90,6 @@ module Protocol
|
|
95
90
|
|
96
91
|
def delete(id)
|
97
92
|
@streams.delete(id)
|
98
|
-
@dependencies[id]&.delete!
|
99
93
|
end
|
100
94
|
|
101
95
|
# Close the underlying framer and all streams.
|
@@ -402,22 +396,6 @@ module Protocol
|
|
402
396
|
end
|
403
397
|
end
|
404
398
|
|
405
|
-
def send_priority(stream_id, priority)
|
406
|
-
frame = PriorityFrame.new(stream_id)
|
407
|
-
frame.pack(priority)
|
408
|
-
|
409
|
-
write_frame(frame)
|
410
|
-
end
|
411
|
-
|
412
|
-
# Sets the priority for an incoming stream.
|
413
|
-
def receive_priority(frame)
|
414
|
-
if dependency = @dependencies[frame.stream_id]
|
415
|
-
dependency.receive_priority(frame)
|
416
|
-
elsif idle_stream_id?(frame.stream_id)
|
417
|
-
Dependency.create(self, frame.stream_id, frame.unpack)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
399
|
def receive_push_promise(frame)
|
422
400
|
raise ProtocolError, "Unable to receive push promise!"
|
423
401
|
end
|
@@ -470,23 +448,17 @@ module Protocol
|
|
470
448
|
end
|
471
449
|
end
|
472
450
|
|
473
|
-
# Traverse active streams
|
474
|
-
# @
|
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.
|
475
453
|
def consume_window(size = self.available_size)
|
476
454
|
# Return if there is no window to consume:
|
477
455
|
return unless size > 0
|
478
456
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
# buffer.puts
|
485
|
-
#
|
486
|
-
# @dependency.print_hierarchy(buffer)
|
487
|
-
# end
|
488
|
-
|
489
|
-
@dependency.consume_window(size)
|
457
|
+
@streams.each_value do |stream|
|
458
|
+
if stream.active?
|
459
|
+
stream.window_updated(size)
|
460
|
+
end
|
461
|
+
end
|
490
462
|
end
|
491
463
|
|
492
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"
|
@@ -22,7 +21,7 @@ module Protocol
|
|
22
21
|
FRAMES = [
|
23
22
|
DataFrame,
|
24
23
|
HeadersFrame,
|
25
|
-
PriorityFrame
|
24
|
+
nil, # PriorityFrame is deprecated / removed.
|
26
25
|
ResetStreamFrame,
|
27
26
|
SettingsFrame,
|
28
27
|
PushPromiseFrame,
|
@@ -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
|
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
|
@@ -81,7 +81,6 @@ files:
|
|
81
81
|
- lib/protocol/http2/connection.rb
|
82
82
|
- lib/protocol/http2/continuation_frame.rb
|
83
83
|
- lib/protocol/http2/data_frame.rb
|
84
|
-
- lib/protocol/http2/dependency.rb
|
85
84
|
- lib/protocol/http2/error.rb
|
86
85
|
- lib/protocol/http2/flow_controlled.rb
|
87
86
|
- lib/protocol/http2/frame.rb
|
@@ -90,7 +89,6 @@ files:
|
|
90
89
|
- lib/protocol/http2/headers_frame.rb
|
91
90
|
- lib/protocol/http2/padded.rb
|
92
91
|
- lib/protocol/http2/ping_frame.rb
|
93
|
-
- lib/protocol/http2/priority_frame.rb
|
94
92
|
- lib/protocol/http2/push_promise_frame.rb
|
95
93
|
- lib/protocol/http2/reset_stream_frame.rb
|
96
94
|
- lib/protocol/http2/server.rb
|
@@ -103,6 +101,7 @@ files:
|
|
103
101
|
- lib/traces/provider/protocol/http2/framer.rb
|
104
102
|
- license.md
|
105
103
|
- readme.md
|
104
|
+
- releases.md
|
106
105
|
homepage: https://github.com/socketry/protocol-http2
|
107
106
|
licenses:
|
108
107
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,245 +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
|
-
def == other
|
62
|
-
@id == other.id
|
63
|
-
end
|
64
|
-
|
65
|
-
# The connection this stream belongs to.
|
66
|
-
attr :connection
|
67
|
-
|
68
|
-
# Stream ID (odd for client initiated streams, even otherwise).
|
69
|
-
attr :id
|
70
|
-
|
71
|
-
# The parent dependency.
|
72
|
-
attr_accessor :parent
|
73
|
-
|
74
|
-
# The dependent children.
|
75
|
-
attr_accessor :children
|
76
|
-
|
77
|
-
# The weight of the stream relative to other siblings.
|
78
|
-
attr_accessor :weight
|
79
|
-
|
80
|
-
def stream
|
81
|
-
@stream ||= @connection.streams[@id]
|
82
|
-
end
|
83
|
-
|
84
|
-
def clear_cache!
|
85
|
-
@ordered_children = nil
|
86
|
-
end
|
87
|
-
|
88
|
-
def delete!
|
89
|
-
@connection.dependencies.delete(@id)
|
90
|
-
|
91
|
-
@parent.remove_child(self)
|
92
|
-
|
93
|
-
@children&.each do |id, child|
|
94
|
-
parent.add_child(child)
|
95
|
-
end
|
96
|
-
|
97
|
-
@connection = nil
|
98
|
-
@parent = nil
|
99
|
-
@children = nil
|
100
|
-
end
|
101
|
-
|
102
|
-
def add_child(dependency)
|
103
|
-
@children ||= {}
|
104
|
-
@children[dependency.id] = dependency
|
105
|
-
|
106
|
-
dependency.parent = self
|
107
|
-
|
108
|
-
if @ordered_children
|
109
|
-
# Binary search for insertion point:
|
110
|
-
index = @ordered_children.bsearch_index do |child|
|
111
|
-
child.weight >= dependency.weight
|
112
|
-
end
|
113
|
-
|
114
|
-
if index
|
115
|
-
@ordered_children.insert(index, dependency)
|
116
|
-
else
|
117
|
-
@ordered_children.push(dependency)
|
118
|
-
end
|
119
|
-
|
120
|
-
@total_weight += dependency.weight
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def remove_child(dependency)
|
125
|
-
@children&.delete(dependency.id)
|
126
|
-
|
127
|
-
if @ordered_children
|
128
|
-
# Don't do a linear search here, it can be slow. Instead, the child's parent will be set to `nil`, and we check this in {#consume_window} using `delete_if`.
|
129
|
-
# @ordered_children.delete(dependency)
|
130
|
-
@total_weight -= dependency.weight
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# 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.
|
135
|
-
# @param parent [Dependency] the dependency which will be inserted, taking control of all current children.
|
136
|
-
def exclusive_child(parent)
|
137
|
-
parent.children = @children
|
138
|
-
|
139
|
-
@children&.each_value do |child|
|
140
|
-
child.parent = parent
|
141
|
-
end
|
142
|
-
|
143
|
-
parent.clear_cache!
|
144
|
-
|
145
|
-
@children = {parent.id => parent}
|
146
|
-
self.clear_cache!
|
147
|
-
|
148
|
-
parent.parent = self
|
149
|
-
end
|
150
|
-
|
151
|
-
def process_priority(priority)
|
152
|
-
dependent_id = priority.stream_dependency
|
153
|
-
|
154
|
-
if dependent_id == @id
|
155
|
-
raise ProtocolError, "Stream priority for stream id #{@id} cannot depend on itself!"
|
156
|
-
end
|
157
|
-
|
158
|
-
@weight = priority.weight
|
159
|
-
|
160
|
-
# We essentially ignore `dependent_id` if the dependency does not exist:
|
161
|
-
if parent = @connection.dependencies[dependent_id]
|
162
|
-
if priority.exclusive
|
163
|
-
@parent.remove_child(self)
|
164
|
-
|
165
|
-
parent.exclusive_child(self)
|
166
|
-
elsif !@parent.equal?(parent)
|
167
|
-
@parent.remove_child(self)
|
168
|
-
|
169
|
-
parent.add_child(self)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Change the priority of the stream both locally and remotely.
|
175
|
-
def priority= priority
|
176
|
-
send_priority(priority)
|
177
|
-
process_priority(priority)
|
178
|
-
end
|
179
|
-
|
180
|
-
# The current local priority of the stream.
|
181
|
-
def priority(exclusive = false)
|
182
|
-
Priority.new(exclusive, @parent.id, @weight)
|
183
|
-
end
|
184
|
-
|
185
|
-
def send_priority(priority)
|
186
|
-
@connection.send_priority(@id, priority)
|
187
|
-
end
|
188
|
-
|
189
|
-
def receive_priority(frame)
|
190
|
-
self.process_priority(frame.unpack)
|
191
|
-
end
|
192
|
-
|
193
|
-
def total_weight
|
194
|
-
self.ordered_children
|
195
|
-
|
196
|
-
return @total_weight
|
197
|
-
end
|
198
|
-
|
199
|
-
def ordered_children
|
200
|
-
unless @ordered_children
|
201
|
-
if @children and !@children.empty?
|
202
|
-
@ordered_children = @children.values.sort
|
203
|
-
@total_weight = @ordered_children.sum(&:weight)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
return @ordered_children
|
208
|
-
end
|
209
|
-
|
210
|
-
# Traverse active streams in order of priority and allow them to consume the available flow-control window.
|
211
|
-
# @param amount [Integer] the amount of data to write. Defaults to the current window capacity.
|
212
|
-
def consume_window(size)
|
213
|
-
# If there is an associated stream, give it priority:
|
214
|
-
if stream = self.stream
|
215
|
-
return if stream.window_updated(size)
|
216
|
-
end
|
217
|
-
|
218
|
-
# Otherwise, allow the dependent children to use up the available window:
|
219
|
-
self.ordered_children&.delete_if do |child|
|
220
|
-
if child.parent
|
221
|
-
# Compute the proportional allocation:
|
222
|
-
allocated = (child.weight * size) / @total_weight
|
223
|
-
|
224
|
-
child.consume_window(allocated) if allocated > 0
|
225
|
-
|
226
|
-
false
|
227
|
-
else
|
228
|
-
true
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
def inspect
|
234
|
-
"\#<#{self.class} id=#{@id} parent id=#{@parent&.id} weight=#{@weight} #{@children&.size || 0} children>"
|
235
|
-
end
|
236
|
-
|
237
|
-
def print_hierarchy(output = $stderr, indent: 0)
|
238
|
-
output.puts "#{"\t" * indent}#{self.inspect}"
|
239
|
-
@children&.each_value do |child|
|
240
|
-
child.print_hierarchy(output, indent: indent+1)
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
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
|