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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 478bea0c69707c0ef4e07443b9438e61b2522a0a46fa7287ada3c07bc3b60916
4
- data.tar.gz: 38ca40af5bab92c1125665a88cae0f0fd6faef5852de8e523b58753d960ca07c
3
+ metadata.gz: 4d8810aec1217f41c16dc254d8a38839766d0a6717ef1d8483b2c21eaa367139
4
+ data.tar.gz: 88cb94b5292c4fa1f430de2ee625a11650cd5564bc11a8e2df47eda25f8ce685
5
5
  SHA512:
6
- metadata.gz: 3f6e3913fab16fceb8567a716ca7d1b7576a2af6af52a4cdd4a2cd83a3ab8e51faa3d0f31b3e6bb2c9953055c1ead44386b3013405703dd606939113893393dc
7
- data.tar.gz: 3a58fbefd9f02483e044226f05bac8d7939f03b4c65bfd28341a065bd80cb90d93110ebb6bcf3c13169f7c7ff800e2ba5076f551f3b7c9010cc4fd9856e3c245
6
+ metadata.gz: 7119f9553cb2289f9dda868dc50f2624fd1d1205a81293e3fa1ec159424f3566adf7c51eb597ae47a3e421fa0510278071284cc951b87930b496b861321160d6
7
+ data.tar.gz: 3a270e16a604ab0fd9b251443472a9ff952a7db25065bd7c29d35fa00cf48bfe8d8c163d35f27d75366ebea50acac3f91cb296feb869c678dce5597cfcf97204
data/.travis.yml CHANGED
@@ -4,11 +4,10 @@ cache: bundler
4
4
 
5
5
  matrix:
6
6
  include:
7
- - rvm: 2.3
8
7
  - rvm: 2.4
9
8
  - rvm: 2.5
10
9
  - rvm: 2.6
11
- - rvm: 2.7
10
+ - rvm: 2.7
12
11
  - rvm: 2.6
13
12
  env: COVERAGE=PartialSummary,Coveralls
14
13
  - rvm: truffleruby
@@ -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
- # Fetch (or create) the flow control windows for the specified stream id.
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 = dependency_for(frame.stream_id)
421
- dependency.receive_priority(frame)
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 initialize(connection, id, dependent_id = 0, weight = DEFAULT_WEIGHT)
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
- # Stream priority:
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 stream id that this stream depends on, according to the priority.
67
- attr_accessor :dependent_id
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.each_value do |child|
98
- child.dependent_id = parent.id
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.dependent_id = @id
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 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 priority.exclusive
129
- self.parent&.remove_child(self)
130
-
131
- self.parent(dependent_id).exclusive_child(self)
132
- elsif dependent_id != @dependent_id
133
- self.parent&.remove_child(self)
134
-
135
- @dependent_id = dependent_id
136
-
137
- self.parent.add_child(self)
138
- else
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, @dependent_id, @weight)
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
@@ -19,7 +19,6 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'window_update_frame'
22
- require_relative 'extensions/sum'
23
22
 
24
23
  module Protocol
25
24
  module HTTP2
@@ -21,9 +21,6 @@
21
21
 
22
22
  require_relative 'error'
23
23
 
24
- # This only exists to support Ruby 2.3:
25
- require_relative 'extensions/unpack'
26
-
27
24
  module Protocol
28
25
  module HTTP2
29
26
  END_STREAM = 0x1
@@ -68,6 +68,9 @@ module Protocol
68
68
 
69
69
  if priority
70
70
  buffer << priority.pack
71
+ set_flags(PRIORITY)
72
+ else
73
+ clear_flags(PRIORITY)
71
74
  end
72
75
 
73
76
  buffer << data
@@ -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 = @connection.dependency_for(@id)
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(*args)
166
+ def send_headers(*arguments)
167
167
  if @state == :idle
168
- frame = write_headers(*args)
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(*args)
176
+ frame = write_headers(*arguments)
177
177
 
178
178
  @state = :half_closed_remote
179
179
  elsif @state == :open
180
- frame = write_headers(*args)
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(*args)
186
+ frame = write_headers(*arguments)
187
187
 
188
188
  if frame.end_stream?
189
189
  close!
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Protocol
22
22
  module HTTP2
23
- VERSION = "0.11.5"
23
+ VERSION = "0.11.6"
24
24
  end
25
25
  end
@@ -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.5
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-21 00:00:00.000000000 Z
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: '0'
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.1.2
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