protocol-http2 0.26.2 → 0.27.0
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
- checksums.yaml.gz.sig +0 -0
- data/context/index.yaml +2 -0
- data/lib/protocol/http2/connection.rb +67 -11
- data/lib/protocol/http2/goaway_frame.rb +1 -1
- data/lib/protocol/http2/version.rb +1 -1
- data/license.md +1 -0
- data/readme.md +12 -14
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +4 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e218c8562a926b98f24ad707341e1ee24d26ed6ce94b9f6550049000f415cd2b
|
|
4
|
+
data.tar.gz: 8ed236e94626982bd750d03a5c568c7b438e233b09256b671e5373db0881617c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31dfad736c2d3807ba103ccd70dea68d95375fe53f290582f805691c7b51cc43512494e2aae439c90082b0fbf518568c78e574bf9a3bb19dc4312127eb6378db
|
|
7
|
+
data.tar.gz: 2ee408ca054cd3ae3eb205489ea0b7095fe896af11bcb810e377a627d824cd2b6440d013ace9ebba79886ac71c067a541209007c9ee169dcd78803c2be531172
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/index.yaml
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
---
|
|
4
4
|
description: A low level implementation of the HTTP/2 protocol.
|
|
5
5
|
metadata:
|
|
6
|
+
bug_tracker_uri: https://github.com/socketry/protocol-http2/issues
|
|
7
|
+
changelog_uri: https://github.com/socketry/protocol-http2/blob/main/releases.md
|
|
6
8
|
documentation_uri: https://socketry.github.io/protocol-http2/
|
|
7
9
|
source_code_uri: https://github.com/socketry/protocol-http2.git
|
|
8
10
|
files:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2019-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2023, by Marco Concetto Rudilosso.
|
|
6
|
+
# Copyright, 2026, by Denis Talakevich.
|
|
6
7
|
|
|
7
8
|
require_relative "framer"
|
|
8
9
|
require_relative "flow_controlled"
|
|
@@ -44,6 +45,9 @@ module Protocol
|
|
|
44
45
|
|
|
45
46
|
@local_window = LocalWindow.new
|
|
46
47
|
@remote_window = Window.new
|
|
48
|
+
|
|
49
|
+
# The lowest Last-Stream-ID received in a GOAWAY frame, or nil if no GOAWAY frame has been received:
|
|
50
|
+
@goaway_stream_id = nil
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
# The connection stream ID (always 0 for connection-level operations).
|
|
@@ -94,16 +98,41 @@ module Protocol
|
|
|
94
98
|
# The highest stream_id that has been successfully accepted by this connection.
|
|
95
99
|
attr :remote_stream_id
|
|
96
100
|
|
|
101
|
+
# The lowest Last-Stream-ID received in a GOAWAY frame.
|
|
102
|
+
attr :goaway_stream_id
|
|
103
|
+
|
|
97
104
|
# Whether the connection is effectively or actually closed.
|
|
98
105
|
def closed?
|
|
99
106
|
@state == :closed || @framer.nil?
|
|
100
107
|
end
|
|
101
108
|
|
|
109
|
+
# Whether the remote peer has sent us a GOAWAY frame. We must not initiate any new streams on this connection, but existing streams may still be in progress.
|
|
110
|
+
# @returns [Boolean] True if a GOAWAY frame has been received.
|
|
111
|
+
def goaway_received?
|
|
112
|
+
!@goaway_stream_id.nil?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Transition the connection into the closed state if a graceful GOAWAY was received and there is nothing left to drain.
|
|
116
|
+
#
|
|
117
|
+
# As with {close!}, this is a state transition only: the owner of the connection is responsible for closing the underlying framer.
|
|
118
|
+
def close_if_drained!
|
|
119
|
+
if self.goaway_received? && @streams.empty?
|
|
120
|
+
self.close!
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
102
124
|
# Remove a stream from the active streams collection.
|
|
125
|
+
#
|
|
126
|
+
# If the remote peer has sent a graceful GOAWAY frame, the connection is only kept open in order to drain the streams it accepted, so when the last one completes there is nothing left to read.
|
|
127
|
+
#
|
|
103
128
|
# @parameter id [Integer] The stream ID to remove.
|
|
104
129
|
# @returns [Stream | Nil] The removed stream, or nil if not found.
|
|
105
130
|
def delete(id)
|
|
106
|
-
@streams.delete(id)
|
|
131
|
+
stream = @streams.delete(id)
|
|
132
|
+
|
|
133
|
+
self.close_if_drained!
|
|
134
|
+
|
|
135
|
+
return stream
|
|
107
136
|
end
|
|
108
137
|
|
|
109
138
|
# Close the underlying framer and all streams.
|
|
@@ -229,25 +258,39 @@ module Protocol
|
|
|
229
258
|
end
|
|
230
259
|
|
|
231
260
|
# Process a GOAWAY frame from the remote peer.
|
|
261
|
+
#
|
|
262
|
+
# A GOAWAY frame with a zero error code is a graceful shutdown: the remote peer will not accept any new streams, but it is still processing the streams at or below `last_stream_id` and will send their responses (RFC 9113 §6.8). We must keep reading until those streams complete, otherwise requests which the remote peer has already processed - and whose side effects have already happened - fail locally. The connection is closed once the last of those streams completes, or the remote peer closes it.
|
|
263
|
+
#
|
|
264
|
+
# A GOAWAY frame with a non-zero error code is a connection error: the connection transitions into the closed state and {GoawayError} is raised.
|
|
265
|
+
#
|
|
232
266
|
# @parameter frame [GoawayFrame] The GOAWAY frame to process.
|
|
233
267
|
# @raises [GoawayError] If the frame indicates a connection error.
|
|
234
268
|
def receive_goaway(frame)
|
|
235
|
-
# We capture the last stream that
|
|
236
|
-
|
|
269
|
+
# We capture the last locally-initiated stream that may have been processed by the peer.
|
|
270
|
+
goaway_stream_id, error_code, message = frame.unpack
|
|
237
271
|
|
|
238
|
-
|
|
272
|
+
# A peer can send an initial GOAWAY with a high stream ID, followed by another GOAWAY with a lower stream ID. The effective cutoff can only decrease (RFC 9113 §6.8).
|
|
273
|
+
if @goaway_stream_id.nil? || goaway_stream_id < @goaway_stream_id
|
|
274
|
+
@goaway_stream_id = goaway_stream_id
|
|
275
|
+
end
|
|
239
276
|
|
|
240
|
-
#
|
|
241
|
-
|
|
277
|
+
# Locally-initiated streams above the last stream ID were not processed by the remote peer and are safe to retry (RFC 9113 §6.8). They are removed from the connection before being closed, both so that what remains is exactly the set of streams we are waiting on, and so that closing them cannot mutate the collection while we are traversing it.
|
|
278
|
+
refused_streams = @streams.select{|id, stream| local_stream_id?(id) && id > @goaway_stream_id}
|
|
279
|
+
refused_streams.each_key{|id| @streams.delete(id)}
|
|
242
280
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
281
|
+
# The state of the connection is decided before any stream is closed, so that it cannot be left undecided by a `closed` hook which raises, and cannot be influenced by one which creates a stream.
|
|
282
|
+
if error_code != 0
|
|
283
|
+
self.close!
|
|
284
|
+
else
|
|
285
|
+
self.close_if_drained!
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
unless refused_streams.empty?
|
|
289
|
+
error = ::Protocol::HTTP::RefusedError.new("GOAWAY: request not processed.")
|
|
290
|
+
refused_streams.each_value{|stream| stream.close(error)}
|
|
247
291
|
end
|
|
248
292
|
|
|
249
293
|
if error_code != 0
|
|
250
|
-
# Shut down immediately.
|
|
251
294
|
raise GoawayError.new(message, error_code)
|
|
252
295
|
end
|
|
253
296
|
end
|
|
@@ -404,6 +447,14 @@ module Protocol
|
|
|
404
447
|
false
|
|
405
448
|
end
|
|
406
449
|
|
|
450
|
+
# Check if the given stream ID represents a locally-initiated stream.
|
|
451
|
+
# This method should be overridden by client/server implementations.
|
|
452
|
+
# @parameter id [Integer] The stream ID to check.
|
|
453
|
+
# @returns [Boolean] True if the stream ID is locally-initiated.
|
|
454
|
+
def local_stream_id?(id)
|
|
455
|
+
false
|
|
456
|
+
end
|
|
457
|
+
|
|
407
458
|
# Accept an incoming stream from the other side of the connnection.
|
|
408
459
|
# On the server side, we accept requests.
|
|
409
460
|
def accept_stream(stream_id, &block)
|
|
@@ -425,6 +476,11 @@ module Protocol
|
|
|
425
476
|
# On the client side, we create requests.
|
|
426
477
|
# @return [Stream] the created stream.
|
|
427
478
|
def create_stream(id = next_stream_id, &block)
|
|
479
|
+
if self.goaway_received? and local_stream_id?(id)
|
|
480
|
+
# Receivers of a GOAWAY frame MUST NOT open additional streams on the connection (RFC 9113 §6.8). A new connection has to be established for new streams.
|
|
481
|
+
raise ProtocolError, "Cannot create stream #{id} after GOAWAY!"
|
|
482
|
+
end
|
|
483
|
+
|
|
428
484
|
if @streams.key?(id)
|
|
429
485
|
raise ProtocolError, "Cannot create stream with id #{id}, already exists!"
|
|
430
486
|
end
|
data/license.md
CHANGED
|
@@ -5,6 +5,7 @@ Copyright, 2019, by Yuta Iwama.
|
|
|
5
5
|
Copyright, 2020, by Olle Jonsson.
|
|
6
6
|
Copyright, 2023, by Marco Concetto Rudilosso.
|
|
7
7
|
Copyright, 2024, by Adam Petro.
|
|
8
|
+
Copyright, 2026, by Denis Talakevich.
|
|
8
9
|
|
|
9
10
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
11
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
|
@@ -14,6 +14,11 @@ Please see the [project documentation](https://socketry.github.io/protocol-http2
|
|
|
14
14
|
|
|
15
15
|
Please see the [project releases](https://socketry.github.io/protocol-http2/releases/index) for all releases.
|
|
16
16
|
|
|
17
|
+
### v0.27.0
|
|
18
|
+
|
|
19
|
+
- On a graceful `GOAWAY` (error code `0`), keep the connection open until the streams the remote peer accepted have completed, instead of closing it immediately and failing those requests with `EOFError`.
|
|
20
|
+
- `Connection#create_stream` refuses to open a locally-initiated stream once a `GOAWAY` has been received, as required by RFC 9113 §6.8.
|
|
21
|
+
|
|
17
22
|
### v0.26.2
|
|
18
23
|
|
|
19
24
|
- Ignore the reserved high bit when decoding GOAWAY last stream IDs.
|
|
@@ -52,13 +57,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http2/rele
|
|
|
52
57
|
|
|
53
58
|
- **Breaking**: Removed support for priority frame and stream dependencies. The `Protocol::HTTP2::Stream` class no longer tracks dependencies, and `Stream#send_headers` no longer takes `priority` as the first argument. This change simplifies the internal implementation significantly as HTTP/2 priority frames have been deprecated in the protocol specification.
|
|
54
59
|
|
|
55
|
-
### v0.20.0
|
|
56
|
-
|
|
57
|
-
- Improved performance of dependency management by avoiding linear search operations.
|
|
58
|
-
- Removed `traces` as a required dependency - it's now optional and only used when explicitly needed.
|
|
59
|
-
- Added better documentation for `maximum_concurrent_streams` setting.
|
|
60
|
-
- Restored 100% test coverage and exposed trace provider for optional tracing support.
|
|
61
|
-
|
|
62
60
|
## See Also
|
|
63
61
|
|
|
64
62
|
- [Async::HTTP](https://github.com/socketry/async-http) - A high-level HTTP client and server implementation.
|
|
@@ -67,26 +65,26 @@ Please see the [project releases](https://socketry.github.io/protocol-http2/rele
|
|
|
67
65
|
|
|
68
66
|
We welcome contributions to this project.
|
|
69
67
|
|
|
70
|
-
1. Fork
|
|
68
|
+
1. Fork the repository.
|
|
71
69
|
2. Create your feature branch (`git checkout -b my-new-feature`).
|
|
72
|
-
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
70
|
+
3. Commit your changes (`git commit -am 'Add some feature.'`).
|
|
73
71
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
74
|
-
5. Create new
|
|
72
|
+
5. Create a new pull request.
|
|
75
73
|
|
|
76
74
|
### Running Tests
|
|
77
75
|
|
|
78
76
|
To run the test suite:
|
|
79
77
|
|
|
80
|
-
```
|
|
81
|
-
bundle exec sus
|
|
78
|
+
``` bash
|
|
79
|
+
$ bundle exec sus
|
|
82
80
|
```
|
|
83
81
|
|
|
84
82
|
### Making Releases
|
|
85
83
|
|
|
86
84
|
To make a new release:
|
|
87
85
|
|
|
88
|
-
```
|
|
89
|
-
bundle exec bake gem:release:patch # or minor or major
|
|
86
|
+
``` bash
|
|
87
|
+
$ bundle exec bake gem:release:patch # or minor or major
|
|
90
88
|
```
|
|
91
89
|
|
|
92
90
|
### Developer Certificate of Origin
|
data/releases.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.27.0
|
|
4
|
+
|
|
5
|
+
- On a graceful `GOAWAY` (error code `0`), keep the connection open until the streams the remote peer accepted have completed, instead of closing it immediately and failing those requests with `EOFError`.
|
|
6
|
+
- `Connection#create_stream` refuses to open a locally-initiated stream once a `GOAWAY` has been received, as required by RFC 9113 §6.8.
|
|
7
|
+
|
|
3
8
|
## v0.26.2
|
|
4
9
|
|
|
5
10
|
- Ignore the reserved high bit when decoding GOAWAY last stream IDs.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-http2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
8
8
|
- Yuta Iwama
|
|
9
9
|
- Adam Petro
|
|
10
|
+
- Denis Talakevich
|
|
10
11
|
- Marco Concetto Rudilosso
|
|
11
12
|
- Olle Jonsson
|
|
12
13
|
bindir: bin
|
|
@@ -107,6 +108,8 @@ homepage: https://github.com/socketry/protocol-http2
|
|
|
107
108
|
licenses:
|
|
108
109
|
- MIT
|
|
109
110
|
metadata:
|
|
111
|
+
bug_tracker_uri: https://github.com/socketry/protocol-http2/issues
|
|
112
|
+
changelog_uri: https://github.com/socketry/protocol-http2/blob/main/releases.md
|
|
110
113
|
documentation_uri: https://socketry.github.io/protocol-http2/
|
|
111
114
|
source_code_uri: https://github.com/socketry/protocol-http2.git
|
|
112
115
|
rdoc_options: []
|
metadata.gz.sig
CHANGED
|
Binary file
|