protocol-http2 0.11.5 → 0.11.6
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/.travis.yml +1 -2
- data/lib/protocol/http2/connection.rb +38 -26
- data/lib/protocol/http2/dependency.rb +91 -49
- data/lib/protocol/http2/flow_controlled.rb +0 -1
- data/lib/protocol/http2/frame.rb +0 -3
- data/lib/protocol/http2/headers_frame.rb +3 -0
- data/lib/protocol/http2/stream.rb +6 -6
- data/lib/protocol/http2/version.rb +1 -1
- data/protocol-http2.gemspec +2 -0
- metadata +5 -7
- data/lib/protocol/http2/extensions/sum.rb +0 -37
- data/lib/protocol/http2/extensions/unpack.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d8810aec1217f41c16dc254d8a38839766d0a6717ef1d8483b2c21eaa367139
|
4
|
+
data.tar.gz: 88cb94b5292c4fa1f430de2ee625a11650cd5564bc11a8e2df47eda25f8ce685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7119f9553cb2289f9dda868dc50f2624fd1d1205a81293e3fa1ec159424f3566adf7c51eb597ae47a3e421fa0510278071284cc951b87930b496b861321160d6
|
7
|
+
data.tar.gz: 3a270e16a604ab0fd9b251443472a9ff952a7db25065bd7c29d35fa00cf48bfe8d8c163d35f27d75366ebea50acac3f91cb296feb869c678dce5597cfcf97204
|
data/.travis.yml
CHANGED
@@ -63,14 +63,6 @@ module Protocol
|
|
63
63
|
0
|
64
64
|
end
|
65
65
|
|
66
|
-
def parent
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
|
70
|
-
def children
|
71
|
-
@dependency.streams
|
72
|
-
end
|
73
|
-
|
74
66
|
def [] id
|
75
67
|
if id.zero?
|
76
68
|
self
|
@@ -113,10 +105,7 @@ module Protocol
|
|
113
105
|
|
114
106
|
def delete(id)
|
115
107
|
@streams.delete(id)
|
116
|
-
|
117
|
-
if dependency = @dependencies[id]
|
118
|
-
dependency.delete! if dependency.irrelevant?
|
119
|
-
end
|
108
|
+
@dependencies[id]&.delete!
|
120
109
|
end
|
121
110
|
|
122
111
|
# Close the underlying framer and all streams.
|
@@ -145,20 +134,10 @@ module Protocol
|
|
145
134
|
end
|
146
135
|
|
147
136
|
attr :streams
|
137
|
+
|
148
138
|
attr :dependencies
|
149
139
|
|
150
|
-
|
151
|
-
# @param id [Integer] the stream id.
|
152
|
-
def dependency_for(id)
|
153
|
-
@dependencies.fetch(id) do
|
154
|
-
dependency = Dependency.new(self, id)
|
155
|
-
|
156
|
-
# TODO this might be irrelevant, if initially processing priority frame.
|
157
|
-
@dependency.add_child(dependency)
|
158
|
-
|
159
|
-
@dependencies[id] = dependency
|
160
|
-
end
|
161
|
-
end
|
140
|
+
attr :dependency
|
162
141
|
|
163
142
|
# 6.8. GOAWAY
|
164
143
|
# There is an inherent race condition between an endpoint starting new streams and the remote sending a GOAWAY frame. To deal with this case, the GOAWAY contains the stream identifier of the last peer-initiated stream that was or might be processed on the sending endpoint in this connection. For instance, if the server sends a GOAWAY frame, the identified stream is the highest-numbered stream initiated by the client.
|
@@ -341,6 +320,8 @@ module Protocol
|
|
341
320
|
|
342
321
|
if stream = @streams[frame.stream_id]
|
343
322
|
stream.receive_data(frame)
|
323
|
+
elsif closed_stream_id?(frame.stream_id)
|
324
|
+
# This can occur if one end sent a stream reset, while the other end was sending a data frame. It's mostly harmless.
|
344
325
|
else
|
345
326
|
raise ProtocolError, "Cannot receive data for stream id #{frame.stream_id}"
|
346
327
|
end
|
@@ -415,10 +396,31 @@ module Protocol
|
|
415
396
|
write_frame(frame)
|
416
397
|
end
|
417
398
|
|
399
|
+
def idle_stream_id?(id)
|
400
|
+
if id.even?
|
401
|
+
# Server-initiated streams are even.
|
402
|
+
if @local_stream_id.even?
|
403
|
+
id >= @local_stream_id
|
404
|
+
else
|
405
|
+
id > @remote_stream_id
|
406
|
+
end
|
407
|
+
elsif id.odd?
|
408
|
+
# Client-initiated streams are odd.
|
409
|
+
if @local_stream_id.odd?
|
410
|
+
id >= @local_stream_id
|
411
|
+
else
|
412
|
+
id > @remote_stream_id
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
418
417
|
# Sets the priority for an incoming stream.
|
419
418
|
def receive_priority(frame)
|
420
|
-
dependency =
|
421
|
-
|
419
|
+
if dependency = @dependencies[frame.stream_id]
|
420
|
+
dependency.receive_priority(frame)
|
421
|
+
elsif idle_stream_id?(frame.stream_id)
|
422
|
+
Dependency.create(self, frame.stream_id, frame.unpack)
|
423
|
+
end
|
422
424
|
end
|
423
425
|
|
424
426
|
def receive_push_promise(frame)
|
@@ -472,6 +474,16 @@ module Protocol
|
|
472
474
|
# Return if there is no window to consume:
|
473
475
|
return unless size > 0
|
474
476
|
|
477
|
+
# Console.logger.debug(self) do |buffer|
|
478
|
+
# @dependencies.each do |id, dependency|
|
479
|
+
# buffer.puts "- #{dependency}"
|
480
|
+
# end
|
481
|
+
#
|
482
|
+
# buffer.puts
|
483
|
+
#
|
484
|
+
# @dependency.print_hierarchy(buffer)
|
485
|
+
# end
|
486
|
+
|
475
487
|
@dependency.consume_window(size)
|
476
488
|
end
|
477
489
|
|
@@ -23,16 +23,44 @@ module Protocol
|
|
23
23
|
DEFAULT_WEIGHT = 16
|
24
24
|
|
25
25
|
class Dependency
|
26
|
-
def
|
26
|
+
def self.create(connection, id, priority = nil)
|
27
|
+
weight = DEFAULT_WEIGHT
|
28
|
+
exclusive = false
|
29
|
+
|
30
|
+
if priority
|
31
|
+
if parent = connection.dependencies[priority.stream_dependency]
|
32
|
+
exclusive = priority.exclusive
|
33
|
+
end
|
34
|
+
|
35
|
+
weight = priority.weight
|
36
|
+
end
|
37
|
+
|
38
|
+
if parent.nil?
|
39
|
+
parent = connection.dependency
|
40
|
+
end
|
41
|
+
|
42
|
+
dependency = self.new(connection, id, weight)
|
43
|
+
|
44
|
+
connection.dependencies[id] = dependency
|
45
|
+
|
46
|
+
if exclusive
|
47
|
+
parent.exclusive_child(dependency)
|
48
|
+
else
|
49
|
+
parent.add_child(dependency)
|
50
|
+
end
|
51
|
+
|
52
|
+
return dependency
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(connection, id, weight = DEFAULT_WEIGHT)
|
27
56
|
@connection = connection
|
28
57
|
@id = id
|
29
58
|
|
30
|
-
|
31
|
-
@dependent_id = dependent_id
|
32
|
-
@weight = weight
|
33
|
-
|
59
|
+
@parent = nil
|
34
60
|
@children = nil
|
35
61
|
|
62
|
+
@weight = weight
|
63
|
+
|
36
64
|
# Cache of any associated stream:
|
37
65
|
@stream = nil
|
38
66
|
|
@@ -45,26 +73,17 @@ module Protocol
|
|
45
73
|
@weight <=> other.weight
|
46
74
|
end
|
47
75
|
|
48
|
-
def irrelevant?
|
49
|
-
(@weight == DEFAULT_WEIGHT) && (@children.nil? || @children.empty?)
|
50
|
-
end
|
51
|
-
|
52
|
-
def delete!
|
53
|
-
@connection.dependencies.delete(@id)
|
54
|
-
self.parent&.remove_child(self)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Cache of dependent children.
|
58
|
-
attr_accessor :children
|
59
|
-
|
60
76
|
# The connection this stream belongs to.
|
61
77
|
attr :connection
|
62
78
|
|
63
79
|
# Stream ID (odd for client initiated streams, even otherwise).
|
64
80
|
attr :id
|
65
81
|
|
66
|
-
# The
|
67
|
-
attr_accessor :
|
82
|
+
# The parent dependency.
|
83
|
+
attr_accessor :parent
|
84
|
+
|
85
|
+
# The dependent children.
|
86
|
+
attr_accessor :children
|
68
87
|
|
69
88
|
# The weight of the stream relative to other siblings.
|
70
89
|
attr_accessor :weight
|
@@ -77,10 +96,26 @@ module Protocol
|
|
77
96
|
@ordered_children = nil
|
78
97
|
end
|
79
98
|
|
99
|
+
def delete!
|
100
|
+
@connection.dependencies.delete(@id)
|
101
|
+
|
102
|
+
@parent.remove_child(self)
|
103
|
+
|
104
|
+
@children&.each do |id, child|
|
105
|
+
parent.add_child(child)
|
106
|
+
end
|
107
|
+
|
108
|
+
@connection = nil
|
109
|
+
@parent = nil
|
110
|
+
@children = nil
|
111
|
+
end
|
112
|
+
|
80
113
|
def add_child(dependency)
|
81
114
|
@children ||= {}
|
82
115
|
@children[dependency.id] = dependency
|
83
116
|
|
117
|
+
dependency.parent = self
|
118
|
+
|
84
119
|
self.clear_cache!
|
85
120
|
end
|
86
121
|
|
@@ -90,33 +125,24 @@ module Protocol
|
|
90
125
|
self.clear_cache!
|
91
126
|
end
|
92
127
|
|
128
|
+
# 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.
|
129
|
+
# @param parent [Dependency] the dependency which will be inserted, taking control of all current children.
|
93
130
|
def exclusive_child(parent)
|
94
131
|
parent.children = @children
|
95
|
-
parent.clear_cache!
|
96
132
|
|
97
|
-
@children
|
98
|
-
child.
|
133
|
+
@children&.each_value do |child|
|
134
|
+
child.parent = parent
|
99
135
|
end
|
100
136
|
|
137
|
+
parent.clear_cache!
|
138
|
+
|
101
139
|
@children = {parent.id => parent}
|
102
140
|
self.clear_cache!
|
103
141
|
|
104
|
-
parent.
|
105
|
-
end
|
106
|
-
|
107
|
-
def parent(id = @dependent_id)
|
108
|
-
@connection.dependency_for(id)
|
109
|
-
end
|
110
|
-
|
111
|
-
def parent= dependency
|
112
|
-
self.parent&.remove_child(self)
|
113
|
-
|
114
|
-
@dependent_id = dependency.id
|
115
|
-
|
116
|
-
dependency.add_child(self)
|
142
|
+
parent.parent = self
|
117
143
|
end
|
118
144
|
|
119
|
-
def process_priority
|
145
|
+
def process_priority(priority)
|
120
146
|
dependent_id = priority.stream_dependency
|
121
147
|
|
122
148
|
if dependent_id == @id
|
@@ -125,18 +151,17 @@ module Protocol
|
|
125
151
|
|
126
152
|
@weight = priority.weight
|
127
153
|
|
128
|
-
if
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
self.parent&.clear_cache!
|
154
|
+
# We essentially ignore `dependent_id` if the dependency does not exist:
|
155
|
+
if parent = @connection.dependencies[dependent_id]
|
156
|
+
if priority.exclusive
|
157
|
+
@parent.remove_child(self)
|
158
|
+
|
159
|
+
parent.exclusive_child(self)
|
160
|
+
elsif !@parent.equal?(parent)
|
161
|
+
@parent.remove_child(self)
|
162
|
+
|
163
|
+
parent.add_child(self)
|
164
|
+
end
|
140
165
|
end
|
141
166
|
end
|
142
167
|
|
@@ -148,7 +173,7 @@ module Protocol
|
|
148
173
|
|
149
174
|
# The current local priority of the stream.
|
150
175
|
def priority(exclusive = false)
|
151
|
-
Priority.new(exclusive, @
|
176
|
+
Priority.new(exclusive, @parent.id, @weight)
|
152
177
|
end
|
153
178
|
|
154
179
|
def send_priority(priority)
|
@@ -159,6 +184,12 @@ module Protocol
|
|
159
184
|
self.process_priority(frame.unpack)
|
160
185
|
end
|
161
186
|
|
187
|
+
def total_weight
|
188
|
+
self.orderd_children
|
189
|
+
|
190
|
+
return @total_weight
|
191
|
+
end
|
192
|
+
|
162
193
|
def ordered_children
|
163
194
|
unless @ordered_children
|
164
195
|
if @children and !@children.empty?
|
@@ -186,6 +217,17 @@ module Protocol
|
|
186
217
|
child.consume_window(allocated) if allocated > 0
|
187
218
|
end
|
188
219
|
end
|
220
|
+
|
221
|
+
def to_s
|
222
|
+
"\#<#{self.class} id=#{@id} parent id=#{@parent&.id} weight=#{@weight} #{@children&.size || 0} children>"
|
223
|
+
end
|
224
|
+
|
225
|
+
def print_hierarchy(buffer, indent: 0)
|
226
|
+
buffer.puts "#{" " * indent}#{self}"
|
227
|
+
@children&.each_value do |child|
|
228
|
+
child.print_hierarchy(buffer, indent: indent+1)
|
229
|
+
end
|
230
|
+
end
|
189
231
|
end
|
190
232
|
end
|
191
233
|
end
|
data/lib/protocol/http2/frame.rb
CHANGED
@@ -91,7 +91,7 @@ module Protocol
|
|
91
91
|
@local_window = Window.new(@connection.local_settings.initial_window_size)
|
92
92
|
@remote_window = Window.new(@connection.remote_settings.initial_window_size)
|
93
93
|
|
94
|
-
@dependency =
|
94
|
+
@dependency = Dependency.create(@connection, @id)
|
95
95
|
end
|
96
96
|
|
97
97
|
# The connection this stream belongs to.
|
@@ -163,9 +163,9 @@ module Protocol
|
|
163
163
|
end
|
164
164
|
|
165
165
|
# 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.
|
166
|
-
def send_headers(*
|
166
|
+
def send_headers(*arguments)
|
167
167
|
if @state == :idle
|
168
|
-
frame = write_headers(*
|
168
|
+
frame = write_headers(*arguments)
|
169
169
|
|
170
170
|
if frame.end_stream?
|
171
171
|
@state = :half_closed_local
|
@@ -173,17 +173,17 @@ module Protocol
|
|
173
173
|
open!
|
174
174
|
end
|
175
175
|
elsif @state == :reserved_local
|
176
|
-
frame = write_headers(*
|
176
|
+
frame = write_headers(*arguments)
|
177
177
|
|
178
178
|
@state = :half_closed_remote
|
179
179
|
elsif @state == :open
|
180
|
-
frame = write_headers(*
|
180
|
+
frame = write_headers(*arguments)
|
181
181
|
|
182
182
|
if frame.end_stream?
|
183
183
|
@state = :half_closed_local
|
184
184
|
end
|
185
185
|
elsif @state == :half_closed_remote
|
186
|
-
frame = write_headers(*
|
186
|
+
frame = write_headers(*arguments)
|
187
187
|
|
188
188
|
if frame.end_stream?
|
189
189
|
close!
|
data/protocol-http2.gemspec
CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.required_ruby_version = '~> 2.4'
|
22
|
+
|
21
23
|
spec.add_dependency "protocol-hpack", "~> 1.4"
|
22
24
|
spec.add_dependency "protocol-http", "~> 0.15"
|
23
25
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-http2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: protocol-hpack
|
@@ -116,8 +116,6 @@ files:
|
|
116
116
|
- lib/protocol/http2/data_frame.rb
|
117
117
|
- lib/protocol/http2/dependency.rb
|
118
118
|
- lib/protocol/http2/error.rb
|
119
|
-
- lib/protocol/http2/extensions/sum.rb
|
120
|
-
- lib/protocol/http2/extensions/unpack.rb
|
121
119
|
- lib/protocol/http2/flow_controlled.rb
|
122
120
|
- lib/protocol/http2/frame.rb
|
123
121
|
- lib/protocol/http2/framer.rb
|
@@ -144,16 +142,16 @@ require_paths:
|
|
144
142
|
- lib
|
145
143
|
required_ruby_version: !ruby/object:Gem::Requirement
|
146
144
|
requirements:
|
147
|
-
- - "
|
145
|
+
- - "~>"
|
148
146
|
- !ruby/object:Gem::Version
|
149
|
-
version: '
|
147
|
+
version: '2.4'
|
150
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
149
|
requirements:
|
152
150
|
- - ">="
|
153
151
|
- !ruby/object:Gem::Version
|
154
152
|
version: '0'
|
155
153
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
154
|
+
rubygems_version: 3.0.6
|
157
155
|
signing_key:
|
158
156
|
specification_version: 4
|
159
157
|
summary: A low level implementation of the HTTP/2 protocol.
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# Copyright, 2019, by Yuta Iwama.
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
module Protocol
|
22
|
-
module HTTP2
|
23
|
-
module Extensions
|
24
|
-
module Sum
|
25
|
-
def sum(init = 0)
|
26
|
-
reduce(init) do |acc, v|
|
27
|
-
acc + (block_given? ? yield(v) : v)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
unless Array.instance_methods.include?(:sum)
|
36
|
-
Array.prepend(Protocol::HTTP2::Extensions::Sum)
|
37
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# Copyright, 2019, by Yuta Iwama.
|
2
|
-
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
-
# of this software and associated documentation files (the "Software"), to deal
|
6
|
-
# in the Software without restriction, including without limitation the rights
|
7
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
-
# copies of the Software, and to permit persons to whom the Software is
|
9
|
-
# furnished to do so, subject to the following conditions:
|
10
|
-
#
|
11
|
-
# The above copyright notice and this permission notice shall be included in
|
12
|
-
# all copies or substantial portions of the Software.
|
13
|
-
#
|
14
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
-
# THE SOFTWARE.
|
21
|
-
|
22
|
-
require 'protocol/http/error'
|
23
|
-
|
24
|
-
module Protocol
|
25
|
-
module HTTP2
|
26
|
-
module Extensions
|
27
|
-
module Unpack
|
28
|
-
def unpack1(template)
|
29
|
-
unpack(template).first
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
unless String.instance_methods.include?(:unpack1)
|
37
|
-
String.prepend(Protocol::HTTP2::Extensions::Unpack)
|
38
|
-
end
|