protocol-http2 0.22.1 → 0.23.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/getting-started.md +82 -0
- data/context/index.yaml +12 -0
- data/lib/protocol/http2/client.rb +28 -1
- data/lib/protocol/http2/connection.rb +87 -1
- data/lib/protocol/http2/continuation_frame.rb +42 -4
- data/lib/protocol/http2/data_frame.rb +11 -1
- data/lib/protocol/http2/error.rb +17 -2
- data/lib/protocol/http2/flow_controlled.rb +13 -1
- data/lib/protocol/http2/frame.rb +47 -2
- data/lib/protocol/http2/framer.rb +16 -1
- data/lib/protocol/http2/goaway_frame.rb +11 -1
- data/lib/protocol/http2/headers_frame.rb +15 -1
- data/lib/protocol/http2/padded.rb +12 -1
- data/lib/protocol/http2/ping_frame.rb +17 -1
- data/lib/protocol/http2/priority_update_frame.rb +8 -0
- data/lib/protocol/http2/push_promise_frame.rb +10 -1
- data/lib/protocol/http2/reset_stream_frame.rb +10 -1
- data/lib/protocol/http2/server.rb +27 -1
- data/lib/protocol/http2/settings_frame.rb +58 -1
- data/lib/protocol/http2/stream.rb +48 -1
- data/lib/protocol/http2/version.rb +4 -2
- data/lib/protocol/http2/window.rb +25 -1
- data/lib/protocol/http2/window_update_frame.rb +10 -1
- data/readme.md +12 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +6 -4
- metadata.gz.sig +0 -0
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "ping_frame"
|
7
7
|
|
8
8
|
module Protocol
|
9
9
|
module HTTP2
|
10
|
+
# HTTP/2 connection settings container and management.
|
10
11
|
class Settings
|
11
12
|
HEADER_TABLE_SIZE = 0x1
|
12
13
|
ENABLE_PUSH = 0x2
|
@@ -30,6 +31,7 @@ module Protocol
|
|
30
31
|
:no_rfc7540_priorities=,
|
31
32
|
]
|
32
33
|
|
34
|
+
# Initialize settings with default values from HTTP/2 specification.
|
33
35
|
def initialize
|
34
36
|
# These limits are taken from the RFC:
|
35
37
|
# https://tools.ietf.org/html/rfc7540#section-6.5.2
|
@@ -49,6 +51,9 @@ module Protocol
|
|
49
51
|
# This setting can be used to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a value of 0.
|
50
52
|
attr :enable_push
|
51
53
|
|
54
|
+
# Set the server push enable flag.
|
55
|
+
# @parameter value [Integer] Must be 0 (disabled) or 1 (enabled).
|
56
|
+
# @raises [ProtocolError] If the value is invalid.
|
52
57
|
def enable_push= value
|
53
58
|
if value == 0 or value == 1
|
54
59
|
@enable_push = value
|
@@ -57,6 +62,8 @@ module Protocol
|
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
65
|
+
# Check if server push is enabled.
|
66
|
+
# @returns [Boolean] True if server push is enabled.
|
60
67
|
def enable_push?
|
61
68
|
@enable_push == 1
|
62
69
|
end
|
@@ -67,6 +74,9 @@ module Protocol
|
|
67
74
|
# Indicates the sender's initial window size (in octets) for stream-level flow control.
|
68
75
|
attr :initial_window_size
|
69
76
|
|
77
|
+
# Set the initial window size for stream-level flow control.
|
78
|
+
# @parameter value [Integer] The window size in octets.
|
79
|
+
# @raises [ProtocolError] If the value exceeds the maximum allowed.
|
70
80
|
def initial_window_size= value
|
71
81
|
if value <= MAXIMUM_ALLOWED_WINDOW_SIZE
|
72
82
|
@initial_window_size = value
|
@@ -78,6 +88,9 @@ module Protocol
|
|
78
88
|
# Indicates the size of the largest frame payload that the sender is willing to receive, in octets.
|
79
89
|
attr :maximum_frame_size
|
80
90
|
|
91
|
+
# Set the maximum frame size the sender is willing to receive.
|
92
|
+
# @parameter value [Integer] The maximum frame size in octets.
|
93
|
+
# @raises [ProtocolError] If the value is outside the allowed range.
|
81
94
|
def maximum_frame_size= value
|
82
95
|
if value > MAXIMUM_ALLOWED_FRAME_SIZE
|
83
96
|
raise ProtocolError, "Invalid value for maximum_frame_size: #{value} > #{MAXIMUM_ALLOWED_FRAME_SIZE}"
|
@@ -93,6 +106,9 @@ module Protocol
|
|
93
106
|
|
94
107
|
attr :enable_connect_protocol
|
95
108
|
|
109
|
+
# Set the CONNECT protocol enable flag.
|
110
|
+
# @parameter value [Integer] Must be 0 (disabled) or 1 (enabled).
|
111
|
+
# @raises [ProtocolError] If the value is invalid.
|
96
112
|
def enable_connect_protocol= value
|
97
113
|
if value == 0 or value == 1
|
98
114
|
@enable_connect_protocol = value
|
@@ -101,12 +117,17 @@ module Protocol
|
|
101
117
|
end
|
102
118
|
end
|
103
119
|
|
120
|
+
# Check if CONNECT protocol is enabled.
|
121
|
+
# @returns [Boolean] True if CONNECT protocol is enabled.
|
104
122
|
def enable_connect_protocol?
|
105
123
|
@enable_connect_protocol == 1
|
106
124
|
end
|
107
125
|
|
108
126
|
attr :no_rfc7540_priorities
|
109
127
|
|
128
|
+
# Set the RFC 7540 priorities disable flag.
|
129
|
+
# @parameter value [Integer] Must be 0 (enabled) or 1 (disabled).
|
130
|
+
# @raises [ProtocolError] If the value is invalid.
|
110
131
|
def no_rfc7540_priorities= value
|
111
132
|
if value == 0 or value == 1
|
112
133
|
@no_rfc7540_priorities = value
|
@@ -115,10 +136,14 @@ module Protocol
|
|
115
136
|
end
|
116
137
|
end
|
117
138
|
|
139
|
+
# Check if RFC 7540 priorities are disabled.
|
140
|
+
# @returns [Boolean] True if RFC 7540 priorities are disabled.
|
118
141
|
def no_rfc7540_priorities?
|
119
142
|
@no_rfc7540_priorities == 1
|
120
143
|
end
|
121
144
|
|
145
|
+
# Update settings with a hash of changes.
|
146
|
+
# @parameter changes [Hash] Hash of setting keys and values to update.
|
122
147
|
def update(changes)
|
123
148
|
changes.each do |key, value|
|
124
149
|
if name = ASSIGN[key]
|
@@ -128,7 +153,10 @@ module Protocol
|
|
128
153
|
end
|
129
154
|
end
|
130
155
|
|
156
|
+
# Manages pending settings changes that haven't been acknowledged yet.
|
131
157
|
class PendingSettings
|
158
|
+
# Initialize with current settings.
|
159
|
+
# @parameter current [Settings] The current settings object.
|
132
160
|
def initialize(current = Settings.new)
|
133
161
|
@current = current
|
134
162
|
@pending = current.dup
|
@@ -139,11 +167,14 @@ module Protocol
|
|
139
167
|
attr :current
|
140
168
|
attr :pending
|
141
169
|
|
170
|
+
# Append changes to the pending queue.
|
171
|
+
# @parameter changes [Hash] Hash of setting changes to queue.
|
142
172
|
def append(changes)
|
143
173
|
@queue << changes
|
144
174
|
@pending.update(changes)
|
145
175
|
end
|
146
176
|
|
177
|
+
# Acknowledge the next set of pending changes.
|
147
178
|
def acknowledge
|
148
179
|
if changes = @queue.shift
|
149
180
|
@current.update(changes)
|
@@ -154,30 +185,44 @@ module Protocol
|
|
154
185
|
end
|
155
186
|
end
|
156
187
|
|
188
|
+
# Get the current header table size setting.
|
189
|
+
# @returns [Integer] The header table size in octets.
|
157
190
|
def header_table_size
|
158
191
|
@current.header_table_size
|
159
192
|
end
|
160
193
|
|
194
|
+
# Get the current enable push setting.
|
195
|
+
# @returns [Integer] 1 if push is enabled, 0 if disabled.
|
161
196
|
def enable_push
|
162
197
|
@current.enable_push
|
163
198
|
end
|
164
199
|
|
200
|
+
# Get the current maximum concurrent streams setting.
|
201
|
+
# @returns [Integer] The maximum number of concurrent streams.
|
165
202
|
def maximum_concurrent_streams
|
166
203
|
@current.maximum_concurrent_streams
|
167
204
|
end
|
168
205
|
|
206
|
+
# Get the current initial window size setting.
|
207
|
+
# @returns [Integer] The initial window size in octets.
|
169
208
|
def initial_window_size
|
170
209
|
@current.initial_window_size
|
171
210
|
end
|
172
211
|
|
212
|
+
# Get the current maximum frame size setting.
|
213
|
+
# @returns [Integer] The maximum frame size in octets.
|
173
214
|
def maximum_frame_size
|
174
215
|
@current.maximum_frame_size
|
175
216
|
end
|
176
217
|
|
218
|
+
# Get the current maximum header list size setting.
|
219
|
+
# @returns [Integer] The maximum header list size in octets.
|
177
220
|
def maximum_header_list_size
|
178
221
|
@current.maximum_header_list_size
|
179
222
|
end
|
180
223
|
|
224
|
+
# Get the current CONNECT protocol enable setting.
|
225
|
+
# @returns [Integer] 1 if CONNECT protocol is enabled, 0 if disabled.
|
181
226
|
def enable_connect_protocol
|
182
227
|
@current.enable_connect_protocol
|
183
228
|
end
|
@@ -197,10 +242,14 @@ module Protocol
|
|
197
242
|
|
198
243
|
include Acknowledgement
|
199
244
|
|
245
|
+
# Check if this frame applies to the connection level.
|
246
|
+
# @returns [Boolean] Always returns true for SETTINGS frames.
|
200
247
|
def connection?
|
201
248
|
true
|
202
249
|
end
|
203
250
|
|
251
|
+
# Unpack settings parameters from the frame payload.
|
252
|
+
# @returns [Array] Array of [key, value] pairs representing settings.
|
204
253
|
def unpack
|
205
254
|
if buffer = super
|
206
255
|
# TODO String#each_slice, or #each_unpack would be nice.
|
@@ -210,14 +259,22 @@ module Protocol
|
|
210
259
|
end
|
211
260
|
end
|
212
261
|
|
262
|
+
# Pack settings parameters into the frame payload.
|
263
|
+
# @parameter settings [Array] Array of [key, value] pairs to pack.
|
213
264
|
def pack(settings = [])
|
214
265
|
super(settings.map{|s| s.pack(FORMAT)}.join)
|
215
266
|
end
|
216
267
|
|
268
|
+
# Apply this SETTINGS frame to a connection for processing.
|
269
|
+
# @parameter connection [Connection] The connection to apply the frame to.
|
217
270
|
def apply(connection)
|
218
271
|
connection.receive_settings(self)
|
219
272
|
end
|
220
273
|
|
274
|
+
# Read and validate the SETTINGS frame payload.
|
275
|
+
# @parameter stream [IO] The stream to read from.
|
276
|
+
# @raises [ProtocolError] If the frame is invalid.
|
277
|
+
# @raises [FrameSizeError] If the frame length is invalid.
|
221
278
|
def read_payload(stream)
|
222
279
|
super
|
223
280
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "connection"
|
7
7
|
|
@@ -60,6 +60,10 @@ module Protocol
|
|
60
60
|
class Stream
|
61
61
|
include FlowControlled
|
62
62
|
|
63
|
+
# Create a new stream and add it to the connection.
|
64
|
+
# @parameter connection [Connection] The connection this stream belongs to.
|
65
|
+
# @parameter id [Integer] The stream identifier.
|
66
|
+
# @returns [Stream] The newly created stream.
|
63
67
|
def self.create(connection, id)
|
64
68
|
stream = self.new(connection, id)
|
65
69
|
|
@@ -68,6 +72,10 @@ module Protocol
|
|
68
72
|
return stream
|
69
73
|
end
|
70
74
|
|
75
|
+
# Initialize a new stream.
|
76
|
+
# @parameter connection [Connection] The connection this stream belongs to.
|
77
|
+
# @parameter id [Integer] The stream identifier.
|
78
|
+
# @parameter state [Symbol] The initial stream state.
|
71
79
|
def initialize(connection, id, state = :idle)
|
72
80
|
@connection = connection
|
73
81
|
@id = id
|
@@ -95,18 +103,26 @@ module Protocol
|
|
95
103
|
# @attribute [Protocol::HTTP::Header::Priority | Nil] the priority of the stream.
|
96
104
|
attr_accessor :priority
|
97
105
|
|
106
|
+
# Get the maximum frame size for this stream.
|
107
|
+
# @returns [Integer] The maximum frame size from connection settings.
|
98
108
|
def maximum_frame_size
|
99
109
|
@connection.available_frame_size
|
100
110
|
end
|
101
111
|
|
112
|
+
# Write a frame to the connection for this stream.
|
113
|
+
# @parameter frame [Frame] The frame to write.
|
102
114
|
def write_frame(frame)
|
103
115
|
@connection.write_frame(frame)
|
104
116
|
end
|
105
117
|
|
118
|
+
# Check if the stream is active (not idle or closed).
|
119
|
+
# @returns [Boolean] True if the stream is active.
|
106
120
|
def active?
|
107
121
|
@state != :closed && @state != :idle
|
108
122
|
end
|
109
123
|
|
124
|
+
# Check if the stream is closed.
|
125
|
+
# @returns [Boolean] True if the stream is in closed state.
|
110
126
|
def closed?
|
111
127
|
@state == :closed
|
112
128
|
end
|
@@ -170,6 +186,8 @@ module Protocol
|
|
170
186
|
end
|
171
187
|
end
|
172
188
|
|
189
|
+
# Consume from the remote window for both stream and connection.
|
190
|
+
# @parameter frame [Frame] The frame that consumes window space.
|
173
191
|
def consume_remote_window(frame)
|
174
192
|
super
|
175
193
|
|
@@ -187,6 +205,9 @@ module Protocol
|
|
187
205
|
return frame
|
188
206
|
end
|
189
207
|
|
208
|
+
# Send data over this stream.
|
209
|
+
# @parameter arguments [Array] Arguments passed to write_data.
|
210
|
+
# @parameter options [Hash] Options passed to write_data.
|
190
211
|
def send_data(*arguments, **options)
|
191
212
|
if @state == :open
|
192
213
|
frame = write_data(*arguments, **options)
|
@@ -205,6 +226,9 @@ module Protocol
|
|
205
226
|
end
|
206
227
|
end
|
207
228
|
|
229
|
+
# Open the stream by transitioning from idle to open state.
|
230
|
+
# @returns [Stream] Returns self for chaining.
|
231
|
+
# @raises [ProtocolError] If the stream cannot be opened from current state.
|
208
232
|
def open!
|
209
233
|
if @state == :idle
|
210
234
|
@state = :open
|
@@ -234,6 +258,8 @@ module Protocol
|
|
234
258
|
return self
|
235
259
|
end
|
236
260
|
|
261
|
+
# Send a RST_STREAM frame to reset this stream.
|
262
|
+
# @parameter error_code [Integer] The error code to send.
|
237
263
|
def send_reset_stream(error_code = 0)
|
238
264
|
if @state != :idle and @state != :closed
|
239
265
|
frame = ResetStreamFrame.new(@id)
|
@@ -247,6 +273,9 @@ module Protocol
|
|
247
273
|
end
|
248
274
|
end
|
249
275
|
|
276
|
+
# Process headers frame and decode the header block.
|
277
|
+
# @parameter frame [HeadersFrame] The headers frame to process.
|
278
|
+
# @returns [Array] The decoded headers.
|
250
279
|
def process_headers(frame)
|
251
280
|
# Receiving request headers:
|
252
281
|
data = frame.unpack
|
@@ -258,6 +287,8 @@ module Protocol
|
|
258
287
|
# Console.warn(self) {"Received headers in state: #{@state}!"}
|
259
288
|
end
|
260
289
|
|
290
|
+
# Receive and process a headers frame on this stream.
|
291
|
+
# @parameter frame [HeadersFrame] The headers frame to receive.
|
261
292
|
def receive_headers(frame)
|
262
293
|
if @state == :idle
|
263
294
|
if frame.end_stream?
|
@@ -295,6 +326,8 @@ module Protocol
|
|
295
326
|
frame.unpack
|
296
327
|
end
|
297
328
|
|
329
|
+
# Ignore data frame when in an invalid state.
|
330
|
+
# @parameter frame [DataFrame] The data frame to ignore.
|
298
331
|
def ignore_data(frame)
|
299
332
|
# Console.warn(self) {"Received headers in state: #{@state}!"}
|
300
333
|
end
|
@@ -325,6 +358,10 @@ module Protocol
|
|
325
358
|
end
|
326
359
|
end
|
327
360
|
|
361
|
+
# Receive and process a RST_STREAM frame on this stream.
|
362
|
+
# @parameter frame [ResetStreamFrame] The reset stream frame to receive.
|
363
|
+
# @returns [Integer] The error code from the reset frame.
|
364
|
+
# @raises [ProtocolError] If reset is received on an idle stream.
|
328
365
|
def receive_reset_stream(frame)
|
329
366
|
if @state == :idle
|
330
367
|
# If a RST_STREAM frame identifying an idle stream is received, the recipient MUST treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
@@ -355,6 +392,9 @@ module Protocol
|
|
355
392
|
return frame
|
356
393
|
end
|
357
394
|
|
395
|
+
# Transition stream to reserved local state.
|
396
|
+
# @returns [Stream] Returns self for chaining.
|
397
|
+
# @raises [ProtocolError] If the stream cannot be reserved from current state.
|
358
398
|
def reserved_local!
|
359
399
|
if @state == :idle
|
360
400
|
@state = :reserved_local
|
@@ -365,6 +405,9 @@ module Protocol
|
|
365
405
|
return self
|
366
406
|
end
|
367
407
|
|
408
|
+
# Transition stream to reserved remote state.
|
409
|
+
# @returns [Stream] Returns self for chaining.
|
410
|
+
# @raises [ProtocolError] If the stream cannot be reserved from current state.
|
368
411
|
def reserved_remote!
|
369
412
|
if @state == :idle
|
370
413
|
@state = :reserved_remote
|
@@ -402,6 +445,8 @@ module Protocol
|
|
402
445
|
@connection.accept_push_promise_stream(stream_id)
|
403
446
|
end
|
404
447
|
|
448
|
+
# Receive and process a PUSH_PROMISE frame on this stream.
|
449
|
+
# @parameter frame [PushPromiseFrame] The push promise frame to receive.
|
405
450
|
def receive_push_promise(frame)
|
406
451
|
promised_stream_id, data = frame.unpack
|
407
452
|
headers = @connection.decode_headers(data)
|
@@ -412,6 +457,8 @@ module Protocol
|
|
412
457
|
return stream, headers
|
413
458
|
end
|
414
459
|
|
460
|
+
# Get a string representation of the stream.
|
461
|
+
# @returns [String] Human-readable stream information.
|
415
462
|
def inspect
|
416
463
|
"\#<#{self.class} id=#{@id} state=#{@state}>"
|
417
464
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
|
+
# @namespace
|
6
7
|
module Protocol
|
8
|
+
# @namespace
|
7
9
|
module HTTP2
|
8
|
-
VERSION = "0.
|
10
|
+
VERSION = "0.23.0"
|
9
11
|
end
|
10
12
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Protocol
|
7
7
|
module HTTP2
|
8
|
+
# Flow control window for managing HTTP/2 data flow.
|
8
9
|
class Window
|
9
10
|
# When an HTTP/2 connection is first established, new streams are created with an initial flow-control window size of 65,535 octets. The connection flow-control window is also 65,535 octets.
|
10
11
|
DEFAULT_CAPACITY = 0xFFFF
|
11
12
|
|
13
|
+
# Initialize a new flow control window.
|
12
14
|
# @parameter capacity [Integer] The initial window size, typically from the settings.
|
13
15
|
def initialize(capacity = DEFAULT_CAPACITY)
|
14
16
|
# This is the main field required:
|
@@ -40,6 +42,8 @@ module Protocol
|
|
40
42
|
@capacity = value
|
41
43
|
end
|
42
44
|
|
45
|
+
# Consume a specific amount from the available window.
|
46
|
+
# @parameter amount [Integer] The amount to consume from the window.
|
43
47
|
def consume(amount)
|
44
48
|
@available -= amount
|
45
49
|
@used += amount
|
@@ -47,10 +51,15 @@ module Protocol
|
|
47
51
|
|
48
52
|
attr :available
|
49
53
|
|
54
|
+
# Check if there is available window capacity.
|
55
|
+
# @returns [Boolean] True if there is available capacity.
|
50
56
|
def available?
|
51
57
|
@available > 0
|
52
58
|
end
|
53
59
|
|
60
|
+
# Expand the window by a specific amount.
|
61
|
+
# @parameter amount [Integer] The amount to expand the window by.
|
62
|
+
# @raises [FlowControlError] If expansion would cause overflow.
|
54
63
|
def expand(amount)
|
55
64
|
available = @available + amount
|
56
65
|
|
@@ -63,14 +72,20 @@ module Protocol
|
|
63
72
|
@used -= amount
|
64
73
|
end
|
65
74
|
|
75
|
+
# Get the amount of window that should be reclaimed.
|
76
|
+
# @returns [Integer] The amount of used window space.
|
66
77
|
def wanted
|
67
78
|
@used
|
68
79
|
end
|
69
80
|
|
81
|
+
# Check if the window is limited and needs updating.
|
82
|
+
# @returns [Boolean] True if available capacity is less than half of total capacity.
|
70
83
|
def limited?
|
71
84
|
@available < (@capacity / 2)
|
72
85
|
end
|
73
86
|
|
87
|
+
# Get a string representation of the window.
|
88
|
+
# @returns [String] Human-readable window information.
|
74
89
|
def inspect
|
75
90
|
"\#<#{self.class} available=#{@available} used=#{@used} capacity=#{@capacity}#{limited? ? " limited" : nil}>"
|
76
91
|
end
|
@@ -80,6 +95,9 @@ module Protocol
|
|
80
95
|
|
81
96
|
# This is a window which efficiently maintains a desired capacity.
|
82
97
|
class LocalWindow < Window
|
98
|
+
# Initialize a local window with optional desired capacity.
|
99
|
+
# @parameter capacity [Integer] The initial window capacity.
|
100
|
+
# @parameter desired [Integer] The desired window capacity.
|
83
101
|
def initialize(capacity = DEFAULT_CAPACITY, desired: nil)
|
84
102
|
super(capacity)
|
85
103
|
|
@@ -91,6 +109,8 @@ module Protocol
|
|
91
109
|
# The desired capacity of the window.
|
92
110
|
attr_accessor :desired
|
93
111
|
|
112
|
+
# Get the amount of window that should be reclaimed, considering desired capacity.
|
113
|
+
# @returns [Integer] The amount needed to reach desired capacity or used space.
|
94
114
|
def wanted
|
95
115
|
if @desired
|
96
116
|
# We must send an update which allows at least @desired bytes to be sent.
|
@@ -100,6 +120,8 @@ module Protocol
|
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
123
|
+
# Check if the window is limited, considering desired capacity.
|
124
|
+
# @returns [Boolean] True if window needs updating based on desired capacity.
|
103
125
|
def limited?
|
104
126
|
if @desired
|
105
127
|
# Do not send window updates until we are less than half the desired capacity:
|
@@ -109,6 +131,8 @@ module Protocol
|
|
109
131
|
end
|
110
132
|
end
|
111
133
|
|
134
|
+
# Get a string representation of the local window.
|
135
|
+
# @returns [String] Human-readable local window information.
|
112
136
|
def inspect
|
113
137
|
"\#<#{self.class} available=#{@available} used=#{@used} capacity=#{@capacity} desired=#{@desired} #{limited? ? "limited" : nil}>"
|
114
138
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
require_relative "frame"
|
7
7
|
require_relative "window"
|
@@ -18,14 +18,21 @@ module Protocol
|
|
18
18
|
TYPE = 0x8
|
19
19
|
FORMAT = "N"
|
20
20
|
|
21
|
+
# Pack a window size increment into the frame.
|
22
|
+
# @parameter window_size_increment [Integer] The window size increment value.
|
21
23
|
def pack(window_size_increment)
|
22
24
|
super [window_size_increment].pack(FORMAT)
|
23
25
|
end
|
24
26
|
|
27
|
+
# Unpack the window size increment from the frame payload.
|
28
|
+
# @returns [Integer] The window size increment value.
|
25
29
|
def unpack
|
26
30
|
super.unpack1(FORMAT)
|
27
31
|
end
|
28
32
|
|
33
|
+
# Read and validate the WINDOW_UPDATE frame payload.
|
34
|
+
# @parameter stream [IO] The stream to read from.
|
35
|
+
# @raises [FrameSizeError] If the frame length is invalid.
|
29
36
|
def read_payload(stream)
|
30
37
|
super
|
31
38
|
|
@@ -34,6 +41,8 @@ module Protocol
|
|
34
41
|
end
|
35
42
|
end
|
36
43
|
|
44
|
+
# Apply this WINDOW_UPDATE frame to a connection for processing.
|
45
|
+
# @parameter connection [Connection] The connection to apply the frame to.
|
37
46
|
def apply(connection)
|
38
47
|
connection.receive_window_update(self)
|
39
48
|
end
|
data/readme.md
CHANGED
@@ -10,6 +10,18 @@ Please see the [project documentation](https://socketry.github.io/protocol-http2
|
|
10
10
|
|
11
11
|
- [Getting Started](https://socketry.github.io/protocol-http2/guides/getting-started/index) - This guide explains how to use the `protocol-http2` gem to implement a basic HTTP/2 client.
|
12
12
|
|
13
|
+
## Releases
|
14
|
+
|
15
|
+
Please see the [project releases](https://socketry.github.io/protocol-http2/releases/index) for all releases.
|
16
|
+
|
17
|
+
### v0.23.0
|
18
|
+
|
19
|
+
- Introduce a limit to the number of CONTINUATION frames that can be read to prevent resource exhaustion. The default limit is 8 continuation frames, which means a total of 9 frames (1 initial + 8 continuation). This limit can be adjusted by passing a different value to the `limit` parameter in the `Continued.read` method. Setting the limit to 0 will only read the initial frame without any continuation frames. In order to change the default, you can redefine the `LIMIT` constant in the `Protocol::HTTP2::Continued` module, OR you can pass a different frame class to the framer.
|
20
|
+
|
21
|
+
### v0.22.0
|
22
|
+
|
23
|
+
- [Added Priority Update Frame and Stream Priority](https://socketry.github.io/protocol-http2/releases/index#added-priority-update-frame-and-stream-priority)
|
24
|
+
|
13
25
|
## See Also
|
14
26
|
|
15
27
|
- [Async::HTTP](https://github.com/socketry/async-http) - A high-level HTTP client and server implementation.
|
data/releases.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v0.23.0
|
4
|
+
|
5
|
+
- Introduce a limit to the number of CONTINUATION frames that can be read to prevent resource exhaustion. The default limit is 8 continuation frames, which means a total of 9 frames (1 initial + 8 continuation). This limit can be adjusted by passing a different value to the `limit` parameter in the `Continued.read` method. Setting the limit to 0 will only read the initial frame without any continuation frames. In order to change the default, you can redefine the `LIMIT` constant in the `Protocol::HTTP2::Continued` module, OR you can pass a different frame class to the framer.
|
6
|
+
|
3
7
|
## v0.22.0
|
4
8
|
|
5
9
|
### Added Priority Update Frame and Stream Priority
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-http2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.23.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -40,7 +40,7 @@ cert_chain:
|
|
40
40
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
41
41
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
42
42
|
-----END CERTIFICATE-----
|
43
|
-
date:
|
43
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
44
44
|
dependencies:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: protocol-hpack
|
@@ -74,6 +74,8 @@ executables: []
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- context/getting-started.md
|
78
|
+
- context/index.yaml
|
77
79
|
- lib/protocol/http2.rb
|
78
80
|
- lib/protocol/http2/client.rb
|
79
81
|
- lib/protocol/http2/connection.rb
|
@@ -114,14 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
116
|
requirements:
|
115
117
|
- - ">="
|
116
118
|
- !ruby/object:Gem::Version
|
117
|
-
version: '3.
|
119
|
+
version: '3.2'
|
118
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
121
|
requirements:
|
120
122
|
- - ">="
|
121
123
|
- !ruby/object:Gem::Version
|
122
124
|
version: '0'
|
123
125
|
requirements: []
|
124
|
-
rubygems_version: 3.6.
|
126
|
+
rubygems_version: 3.6.9
|
125
127
|
specification_version: 4
|
126
128
|
summary: A low level implementation of the HTTP/2 protocol.
|
127
129
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|