http-2 1.2.1 → 1.2.3
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/LICENSE +21 -0
- data/README.md +9 -0
- data/lib/http/2/base64.rb +8 -8
- data/lib/http/2/client.rb +1 -1
- data/lib/http/2/connection.rb +68 -48
- data/lib/http/2/error.rb +1 -1
- data/lib/http/2/framer.rb +3 -3
- data/lib/http/2/header/compressor.rb +9 -37
- data/lib/http/2/header/decompressor.rb +9 -28
- data/lib/http/2/header/encoding_context.rb +21 -59
- data/lib/http/2/header/huffman.rb +3 -10
- data/lib/http/2/server.rb +2 -2
- data/lib/http/2/settings.rb +20 -4
- data/lib/http/2/version.rb +1 -1
- data/lib/http/2.rb +1 -0
- data/sig/2.rbs +6 -13
- data/sig/connection.rbs +8 -14
- data/sig/header/compressor.rbs +1 -1
- data/sig/header/decompressor.rbs +1 -1
- data/sig/header/encoding_context.rbs +2 -4
- data/sig/server.rbs +2 -0
- data/sig/settings.rbs +71 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aa40c026d7345ba9879c03ce22a9ca6c8ec981482381fd0f89c2da5f4d814214
|
|
4
|
+
data.tar.gz: 9cf43f76f7aed43428b636dca605c05bc8c882bfd2f56cabbadaeff71c42648b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56813420638e4b6b8e310d64fef3a284ae0c597ce77e8fc83ae316f6cf3070a216fe943c5fd8b0cb9741e8103cf64ec17317e6af45163d355489f55d8f17379b
|
|
7
|
+
data.tar.gz: c6af72766c395fb4ef21ba2c6de0ef86dc1432421b4c8b0cdc486e4f006891d8e8f104225f98b7950d2ca31d111f97a2a1f42ba6633aeb62845c712da0d139e5
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 Ilya Grigorik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
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/base64.rb
CHANGED
|
@@ -30,16 +30,16 @@ elsif !defined?(Base64)
|
|
|
30
30
|
str.tr!("+/", "-_")
|
|
31
31
|
str
|
|
32
32
|
end
|
|
33
|
-
end
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
def urlsafe_decode64(str)
|
|
35
|
+
if !str.end_with?("=") && str.length % 4 != 0
|
|
36
|
+
str = str.ljust((str.length + 3) & ~3, "=")
|
|
37
|
+
str.tr!("-_", "+/")
|
|
38
|
+
else
|
|
39
|
+
str = str.tr("-_", "+/")
|
|
40
|
+
end
|
|
41
|
+
strict_decode64(str)
|
|
41
42
|
end
|
|
42
|
-
strict_decode64(str)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
data/lib/http/2/client.rb
CHANGED
|
@@ -64,7 +64,7 @@ module HTTP2
|
|
|
64
64
|
@state = :connected
|
|
65
65
|
emit(:frame, CONNECTION_PREFACE_MAGIC)
|
|
66
66
|
|
|
67
|
-
payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
|
67
|
+
payload = @local_settings.connection_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
|
68
68
|
settings(payload)
|
|
69
69
|
end
|
|
70
70
|
|
data/lib/http/2/connection.rb
CHANGED
|
@@ -10,25 +10,6 @@ module HTTP2
|
|
|
10
10
|
# Default stream_limit
|
|
11
11
|
DEFAULT_MAX_CONCURRENT_STREAMS = 100
|
|
12
12
|
|
|
13
|
-
# Default values for SETTINGS frame, as defined by the spec.
|
|
14
|
-
SPEC_DEFAULT_CONNECTION_SETTINGS = {
|
|
15
|
-
settings_header_table_size: 4096,
|
|
16
|
-
settings_enable_push: 1, # enabled for servers
|
|
17
|
-
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
|
|
18
|
-
settings_initial_window_size: 65_535,
|
|
19
|
-
settings_max_frame_size: 16_384,
|
|
20
|
-
settings_max_header_list_size: (2 << 30) - 1 # unlimited
|
|
21
|
-
}.freeze
|
|
22
|
-
|
|
23
|
-
DEFAULT_CONNECTION_SETTINGS = {
|
|
24
|
-
settings_header_table_size: 4096,
|
|
25
|
-
settings_enable_push: 1, # enabled for servers
|
|
26
|
-
settings_max_concurrent_streams: 100,
|
|
27
|
-
settings_initial_window_size: 65_535,
|
|
28
|
-
settings_max_frame_size: 16_384,
|
|
29
|
-
settings_max_header_list_size: (2 << 30) - 1 # unlimited
|
|
30
|
-
}.freeze
|
|
31
|
-
|
|
32
13
|
# Default stream priority (lower values are higher priority).
|
|
33
14
|
DEFAULT_WEIGHT = 16
|
|
34
15
|
|
|
@@ -56,7 +37,8 @@ module HTTP2
|
|
|
56
37
|
include Error
|
|
57
38
|
include BufferUtils
|
|
58
39
|
|
|
59
|
-
# Connection state (:
|
|
40
|
+
# Connection state (:waiting_magic, :waiting_connection_preface,
|
|
41
|
+
# :connected, :closing, :closed).
|
|
60
42
|
attr_reader :state
|
|
61
43
|
|
|
62
44
|
# Size of current connection flow control window (by default, set to
|
|
@@ -77,13 +59,12 @@ module HTTP2
|
|
|
77
59
|
attr_accessor :active_stream_count
|
|
78
60
|
|
|
79
61
|
# Initializes new connection object.
|
|
80
|
-
#
|
|
81
62
|
def initialize(settings = {})
|
|
82
|
-
@local_settings =
|
|
83
|
-
@remote_settings =
|
|
63
|
+
@local_settings = Settings.new(**settings)
|
|
64
|
+
@remote_settings = Settings.new(settings_max_concurrent_streams: Framer::MAX_STREAM_ID)
|
|
84
65
|
|
|
85
|
-
@compressor = Header::Compressor.new(
|
|
86
|
-
@decompressor = Header::Decompressor.new(
|
|
66
|
+
@compressor = Header::Compressor.new(@local_settings)
|
|
67
|
+
@decompressor = Header::Decompressor.new(@local_settings)
|
|
87
68
|
|
|
88
69
|
@active_stream_count = 0
|
|
89
70
|
@last_stream_id = 0
|
|
@@ -92,11 +73,11 @@ module HTTP2
|
|
|
92
73
|
@oldest_stream_recently_closed = nil
|
|
93
74
|
@pending_settings = []
|
|
94
75
|
|
|
95
|
-
@framer = Framer.new(@local_settings
|
|
76
|
+
@framer = Framer.new(@local_settings.settings_max_frame_size)
|
|
96
77
|
|
|
97
|
-
@local_window_limit = @local_settings
|
|
78
|
+
@local_window_limit = @local_settings.settings_initial_window_size
|
|
98
79
|
@local_window = @local_window_limit
|
|
99
|
-
@remote_window_limit = @remote_settings
|
|
80
|
+
@remote_window_limit = @remote_settings.settings_initial_window_size
|
|
100
81
|
@remote_window = @remote_window_limit
|
|
101
82
|
|
|
102
83
|
@recv_buffer = "".b
|
|
@@ -113,8 +94,15 @@ module HTTP2
|
|
|
113
94
|
@send_buffer = FrameBuffer.new
|
|
114
95
|
end
|
|
115
96
|
|
|
97
|
+
# No new streams may be opened (a GOAWAY was sent or received).
|
|
116
98
|
def closed?
|
|
117
|
-
@state == :closed
|
|
99
|
+
@state == :closed || @state == :closing
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Graceful GOAWAY received: tracked streams may still complete,
|
|
103
|
+
# but no new ones can be opened.
|
|
104
|
+
def closing?
|
|
105
|
+
@state == :closing
|
|
118
106
|
end
|
|
119
107
|
|
|
120
108
|
# Allocates new stream for current connection.
|
|
@@ -123,12 +111,15 @@ module HTTP2
|
|
|
123
111
|
# @param window [Integer]
|
|
124
112
|
# @param parent [Stream]
|
|
125
113
|
def new_stream(**args)
|
|
126
|
-
raise ConnectionClosed if
|
|
127
|
-
raise StreamLimitExceeded if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams]
|
|
114
|
+
raise ConnectionClosed if closed?
|
|
128
115
|
|
|
129
116
|
connection_error(:protocol_error, msg: "id is smaller than previous") if @stream_id < @last_stream_id
|
|
130
117
|
|
|
131
|
-
stream = activate_stream(
|
|
118
|
+
stream = activate_stream(
|
|
119
|
+
id: @stream_id,
|
|
120
|
+
max_concurrent_streams: @remote_settings.settings_max_concurrent_streams,
|
|
121
|
+
**args
|
|
122
|
+
)
|
|
132
123
|
@last_stream_id = stream.id
|
|
133
124
|
|
|
134
125
|
@stream_id += 2
|
|
@@ -158,8 +149,7 @@ module HTTP2
|
|
|
158
149
|
def goaway(error = :no_error, payload = nil)
|
|
159
150
|
send(type: :goaway, stream: 0, last_stream: @last_stream_id,
|
|
160
151
|
error: error, payload: payload)
|
|
161
|
-
|
|
162
|
-
@closed_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
152
|
+
close!
|
|
163
153
|
end
|
|
164
154
|
|
|
165
155
|
# Sends a WINDOW_UPDATE frame to the peer.
|
|
@@ -203,7 +193,7 @@ module HTTP2
|
|
|
203
193
|
elsif read_str(@recv_buffer, 24) == CONNECTION_PREFACE_MAGIC
|
|
204
194
|
# MAGIC is OK. Send our settings
|
|
205
195
|
@state = :waiting_connection_preface
|
|
206
|
-
payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
|
196
|
+
payload = @local_settings.connection_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
|
207
197
|
settings(payload)
|
|
208
198
|
else
|
|
209
199
|
raise HandshakeError
|
|
@@ -244,7 +234,7 @@ module HTTP2
|
|
|
244
234
|
# prevent HTTP/2 CONTINUATION FLOOD
|
|
245
235
|
# same heuristic as the one from HAProxy: https://www.haproxy.com/blog/haproxy-is-resilient-to-the-http-2-continuation-flood
|
|
246
236
|
# different mitigation (connection closed, instead of 400 response)
|
|
247
|
-
unless @continuation_size < @local_settings
|
|
237
|
+
unless @continuation_size < @local_settings.settings_max_frame_size
|
|
248
238
|
connection_error(:protocol_error,
|
|
249
239
|
msg: "too many continuations received")
|
|
250
240
|
end
|
|
@@ -295,7 +285,11 @@ module HTTP2
|
|
|
295
285
|
# PUSH_PROMISE and CONTINUATION frames MUST be minimally
|
|
296
286
|
# processed to ensure a consistent compression state
|
|
297
287
|
decode_headers(frame)
|
|
298
|
-
|
|
288
|
+
# After a GOAWAY, HEADERS may only complete a tracked stream at
|
|
289
|
+
# or below the highest id seen (Section 6.8); anything else is
|
|
290
|
+
# discarded - decoded above, so the compression state is intact.
|
|
291
|
+
# Skip only this frame: later ones may serve completing streams.
|
|
292
|
+
next if closed? && (stream_id > @last_stream_id || !@streams.key?(stream_id))
|
|
299
293
|
|
|
300
294
|
stream = @streams[stream_id]
|
|
301
295
|
if stream.nil?
|
|
@@ -323,7 +317,7 @@ module HTTP2
|
|
|
323
317
|
end
|
|
324
318
|
|
|
325
319
|
decode_headers(frame)
|
|
326
|
-
|
|
320
|
+
next if closed?
|
|
327
321
|
|
|
328
322
|
# PUSH_PROMISE frames MUST be associated with an existing, peer-
|
|
329
323
|
# initiated stream... A receiver MUST treat the receipt of a
|
|
@@ -375,11 +369,16 @@ module HTTP2
|
|
|
375
369
|
end
|
|
376
370
|
else
|
|
377
371
|
case frame_type
|
|
378
|
-
#
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
# unused or closed parent stream.
|
|
372
|
+
# Priority signaling is deprecated (RFC 9113 Section 5.3.2), but
|
|
373
|
+
# a PRIORITY frame may still name a stream id not opened yet:
|
|
374
|
+
# activate it, so the priority sticks if the stream opens.
|
|
382
375
|
when :priority
|
|
376
|
+
# After a GOAWAY, a PRIORITY for a stream already used and
|
|
377
|
+
# closed must not resurrect it: reprioritizing a closed
|
|
378
|
+
# parent is a no-op here, and a phantom stream would hold
|
|
379
|
+
# the draining connection open.
|
|
380
|
+
next if closed? && stream_id <= @last_stream_id
|
|
381
|
+
|
|
383
382
|
stream = activate_stream(
|
|
384
383
|
id: stream_id,
|
|
385
384
|
weight: frame[:weight] || DEFAULT_WEIGHT,
|
|
@@ -495,7 +494,7 @@ module HTTP2
|
|
|
495
494
|
@state = :connected
|
|
496
495
|
connection_settings(frame)
|
|
497
496
|
|
|
498
|
-
when :connected
|
|
497
|
+
when :connected, :closing
|
|
499
498
|
case frame_type
|
|
500
499
|
when :settings
|
|
501
500
|
# @type var frame: settings_frame
|
|
@@ -510,8 +509,13 @@ module HTTP2
|
|
|
510
509
|
# Receivers of a GOAWAY frame MUST NOT open additional streams on
|
|
511
510
|
# the connection, although a new connection can be established
|
|
512
511
|
# for new streams.
|
|
513
|
-
|
|
514
|
-
|
|
512
|
+
if frame[:error] == :no_error && !@streams.empty?
|
|
513
|
+
# A graceful GOAWAY lets tracked streams complete before the
|
|
514
|
+
# connection closes (Section 6.8).
|
|
515
|
+
@state = :closing
|
|
516
|
+
else
|
|
517
|
+
close!
|
|
518
|
+
end
|
|
515
519
|
emit(:goaway, frame[:last_stream], frame[:error], frame[:payload])
|
|
516
520
|
when :altsvc
|
|
517
521
|
# @type var frame: altsvc_frame
|
|
@@ -728,7 +732,7 @@ module HTTP2
|
|
|
728
732
|
#: @type var payload: String
|
|
729
733
|
headers_frame[:payload] = payload
|
|
730
734
|
|
|
731
|
-
max_frame_size = @remote_settings
|
|
735
|
+
max_frame_size = @remote_settings.settings_max_frame_size
|
|
732
736
|
|
|
733
737
|
# if single frame, return immediately
|
|
734
738
|
if payload.bytesize <= max_frame_size
|
|
@@ -768,19 +772,28 @@ module HTTP2
|
|
|
768
772
|
# connection managemet callbacks.
|
|
769
773
|
#
|
|
770
774
|
# @param id [Integer]
|
|
775
|
+
# @param max_concurrent_streams [Symbol] threshold to cap the number of active streams with.
|
|
771
776
|
# @param priority [Integer]
|
|
772
777
|
# @param window [Integer]
|
|
773
778
|
# @param parent [Stream]
|
|
774
|
-
def activate_stream(id:, **args)
|
|
779
|
+
def activate_stream(id:, max_concurrent_streams: @local_settings.settings_max_concurrent_streams, **args)
|
|
775
780
|
connection_error(msg: "Stream ID already exists") if @streams.key?(id)
|
|
776
781
|
|
|
777
|
-
|
|
782
|
+
# SETTINGS_MAX_CONCURRENT_STREAMS limits the number of concurrent streams that the sender
|
|
783
|
+
# of the setting permits the receiver to create (RFC 9113, section 5.1.2), so the bounding
|
|
784
|
+
# limit is the one advertised by the endpoint that did *not* open the stream.
|
|
785
|
+
raise StreamLimitExceeded if @active_stream_count >= max_concurrent_streams
|
|
778
786
|
|
|
779
787
|
stream = Stream.new(connection: self, id: id, **args)
|
|
780
788
|
|
|
781
789
|
stream.once(:close) do
|
|
782
790
|
@streams.delete(id)
|
|
783
791
|
|
|
792
|
+
# A graceful GOAWAY leaves the connection :closing until the
|
|
793
|
+
# tracked streams complete (Section 6.8); the last one to close
|
|
794
|
+
# moves it to its terminal state.
|
|
795
|
+
close! if @state == :closing && @streams.empty?
|
|
796
|
+
|
|
784
797
|
# Store a reference to the closed stream, such that we can respond
|
|
785
798
|
# to any in-flight frames while close is registered on both sides.
|
|
786
799
|
# References to such streams will be purged whenever another stream
|
|
@@ -802,6 +815,8 @@ module HTTP2
|
|
|
802
815
|
true
|
|
803
816
|
end
|
|
804
817
|
@oldest_stream_recently_closed = new_oldest
|
|
818
|
+
else
|
|
819
|
+
@oldest_stream_recently_closed ||= now
|
|
805
820
|
end
|
|
806
821
|
|
|
807
822
|
@streams_recently_closed[id] = now
|
|
@@ -811,6 +826,11 @@ module HTTP2
|
|
|
811
826
|
@streams[id] = stream
|
|
812
827
|
end
|
|
813
828
|
|
|
829
|
+
def close!
|
|
830
|
+
@state = :closed
|
|
831
|
+
@closed_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
832
|
+
end
|
|
833
|
+
|
|
814
834
|
def verify_stream_order(id)
|
|
815
835
|
return unless id.odd?
|
|
816
836
|
|
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/framer.rb
CHANGED
|
@@ -130,10 +130,10 @@ module HTTP2
|
|
|
130
130
|
|
|
131
131
|
stream_id = frame.fetch(:stream, 0)
|
|
132
132
|
|
|
133
|
-
raise CompressionError, "Stream ID (#{stream_id}) is
|
|
133
|
+
raise CompressionError, "Stream ID (#{stream_id}) is outside the valid range" unless stream_id.between?(0, MAX_STREAM_ID)
|
|
134
134
|
|
|
135
|
-
if type == :window_update && frame[:increment]
|
|
136
|
-
raise CompressionError, "Window increment (#{frame[:increment]}) is
|
|
135
|
+
if type == :window_update && !frame[:increment].between?(1, MAX_WINDOWINC)
|
|
136
|
+
raise CompressionError, "Window increment (#{frame[:increment]}) is outside the valid range"
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
flags = frame[:flags]
|
|
@@ -7,18 +7,16 @@ module HTTP2
|
|
|
7
7
|
include PackingExtensions
|
|
8
8
|
include BufferUtils
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@cc = EncodingContext.new(options)
|
|
10
|
+
def initialize(settings = Settings.new)
|
|
11
|
+
@cc = EncodingContext.new(settings)
|
|
13
12
|
end
|
|
14
13
|
|
|
15
|
-
# Set dynamic table size in EncodingContext
|
|
16
|
-
# @param size [Integer] new dynamic table size
|
|
14
|
+
# Set dynamic table +size+ in EncodingContext
|
|
17
15
|
def table_size=(size)
|
|
18
16
|
@cc.table_size = size
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
# Encodes
|
|
19
|
+
# Encodes +i+ via integer representation into +buffer+ at the offset set by +offset+.
|
|
22
20
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-5.1
|
|
23
21
|
#
|
|
24
22
|
# If I < 2^N - 1, encode I on N bits
|
|
@@ -30,11 +28,6 @@ module HTTP2
|
|
|
30
28
|
# I = I / 128
|
|
31
29
|
# encode (I) on 8 bits
|
|
32
30
|
#
|
|
33
|
-
# @param i [Integer] value to encode
|
|
34
|
-
# @param n [Integer] number of available bits
|
|
35
|
-
# @param buffer [String] buffer to pack bytes into
|
|
36
|
-
# @param offset [Integer] offset to insert packed bytes in buffer
|
|
37
|
-
# @return [String] binary string
|
|
38
31
|
def integer(i, n, buffer:, offset: buffer.size)
|
|
39
32
|
limit = (1 << n) - 1
|
|
40
33
|
return pack([i], "C", buffer: buffer, offset: offset) if i < limit
|
|
@@ -65,16 +58,8 @@ module HTTP2
|
|
|
65
58
|
# * If the bit 7 of the first byte is 0, the string value is
|
|
66
59
|
# represented as a list of UTF-8 encoded octets.
|
|
67
60
|
#
|
|
68
|
-
# +@options [:huffman]+ controls whether to use Huffman encoding:
|
|
69
|
-
# :never Do not use Huffman encoding
|
|
70
|
-
# :always Always use Huffman encoding
|
|
71
|
-
# :shorter Use Huffman when the result is strictly shorter
|
|
72
|
-
#
|
|
73
|
-
# @param str [String]
|
|
74
|
-
# @param buffer [String]
|
|
75
|
-
# @return [String] binary string
|
|
76
61
|
def string(str, buffer = "".b)
|
|
77
|
-
case @cc.
|
|
62
|
+
case @cc.settings.huffman
|
|
78
63
|
when :always
|
|
79
64
|
huffman_string(str, buffer)
|
|
80
65
|
when :never
|
|
@@ -93,11 +78,7 @@ module HTTP2
|
|
|
93
78
|
end
|
|
94
79
|
end
|
|
95
80
|
|
|
96
|
-
# Encodes header command with appropriate header representation
|
|
97
|
-
#
|
|
98
|
-
# @param h [Hash] header command
|
|
99
|
-
# @param buffer [String]
|
|
100
|
-
# @return [Buffer]
|
|
81
|
+
# Encodes +h+ header command with appropriate header representation into +buffer+.
|
|
101
82
|
def header(h, buffer = "".b)
|
|
102
83
|
type = h[:type]
|
|
103
84
|
rep = HEADREP[type]
|
|
@@ -128,9 +109,6 @@ module HTTP2
|
|
|
128
109
|
end
|
|
129
110
|
|
|
130
111
|
# Encodes provided list of HTTP headers.
|
|
131
|
-
#
|
|
132
|
-
# @param headers [Array] +[[name, value], ...]+
|
|
133
|
-
# @return [Buffer]
|
|
134
112
|
def encode(headers)
|
|
135
113
|
buffer = "".b
|
|
136
114
|
headers.partition { |f, _| f.start_with? ":" }.each do |hs|
|
|
@@ -144,9 +122,7 @@ module HTTP2
|
|
|
144
122
|
|
|
145
123
|
private
|
|
146
124
|
|
|
147
|
-
#
|
|
148
|
-
# @param buffer [String]
|
|
149
|
-
# @return [String] binary string
|
|
125
|
+
# encodes +str+ into +buffer+ using Huffman encoding.
|
|
150
126
|
def huffman_string(str, buffer = "".b)
|
|
151
127
|
huffman_offset = buffer.bytesize
|
|
152
128
|
buffer << "\x00".b
|
|
@@ -162,18 +138,14 @@ module HTTP2
|
|
|
162
138
|
buffer
|
|
163
139
|
end
|
|
164
140
|
|
|
165
|
-
#
|
|
166
|
-
# @param buffer [String]
|
|
167
|
-
# @return [String] binary string
|
|
141
|
+
# encodes +str+ into +buffer+.
|
|
168
142
|
def plain_string(str, plain = "".b)
|
|
169
143
|
integer(str.bytesize, 7, buffer: plain)
|
|
170
144
|
append_str(plain, str)
|
|
171
145
|
plain
|
|
172
146
|
end
|
|
173
147
|
|
|
174
|
-
#
|
|
175
|
-
# @param huffman_offset [Integer] buffer offset where huffman string was introduced
|
|
176
|
-
# @return [String] binary string
|
|
148
|
+
# encodes the huffman string size from +buffer+ into the string at the offset indicated by +huffman_offset+
|
|
177
149
|
def set_huffman_size(buffer, huffman_offset)
|
|
178
150
|
integer(buffer.bytesize - huffman_offset, 7, buffer: buffer, offset: huffman_offset)
|
|
179
151
|
buffer.setbyte(huffman_offset, buffer.getbyte(huffman_offset) | 0x80)
|
|
@@ -5,32 +5,22 @@ module HTTP2
|
|
|
5
5
|
# Responsible for decoding received headers and maintaining compression
|
|
6
6
|
# context of the opposing peer. Decompressor must be initialized with
|
|
7
7
|
# appropriate starting context based on local role: client or server.
|
|
8
|
-
#
|
|
9
|
-
# @example
|
|
10
|
-
# server_role = Decompressor.new(:request)
|
|
11
|
-
# client_role = Decompressor.new(:response)
|
|
12
8
|
class Decompressor
|
|
13
9
|
include Error
|
|
14
10
|
include BufferUtils
|
|
15
11
|
|
|
16
12
|
FORBIDDEN_HEADERS = %w[connection te].freeze
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@cc = EncodingContext.new(options)
|
|
14
|
+
def initialize(settings = Settings.new)
|
|
15
|
+
@cc = EncodingContext.new(settings)
|
|
21
16
|
end
|
|
22
17
|
|
|
23
|
-
# Set dynamic table size in EncodingContext
|
|
24
|
-
# @param size [Integer] new dynamic table size
|
|
18
|
+
# Set dynamic table +size+ in EncodingContext
|
|
25
19
|
def table_size=(size)
|
|
26
20
|
@cc.table_size = size
|
|
27
21
|
end
|
|
28
22
|
|
|
29
|
-
# Decodes integer value from provided buffer
|
|
30
|
-
#
|
|
31
|
-
# @param buf [String]
|
|
32
|
-
# @param n [Integer] number of available bits
|
|
33
|
-
# @return [Integer]
|
|
23
|
+
# Decodes integer value from provided +buffer+.
|
|
34
24
|
def integer(buf, n)
|
|
35
25
|
limit = (1 << n) - 1
|
|
36
26
|
if n.zero?
|
|
@@ -55,11 +45,9 @@ module HTTP2
|
|
|
55
45
|
i
|
|
56
46
|
end
|
|
57
47
|
|
|
58
|
-
# Decodes string value from provided
|
|
48
|
+
# Decodes string value from provided +buf+.
|
|
59
49
|
#
|
|
60
|
-
#
|
|
61
|
-
# @return [String] UTF-8 encoded string
|
|
62
|
-
# @raise [CompressionError] when input is malformed
|
|
50
|
+
# raises CompressionError when input is malformed
|
|
63
51
|
def string(buf)
|
|
64
52
|
raise CompressionError, "invalid header block fragment" if buf.empty?
|
|
65
53
|
|
|
@@ -72,10 +60,7 @@ module HTTP2
|
|
|
72
60
|
str.force_encoding(Encoding::UTF_8)
|
|
73
61
|
end
|
|
74
62
|
|
|
75
|
-
# Decodes header command from provided
|
|
76
|
-
#
|
|
77
|
-
# @param buf [Buffer]
|
|
78
|
-
# @return [Hash] command
|
|
63
|
+
# Decodes header command from provided +buf+.
|
|
79
64
|
def header(buf)
|
|
80
65
|
peek = buf.getbyte(0)
|
|
81
66
|
|
|
@@ -109,13 +94,9 @@ module HTTP2
|
|
|
109
94
|
end
|
|
110
95
|
end
|
|
111
96
|
|
|
112
|
-
# Decodes and processes header commands within provided
|
|
113
|
-
#
|
|
114
|
-
# @param buf [Buffer]
|
|
115
|
-
# @param frame [HTTP2::Frame, nil]
|
|
116
|
-
# @return [Array] +[[name, value], ...]
|
|
97
|
+
# Decodes and processes header commands within provided +buf+.
|
|
117
98
|
def decode(buf, frame = nil)
|
|
118
|
-
list = []
|
|
99
|
+
list = [] #: Array[header_pair]
|
|
119
100
|
decoding_pseudo_headers = true
|
|
120
101
|
@cc.listen_on_table do
|
|
121
102
|
until buf.empty?
|
|
@@ -86,12 +86,6 @@ module HTTP2
|
|
|
86
86
|
|
|
87
87
|
STATIC_TABLE_SIZE = STATIC_TABLE.size
|
|
88
88
|
|
|
89
|
-
DEFAULT_OPTIONS = {
|
|
90
|
-
huffman: :shorter,
|
|
91
|
-
index: :all,
|
|
92
|
-
table_size: 4096
|
|
93
|
-
}.freeze
|
|
94
|
-
|
|
95
89
|
STATIC_ALL = %i[all static].freeze
|
|
96
90
|
|
|
97
91
|
STATIC_NEVER = %i[never static].freeze
|
|
@@ -99,37 +93,27 @@ module HTTP2
|
|
|
99
93
|
# Current table of header key-value pairs.
|
|
100
94
|
attr_reader :table
|
|
101
95
|
|
|
102
|
-
# Current encoding
|
|
103
|
-
|
|
104
|
-
# :table_size Integer maximum dynamic table size in bytes
|
|
105
|
-
# :huffman Symbol :always, :never, :shorter
|
|
106
|
-
# :index Symbol :all, :static, :never
|
|
107
|
-
attr_reader :options
|
|
96
|
+
# Current encoding settings
|
|
97
|
+
attr_reader :settings
|
|
108
98
|
|
|
109
99
|
# Current table size in octets
|
|
110
100
|
attr_reader :current_table_size
|
|
111
101
|
|
|
112
102
|
# Initializes compression context with appropriate client/server
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
# @param options [Hash] encoding options
|
|
116
|
-
# :table_size Integer maximum dynamic table size in bytes
|
|
117
|
-
# :huffman Symbol :always, :never, :shorter
|
|
118
|
-
# :index Symbol :all, :static, :never
|
|
119
|
-
def initialize(options = {})
|
|
103
|
+
# +settings+ and maximum size of the dynamic table.
|
|
104
|
+
def initialize(settings = Settings.new)
|
|
120
105
|
@table = []
|
|
121
106
|
@table_by_field = Hash.new { |hs, k| hs[k] = [] }
|
|
122
107
|
@unshifts = 0
|
|
123
|
-
@
|
|
124
|
-
@limit =
|
|
108
|
+
@settings = settings
|
|
109
|
+
@limit = settings.table_size
|
|
125
110
|
@_table_updated = false
|
|
126
111
|
@current_table_size = 0
|
|
127
112
|
end
|
|
128
113
|
|
|
129
114
|
# Duplicates current compression context
|
|
130
|
-
# @return [EncodingContext]
|
|
131
115
|
def dup
|
|
132
|
-
other = EncodingContext.new(@
|
|
116
|
+
other = EncodingContext.new(@settings)
|
|
133
117
|
t = @table
|
|
134
118
|
tbf = @table_by_field.transform_values(&:dup)
|
|
135
119
|
unshifts = @unshifts
|
|
@@ -143,16 +127,13 @@ module HTTP2
|
|
|
143
127
|
other
|
|
144
128
|
end
|
|
145
129
|
|
|
146
|
-
# Finds an entry in current dynamic table by index
|
|
147
|
-
# Note that index is zero-based in this module.
|
|
130
|
+
# Finds an entry in current dynamic table by +index+.
|
|
131
|
+
# Note that +index+ is zero-based in this module.
|
|
148
132
|
#
|
|
149
|
-
# If the index is greater than the last index in the static table,
|
|
133
|
+
# If the +index+ is greater than the last index in the static table,
|
|
150
134
|
# an entry in the dynamic table is dereferenced.
|
|
151
135
|
#
|
|
152
|
-
# If the index is greater than the last header index, an error is raised.
|
|
153
|
-
#
|
|
154
|
-
# @param index [Integer] zero-based index in the dynamic table.
|
|
155
|
-
# @return [Array] +[key, value]+
|
|
136
|
+
# If the +index+ is greater than the last header index, an error is raised.
|
|
156
137
|
def dereference(index)
|
|
157
138
|
# NOTE: index is zero-based in this module.
|
|
158
139
|
return STATIC_TABLE[index] if index < STATIC_TABLE_SIZE
|
|
@@ -166,10 +147,6 @@ module HTTP2
|
|
|
166
147
|
|
|
167
148
|
# Header Block Processing
|
|
168
149
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-4.1
|
|
169
|
-
#
|
|
170
|
-
# @param cmd [Hash] { type:, name:, value:, index: }
|
|
171
|
-
# @return [Array, nil] +[name, value]+ header field that is added to the decoded header list,
|
|
172
|
-
# or nil if +cmd[:type]+ is +:changetablesize+
|
|
173
150
|
def process(cmd)
|
|
174
151
|
type = cmd[:type]
|
|
175
152
|
name = cmd[:name]
|
|
@@ -181,7 +158,7 @@ module HTTP2
|
|
|
181
158
|
|
|
182
159
|
# we can receive multiple table size change commands inside a header frame. However,
|
|
183
160
|
# we should blow up if we receive another frame where the new table size is bigger.
|
|
184
|
-
table_size_updated = @limit != @
|
|
161
|
+
table_size_updated = @limit != @settings.table_size
|
|
185
162
|
|
|
186
163
|
raise CompressionError, "dynamic table size update exceed limit" if !table_size_updated && value > @limit
|
|
187
164
|
|
|
@@ -217,11 +194,12 @@ module HTTP2
|
|
|
217
194
|
emit = [name, value]
|
|
218
195
|
|
|
219
196
|
# add to table
|
|
220
|
-
|
|
197
|
+
cmdsize = name.bytesize + value.bytesize + 32
|
|
198
|
+
if type == :incremental && size_check?(cmdsize)
|
|
221
199
|
@table.unshift(emit)
|
|
222
200
|
@unshifts += 1
|
|
223
201
|
@table_by_field[name].unshift([value, @unshifts])
|
|
224
|
-
@current_table_size +=
|
|
202
|
+
@current_table_size += cmdsize
|
|
225
203
|
@_table_updated = true
|
|
226
204
|
end
|
|
227
205
|
|
|
@@ -231,16 +209,10 @@ module HTTP2
|
|
|
231
209
|
end
|
|
232
210
|
end
|
|
233
211
|
|
|
234
|
-
# Plan
|
|
235
|
-
# :never Do not use dynamic table or static table reference at all.
|
|
236
|
-
# :static Use static table only.
|
|
237
|
-
# :all Use all of them.
|
|
238
|
-
#
|
|
239
|
-
# @param headers [Array] +[[name, value], ...]+
|
|
240
|
-
# @return [Array] array of commands
|
|
212
|
+
# Plan +headers+ compression.
|
|
241
213
|
def encode(headers)
|
|
242
214
|
# Literals commands are marked with :noindex when index is not used
|
|
243
|
-
noindex = STATIC_NEVER.include?(@
|
|
215
|
+
noindex = STATIC_NEVER.include?(@settings.index)
|
|
244
216
|
|
|
245
217
|
headers.each do |field, value|
|
|
246
218
|
# Literal header names MUST be translated to lowercase before
|
|
@@ -254,26 +226,17 @@ module HTTP2
|
|
|
254
226
|
end
|
|
255
227
|
end
|
|
256
228
|
|
|
257
|
-
# Emits command for a header.
|
|
229
|
+
# Emits command for a +field+/+value+ header.
|
|
258
230
|
# Prefer static table over dynamic table.
|
|
259
231
|
# Prefer exact match over name-only match.
|
|
260
232
|
#
|
|
261
|
-
# +@options [:index]+ controls whether to use the dynamic table,
|
|
262
|
-
# static table, or both.
|
|
263
|
-
# :never Do not use dynamic table or static table reference at all.
|
|
264
|
-
# :static Use static table only.
|
|
265
|
-
# :all Use all of them.
|
|
266
|
-
#
|
|
267
|
-
# @param field [String] the header field
|
|
268
|
-
# @param value [String] the header value
|
|
269
|
-
# @return [Hash] command
|
|
270
233
|
def addcmd(field, value)
|
|
271
234
|
# @type var exact: Integer?
|
|
272
235
|
exact = nil
|
|
273
236
|
# @type var name_only: Integer?
|
|
274
237
|
name_only = nil
|
|
275
238
|
|
|
276
|
-
index_type = @
|
|
239
|
+
index_type = @settings.index
|
|
277
240
|
|
|
278
241
|
if STATIC_ALL.include?(index_type) &&
|
|
279
242
|
STATIC_TABLE_BY_FIELD.key?(field)
|
|
@@ -337,11 +300,10 @@ module HTTP2
|
|
|
337
300
|
end
|
|
338
301
|
end
|
|
339
302
|
|
|
303
|
+
# whether +cmd+ fits in the dynamic table.
|
|
304
|
+
#
|
|
340
305
|
# To keep the dynamic table size lower than or equal to @limit,
|
|
341
306
|
# remove one or more entries at the end of the dynamic table.
|
|
342
|
-
#
|
|
343
|
-
# @param cmdsize [Integer]
|
|
344
|
-
# @return [Boolean] whether +cmd+ fits in the dynamic table.
|
|
345
307
|
def size_check?(cmdsize)
|
|
346
308
|
resize_table(cmdsize)
|
|
347
309
|
cmdsize <= @limit
|
|
@@ -22,12 +22,7 @@ module HTTP2
|
|
|
22
22
|
|
|
23
23
|
EOS_PADDING = (0..7).map { |n| ("1" * n).b.freeze }.freeze
|
|
24
24
|
|
|
25
|
-
# Encodes provided
|
|
26
|
-
# Length is not encoded in this method.
|
|
27
|
-
#
|
|
28
|
-
# @param str [String]
|
|
29
|
-
# @param buffer [String]
|
|
30
|
-
# @return [String] binary string
|
|
25
|
+
# Encodes provided +str+ via huffman encoding into +buffer+ .
|
|
31
26
|
def encode(str, buffer = "".b)
|
|
32
27
|
bitstring = String.new("", encoding: Encoding::BINARY, capacity: (str.bytesize * 30) + ((8 - str.size) % 8))
|
|
33
28
|
str.each_byte { |chr| append_str(bitstring, ENCODE_TABLE[chr]) }
|
|
@@ -35,11 +30,9 @@ module HTTP2
|
|
|
35
30
|
pack([bitstring], "B*", buffer: buffer)
|
|
36
31
|
end
|
|
37
32
|
|
|
38
|
-
# Decodes provided Huffman coded string
|
|
33
|
+
# Decodes provided Huffman coded string in +buf+.
|
|
39
34
|
#
|
|
40
|
-
#
|
|
41
|
-
# @return [String] binary string
|
|
42
|
-
# @raise [CompressionError] when Huffman coded string is malformed
|
|
35
|
+
# raises CompressionError when Huffman coded string is malformed
|
|
43
36
|
def decode(buf)
|
|
44
37
|
emit = "".b
|
|
45
38
|
state = 0 # start state
|
data/lib/http/2/server.rb
CHANGED
|
@@ -97,10 +97,10 @@ module HTTP2
|
|
|
97
97
|
dependency: 0,
|
|
98
98
|
exclusive: false,
|
|
99
99
|
payload: headers
|
|
100
|
-
}
|
|
100
|
+
} #: headers_frame
|
|
101
101
|
|
|
102
102
|
if body.empty?
|
|
103
|
-
headers_frame[:flags] |=
|
|
103
|
+
headers_frame[:flags] |= END_STREAM
|
|
104
104
|
stream << headers_frame
|
|
105
105
|
else
|
|
106
106
|
stream << headers_frame
|
data/lib/http/2/settings.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTP2
|
|
4
|
+
MAX_HEADER_LIST_SIZE = (2 << 30) - 1
|
|
5
|
+
|
|
4
6
|
# Default values for SETTINGS frame, as defined by the spec.
|
|
5
7
|
SPEC_DEFAULT_CONNECTION_SETTINGS = {
|
|
6
8
|
settings_header_table_size: 4096,
|
|
@@ -8,32 +10,46 @@ module HTTP2
|
|
|
8
10
|
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
|
|
9
11
|
settings_initial_window_size: 65_535,
|
|
10
12
|
settings_max_frame_size: 16_384,
|
|
11
|
-
settings_max_header_list_size:
|
|
13
|
+
settings_max_header_list_size: MAX_HEADER_LIST_SIZE # unlimited
|
|
12
14
|
}.freeze
|
|
13
15
|
|
|
16
|
+
ENCODING_CONTEXT_SETTINGS = %i[huffman index table_size].freeze
|
|
17
|
+
|
|
14
18
|
Settings = Struct.new(
|
|
19
|
+
# connection settings
|
|
15
20
|
:settings_header_table_size,
|
|
16
21
|
:settings_enable_push,
|
|
17
22
|
:settings_max_concurrent_streams,
|
|
18
23
|
:settings_initial_window_size,
|
|
19
24
|
:settings_max_frame_size,
|
|
20
25
|
:settings_max_header_list_size,
|
|
26
|
+
# encoding context settings
|
|
27
|
+
:huffman,
|
|
28
|
+
:index,
|
|
29
|
+
:table_size,
|
|
21
30
|
keyword_init: true
|
|
22
31
|
) do
|
|
23
32
|
def initialize(
|
|
33
|
+
# connection settings
|
|
24
34
|
settings_header_table_size: 4096,
|
|
25
35
|
settings_enable_push: 1,
|
|
26
36
|
settings_max_concurrent_streams: 100,
|
|
27
37
|
settings_initial_window_size: 65_535,
|
|
28
38
|
settings_max_frame_size: 16_384,
|
|
29
|
-
settings_max_header_list_size:
|
|
39
|
+
settings_max_header_list_size: MAX_HEADER_LIST_SIZE,
|
|
40
|
+
# encoding context settings
|
|
41
|
+
huffman: :shorter,
|
|
42
|
+
index: :all,
|
|
43
|
+
table_size: 4096
|
|
30
44
|
)
|
|
31
45
|
super
|
|
32
46
|
end
|
|
33
47
|
|
|
34
|
-
def
|
|
48
|
+
def connection_settings
|
|
49
|
+
return enum_for(__method__) unless block_given?
|
|
50
|
+
|
|
35
51
|
each_pair do |k, v|
|
|
36
|
-
next if
|
|
52
|
+
next if ENCODING_CONTEXT_SETTINGS.include?(k)
|
|
37
53
|
|
|
38
54
|
yield k, v
|
|
39
55
|
end
|
data/lib/http/2/version.rb
CHANGED
data/lib/http/2.rb
CHANGED
data/sig/2.rbs
CHANGED
|
@@ -11,23 +11,10 @@ module HTTP2
|
|
|
11
11
|
|
|
12
12
|
type connection_opts = Hash[Symbol, untyped]
|
|
13
13
|
|
|
14
|
-
type settings_hash = {
|
|
15
|
-
settings_header_table_size: Integer,
|
|
16
|
-
settings_enable_push: Integer,
|
|
17
|
-
settings_max_concurrent_streams: Integer,
|
|
18
|
-
settings_initial_window_size: Integer,
|
|
19
|
-
settings_max_frame_size: Integer,
|
|
20
|
-
settings_max_header_list_size: Integer
|
|
21
|
-
}
|
|
22
|
-
|
|
23
14
|
type settings_ary = Array[settings_enum]
|
|
24
15
|
|
|
25
16
|
type settings_enum = Enumerable[[Symbol, Integer]]
|
|
26
17
|
|
|
27
|
-
SPEC_DEFAULT_CONNECTION_SETTINGS: settings_hash
|
|
28
|
-
|
|
29
|
-
DEFAULT_CONNECTION_SETTINGS: settings_hash
|
|
30
|
-
|
|
31
18
|
DEFAULT_WEIGHT: Integer
|
|
32
19
|
|
|
33
20
|
CONNECTION_PREFACE_MAGIC: String
|
|
@@ -36,6 +23,12 @@ module HTTP2
|
|
|
36
23
|
|
|
37
24
|
RESPONSE_MANDATORY_HEADERS: Array[String]
|
|
38
25
|
|
|
26
|
+
CONNECTION_FRAME_TYPES: Array[Symbol]
|
|
27
|
+
|
|
28
|
+
STREAM_OPEN_STATES: Array[Symbol]
|
|
29
|
+
|
|
30
|
+
HEADERS_FRAME_TYPES: Array[Symbol]
|
|
31
|
+
|
|
39
32
|
# Frame flags
|
|
40
33
|
END_STREAM: Integer
|
|
41
34
|
ACK: Integer
|
data/sig/connection.rbs
CHANGED
|
@@ -4,16 +4,6 @@ module HTTP2
|
|
|
4
4
|
include Emitter
|
|
5
5
|
include BufferUtils
|
|
6
6
|
|
|
7
|
-
REQUEST_MANDATORY_HEADERS: Array[String]
|
|
8
|
-
|
|
9
|
-
RESPONSE_MANDATORY_HEADERS: Array[String]
|
|
10
|
-
|
|
11
|
-
CONNECTION_FRAME_TYPES: Array[Symbol]
|
|
12
|
-
|
|
13
|
-
HEADERS_FRAME_TYPES: Array[Symbol]
|
|
14
|
-
|
|
15
|
-
STREAM_OPEN_STATES: Array[Symbol]
|
|
16
|
-
|
|
17
7
|
attr_reader state: Symbol
|
|
18
8
|
|
|
19
9
|
attr_reader local_window: Integer
|
|
@@ -21,8 +11,8 @@ module HTTP2
|
|
|
21
11
|
|
|
22
12
|
alias window local_window
|
|
23
13
|
|
|
24
|
-
attr_reader remote_settings:
|
|
25
|
-
attr_reader local_settings:
|
|
14
|
+
attr_reader remote_settings: Settings
|
|
15
|
+
attr_reader local_settings: Settings
|
|
26
16
|
attr_reader pending_settings: settings_ary
|
|
27
17
|
|
|
28
18
|
attr_accessor active_stream_count: Integer
|
|
@@ -59,6 +49,10 @@ module HTTP2
|
|
|
59
49
|
|
|
60
50
|
def closed?: () -> bool
|
|
61
51
|
|
|
52
|
+
def closing?: () -> bool
|
|
53
|
+
|
|
54
|
+
def close!: () -> void
|
|
55
|
+
|
|
62
56
|
def new_stream: (**untyped) -> Stream
|
|
63
57
|
|
|
64
58
|
def ping: (String) -> void
|
|
@@ -83,7 +77,7 @@ module HTTP2
|
|
|
83
77
|
|
|
84
78
|
def connection_frame?: (frame) -> bool
|
|
85
79
|
|
|
86
|
-
def connection_management: (
|
|
80
|
+
def connection_management: (frame frame) -> void
|
|
87
81
|
|
|
88
82
|
def ping_management: (frame) -> void
|
|
89
83
|
|
|
@@ -95,7 +89,7 @@ module HTTP2
|
|
|
95
89
|
|
|
96
90
|
def encode_headers: (headers_frame | push_promise_frame) -> void
|
|
97
91
|
|
|
98
|
-
def activate_stream: (id: Integer, **untyped) -> Stream
|
|
92
|
+
def activate_stream: (id: Integer, ?max_concurrent_streams: Integer, **untyped) -> Stream
|
|
99
93
|
|
|
100
94
|
def verify_stream_order: (Integer id) -> void
|
|
101
95
|
|
data/sig/header/compressor.rbs
CHANGED
data/sig/header/decompressor.rbs
CHANGED
|
@@ -11,14 +11,12 @@ module HTTP2
|
|
|
11
11
|
|
|
12
12
|
STATIC_NEVER: Array[Symbol]
|
|
13
13
|
|
|
14
|
-
DEFAULT_OPTIONS: context_hash
|
|
15
|
-
|
|
16
14
|
UPPER: Regexp
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
attr_reader table: Array[header_pair]
|
|
20
18
|
|
|
21
|
-
attr_reader
|
|
19
|
+
attr_reader settings: Settings
|
|
22
20
|
|
|
23
21
|
attr_reader current_table_size: Integer
|
|
24
22
|
|
|
@@ -46,7 +44,7 @@ module HTTP2
|
|
|
46
44
|
|
|
47
45
|
private
|
|
48
46
|
|
|
49
|
-
def initialize: (?
|
|
47
|
+
def initialize: (?Settings settings) -> void
|
|
50
48
|
|
|
51
49
|
def add_to_table: (string name, string value) -> void
|
|
52
50
|
|
data/sig/server.rbs
CHANGED
data/sig/settings.rbs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module HTTP2
|
|
2
|
+
MAX_HEADER_LIST_SIZE: Integer
|
|
3
|
+
|
|
4
|
+
SPEC_DEFAULT_CONNECTION_SETTINGS: Hash[Symbol, Integer]
|
|
5
|
+
|
|
6
|
+
ENCODING_CONTEXT_SETTINGS: Array[Symbol]
|
|
7
|
+
|
|
8
|
+
type huffman_type = :always | :never | :shorter
|
|
9
|
+
|
|
10
|
+
type index_type = :all | :static | :never
|
|
11
|
+
|
|
12
|
+
# Container for all settings required for the management of an HTTP/2 connection. This
|
|
13
|
+
# includes the managemenet of the settings negotiated via the SETTINGS frame, as well as
|
|
14
|
+
# user-level configuration related with header compression.
|
|
15
|
+
class Settings # this is actually a Struct, but RBS does not support it yet
|
|
16
|
+
# connection settings (send and received in the SETTINGS frame)
|
|
17
|
+
|
|
18
|
+
# maximum size of the header compression table used to decode header blocks, in bytes.
|
|
19
|
+
attr_reader settings_header_table_size: Integer
|
|
20
|
+
|
|
21
|
+
# can be used to disable server push (set to 0)
|
|
22
|
+
attr_reader settings_enable_push: Integer
|
|
23
|
+
|
|
24
|
+
# maximum number of concurrent streams that the sender will allow.
|
|
25
|
+
attr_reader settings_max_concurrent_streams: Integer
|
|
26
|
+
|
|
27
|
+
# initial window size (in bytes) for stream-level flow control.
|
|
28
|
+
attr_reader settings_initial_window_size: Integer
|
|
29
|
+
|
|
30
|
+
# max size of for payload that the sender is willing to receive, in bytes.
|
|
31
|
+
attr_reader settings_max_frame_size: Integer
|
|
32
|
+
attr_reader settings_max_header_list_size: Integer
|
|
33
|
+
|
|
34
|
+
# encoding context settings
|
|
35
|
+
|
|
36
|
+
# controls how to use huffman encoding to encode strings. can be one of the following values:
|
|
37
|
+
#
|
|
38
|
+
# * `:always` - Always use Huffman encoding
|
|
39
|
+
# * `:never` - Do not use Huffman encoding
|
|
40
|
+
# * `:shorter` - Use Huffman when the result is strictly shorter
|
|
41
|
+
attr_reader huffman: huffman_type
|
|
42
|
+
|
|
43
|
+
# controls the configuration of the HPACK indexing tables:
|
|
44
|
+
#
|
|
45
|
+
# * `:static` - use the static table only
|
|
46
|
+
# * `:all` - use the static table and the dynamic table
|
|
47
|
+
# * `:never` - do not use any of the tables.
|
|
48
|
+
attr_reader index: index_type
|
|
49
|
+
|
|
50
|
+
# maximum dynamic table size in bytes
|
|
51
|
+
attr_reader table_size: Integer
|
|
52
|
+
|
|
53
|
+
def initialize: (
|
|
54
|
+
# connection settings
|
|
55
|
+
?settings_header_table_size: Integer,
|
|
56
|
+
?settings_enable_push: Integer,
|
|
57
|
+
?settings_max_concurrent_streams: Integer,
|
|
58
|
+
?settings_initial_window_size: Integer,
|
|
59
|
+
?settings_max_frame_size: Integer,
|
|
60
|
+
?settings_max_header_list_size: Integer,
|
|
61
|
+
# encoding context settings
|
|
62
|
+
?huffman: huffman_type,
|
|
63
|
+
?index: index_type,
|
|
64
|
+
?table_size: Integer
|
|
65
|
+
) -> void
|
|
66
|
+
|
|
67
|
+
# yields connection-level settings only.
|
|
68
|
+
def connection_settings: () { (Symbol, Integer) -> void } -> self
|
|
69
|
+
| () -> Enumerable[[Symbol, Integer]]
|
|
70
|
+
end
|
|
71
|
+
end
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -19,6 +19,7 @@ executables: []
|
|
|
19
19
|
extensions: []
|
|
20
20
|
extra_rdoc_files: []
|
|
21
21
|
files:
|
|
22
|
+
- LICENSE
|
|
22
23
|
- README.md
|
|
23
24
|
- lib/http/2.rb
|
|
24
25
|
- lib/http/2/base64.rb
|
|
@@ -54,6 +55,7 @@ files:
|
|
|
54
55
|
- sig/header/encoding_context.rbs
|
|
55
56
|
- sig/header/huffman.rbs
|
|
56
57
|
- sig/server.rbs
|
|
58
|
+
- sig/settings.rbs
|
|
57
59
|
- sig/stream.rbs
|
|
58
60
|
homepage: https://github.com/igrigorik/http-2
|
|
59
61
|
licenses:
|
|
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
80
|
- !ruby/object:Gem::Version
|
|
79
81
|
version: '0'
|
|
80
82
|
requirements: []
|
|
81
|
-
rubygems_version:
|
|
83
|
+
rubygems_version: 4.0.16
|
|
82
84
|
specification_version: 4
|
|
83
85
|
summary: Pure-ruby HTTP 2.0 protocol implementation
|
|
84
86
|
test_files: []
|