http-2 1.2.1 → 1.2.2
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/README.md +9 -0
- data/lib/http/2/connection.rb +56 -18
- data/lib/http/2/error.rb +1 -1
- data/lib/http/2/version.rb +1 -1
- data/sig/connection.rbs +5 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b41c25d0512d4056a38265801e7eef05df3cab5720ecbe11ff088c906752019
|
|
4
|
+
data.tar.gz: 0b7322ff7f49e8dd892a9804a1a710be23f3c7105f76f7c67f62b281843616fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32b4191d53effc33d5186da7093af95698809f2fec89fe37ce01898628d598e7a1215704875ec06ebd9fcaf84b11517310ee46631fb7a2c6b4a2a17fb3ad8b94
|
|
7
|
+
data.tar.gz: 5ce7ec0abedca471a25e45883ba4b8d315eb1e78879ed5a8c563dc6a58a7e128bbe9f78f878a8d9956dd90254718634bc67323b57039cb33e87d6e1552cac392
|
data/README.md
CHANGED
|
@@ -85,6 +85,15 @@ Events emitted by the connection object:
|
|
|
85
85
|
</tr>
|
|
86
86
|
</table>
|
|
87
87
|
|
|
88
|
+
Connection shutdown happens in two steps. Once a GOAWAY frame is sent or
|
|
89
|
+
received, no new streams may be opened: `closed?` turns true and `new_stream`
|
|
90
|
+
raises `ConnectionClosed` — new work belongs on a new connection. After a
|
|
91
|
+
graceful GOAWAY (`:no_error`), streams the peer may still complete keep
|
|
92
|
+
running: `closing?` distinguishes this draining state, in which the connection
|
|
93
|
+
keeps processing frames — including connection-level flow control — until the
|
|
94
|
+
last tracked stream closes. Streams the GOAWAY refused (id above its
|
|
95
|
+
last-stream-id) are reported via the `:goaway` event, so their callers can
|
|
96
|
+
retry them on a fresh connection.
|
|
88
97
|
|
|
89
98
|
### Stream lifecycle management
|
|
90
99
|
|
data/lib/http/2/connection.rb
CHANGED
|
@@ -56,7 +56,8 @@ module HTTP2
|
|
|
56
56
|
include Error
|
|
57
57
|
include BufferUtils
|
|
58
58
|
|
|
59
|
-
# Connection state (:
|
|
59
|
+
# Connection state (:waiting_magic, :waiting_connection_preface,
|
|
60
|
+
# :connected, :closing, :closed).
|
|
60
61
|
attr_reader :state
|
|
61
62
|
|
|
62
63
|
# Size of current connection flow control window (by default, set to
|
|
@@ -113,8 +114,15 @@ module HTTP2
|
|
|
113
114
|
@send_buffer = FrameBuffer.new
|
|
114
115
|
end
|
|
115
116
|
|
|
117
|
+
# No new streams may be opened (a GOAWAY was sent or received).
|
|
116
118
|
def closed?
|
|
117
|
-
@state == :closed
|
|
119
|
+
@state == :closed || @state == :closing
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Graceful GOAWAY received: tracked streams may still complete,
|
|
123
|
+
# but no new ones can be opened.
|
|
124
|
+
def closing?
|
|
125
|
+
@state == :closing
|
|
118
126
|
end
|
|
119
127
|
|
|
120
128
|
# Allocates new stream for current connection.
|
|
@@ -123,12 +131,15 @@ module HTTP2
|
|
|
123
131
|
# @param window [Integer]
|
|
124
132
|
# @param parent [Stream]
|
|
125
133
|
def new_stream(**args)
|
|
126
|
-
raise ConnectionClosed if
|
|
127
|
-
raise StreamLimitExceeded if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams]
|
|
134
|
+
raise ConnectionClosed if closed?
|
|
128
135
|
|
|
129
136
|
connection_error(:protocol_error, msg: "id is smaller than previous") if @stream_id < @last_stream_id
|
|
130
137
|
|
|
131
|
-
stream = activate_stream(
|
|
138
|
+
stream = activate_stream(
|
|
139
|
+
id: @stream_id,
|
|
140
|
+
max_concurrent_streams: @remote_settings[:settings_max_concurrent_streams],
|
|
141
|
+
**args
|
|
142
|
+
)
|
|
132
143
|
@last_stream_id = stream.id
|
|
133
144
|
|
|
134
145
|
@stream_id += 2
|
|
@@ -158,8 +169,7 @@ module HTTP2
|
|
|
158
169
|
def goaway(error = :no_error, payload = nil)
|
|
159
170
|
send(type: :goaway, stream: 0, last_stream: @last_stream_id,
|
|
160
171
|
error: error, payload: payload)
|
|
161
|
-
|
|
162
|
-
@closed_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
172
|
+
close!
|
|
163
173
|
end
|
|
164
174
|
|
|
165
175
|
# Sends a WINDOW_UPDATE frame to the peer.
|
|
@@ -295,7 +305,11 @@ module HTTP2
|
|
|
295
305
|
# PUSH_PROMISE and CONTINUATION frames MUST be minimally
|
|
296
306
|
# processed to ensure a consistent compression state
|
|
297
307
|
decode_headers(frame)
|
|
298
|
-
|
|
308
|
+
# After a GOAWAY, HEADERS may only complete a tracked stream at
|
|
309
|
+
# or below the highest id seen (Section 6.8); anything else is
|
|
310
|
+
# discarded - decoded above, so the compression state is intact.
|
|
311
|
+
# Skip only this frame: later ones may serve completing streams.
|
|
312
|
+
next if closed? && (stream_id > @last_stream_id || !@streams.key?(stream_id))
|
|
299
313
|
|
|
300
314
|
stream = @streams[stream_id]
|
|
301
315
|
if stream.nil?
|
|
@@ -323,7 +337,7 @@ module HTTP2
|
|
|
323
337
|
end
|
|
324
338
|
|
|
325
339
|
decode_headers(frame)
|
|
326
|
-
|
|
340
|
+
next if closed?
|
|
327
341
|
|
|
328
342
|
# PUSH_PROMISE frames MUST be associated with an existing, peer-
|
|
329
343
|
# initiated stream... A receiver MUST treat the receipt of a
|
|
@@ -375,11 +389,16 @@ module HTTP2
|
|
|
375
389
|
end
|
|
376
390
|
else
|
|
377
391
|
case frame_type
|
|
378
|
-
#
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
# unused or closed parent stream.
|
|
392
|
+
# Priority signaling is deprecated (RFC 9113 Section 5.3.2), but
|
|
393
|
+
# a PRIORITY frame may still name a stream id not opened yet:
|
|
394
|
+
# activate it, so the priority sticks if the stream opens.
|
|
382
395
|
when :priority
|
|
396
|
+
# After a GOAWAY, a PRIORITY for a stream already used and
|
|
397
|
+
# closed must not resurrect it: reprioritizing a closed
|
|
398
|
+
# parent is a no-op here, and a phantom stream would hold
|
|
399
|
+
# the draining connection open.
|
|
400
|
+
next if closed? && stream_id <= @last_stream_id
|
|
401
|
+
|
|
383
402
|
stream = activate_stream(
|
|
384
403
|
id: stream_id,
|
|
385
404
|
weight: frame[:weight] || DEFAULT_WEIGHT,
|
|
@@ -495,7 +514,7 @@ module HTTP2
|
|
|
495
514
|
@state = :connected
|
|
496
515
|
connection_settings(frame)
|
|
497
516
|
|
|
498
|
-
when :connected
|
|
517
|
+
when :connected, :closing
|
|
499
518
|
case frame_type
|
|
500
519
|
when :settings
|
|
501
520
|
# @type var frame: settings_frame
|
|
@@ -510,8 +529,13 @@ module HTTP2
|
|
|
510
529
|
# Receivers of a GOAWAY frame MUST NOT open additional streams on
|
|
511
530
|
# the connection, although a new connection can be established
|
|
512
531
|
# for new streams.
|
|
513
|
-
|
|
514
|
-
|
|
532
|
+
if frame[:error] == :no_error && !@streams.empty?
|
|
533
|
+
# A graceful GOAWAY lets tracked streams complete before the
|
|
534
|
+
# connection closes (Section 6.8).
|
|
535
|
+
@state = :closing
|
|
536
|
+
else
|
|
537
|
+
close!
|
|
538
|
+
end
|
|
515
539
|
emit(:goaway, frame[:last_stream], frame[:error], frame[:payload])
|
|
516
540
|
when :altsvc
|
|
517
541
|
# @type var frame: altsvc_frame
|
|
@@ -768,19 +792,28 @@ module HTTP2
|
|
|
768
792
|
# connection managemet callbacks.
|
|
769
793
|
#
|
|
770
794
|
# @param id [Integer]
|
|
795
|
+
# @param max_concurrent_streams [Symbol] threshold to cap the number of active streams with.
|
|
771
796
|
# @param priority [Integer]
|
|
772
797
|
# @param window [Integer]
|
|
773
798
|
# @param parent [Stream]
|
|
774
|
-
def activate_stream(id:, **args)
|
|
799
|
+
def activate_stream(id:, max_concurrent_streams: @local_settings[:settings_max_concurrent_streams], **args)
|
|
775
800
|
connection_error(msg: "Stream ID already exists") if @streams.key?(id)
|
|
776
801
|
|
|
777
|
-
|
|
802
|
+
# SETTINGS_MAX_CONCURRENT_STREAMS limits the number of concurrent streams that the sender
|
|
803
|
+
# of the setting permits the receiver to create (RFC 9113, section 5.1.2), so the bounding
|
|
804
|
+
# limit is the one advertised by the endpoint that did *not* open the stream.
|
|
805
|
+
raise StreamLimitExceeded if @active_stream_count >= max_concurrent_streams
|
|
778
806
|
|
|
779
807
|
stream = Stream.new(connection: self, id: id, **args)
|
|
780
808
|
|
|
781
809
|
stream.once(:close) do
|
|
782
810
|
@streams.delete(id)
|
|
783
811
|
|
|
812
|
+
# A graceful GOAWAY leaves the connection :closing until the
|
|
813
|
+
# tracked streams complete (Section 6.8); the last one to close
|
|
814
|
+
# moves it to its terminal state.
|
|
815
|
+
close! if @state == :closing && @streams.empty?
|
|
816
|
+
|
|
784
817
|
# Store a reference to the closed stream, such that we can respond
|
|
785
818
|
# to any in-flight frames while close is registered on both sides.
|
|
786
819
|
# References to such streams will be purged whenever another stream
|
|
@@ -811,6 +844,11 @@ module HTTP2
|
|
|
811
844
|
@streams[id] = stream
|
|
812
845
|
end
|
|
813
846
|
|
|
847
|
+
def close!
|
|
848
|
+
@state = :closed
|
|
849
|
+
@closed_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
850
|
+
end
|
|
851
|
+
|
|
814
852
|
def verify_stream_order(id)
|
|
815
853
|
return unless id.odd?
|
|
816
854
|
|
data/lib/http/2/error.rb
CHANGED
|
@@ -53,7 +53,7 @@ module HTTP2
|
|
|
53
53
|
# Raised if stream has been closed and new frames cannot be sent.
|
|
54
54
|
class StreamClosed < Error; end
|
|
55
55
|
|
|
56
|
-
# Raised if connection has been closed (or
|
|
56
|
+
# Raised if connection has been closed (or closing) and new stream
|
|
57
57
|
# cannot be opened.
|
|
58
58
|
class ConnectionClosed < Error; end
|
|
59
59
|
|
data/lib/http/2/version.rb
CHANGED
data/sig/connection.rbs
CHANGED
|
@@ -59,6 +59,10 @@ module HTTP2
|
|
|
59
59
|
|
|
60
60
|
def closed?: () -> bool
|
|
61
61
|
|
|
62
|
+
def closing?: () -> bool
|
|
63
|
+
|
|
64
|
+
def close!: () -> void
|
|
65
|
+
|
|
62
66
|
def new_stream: (**untyped) -> Stream
|
|
63
67
|
|
|
64
68
|
def ping: (String) -> void
|
|
@@ -95,7 +99,7 @@ module HTTP2
|
|
|
95
99
|
|
|
96
100
|
def encode_headers: (headers_frame | push_promise_frame) -> void
|
|
97
101
|
|
|
98
|
-
def activate_stream: (id: Integer, **untyped) -> Stream
|
|
102
|
+
def activate_stream: (id: Integer, ?max_concurrent_streams: Integer, **untyped) -> Stream
|
|
99
103
|
|
|
100
104
|
def verify_stream_order: (Integer id) -> void
|
|
101
105
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http-2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
79
|
version: '0'
|
|
80
80
|
requirements: []
|
|
81
|
-
rubygems_version:
|
|
81
|
+
rubygems_version: 4.0.16
|
|
82
82
|
specification_version: 4
|
|
83
83
|
summary: Pure-ruby HTTP 2.0 protocol implementation
|
|
84
84
|
test_files: []
|