protocol-http2 0.11.5 → 0.11.6

Sign up to get free protection for your applications and to get access to all the features.
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