http-2 1.2.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b41c25d0512d4056a38265801e7eef05df3cab5720ecbe11ff088c906752019
4
- data.tar.gz: 0b7322ff7f49e8dd892a9804a1a710be23f3c7105f76f7c67f62b281843616fc
3
+ metadata.gz: aa40c026d7345ba9879c03ce22a9ca6c8ec981482381fd0f89c2da5f4d814214
4
+ data.tar.gz: 9cf43f76f7aed43428b636dca605c05bc8c882bfd2f56cabbadaeff71c42648b
5
5
  SHA512:
6
- metadata.gz: 32b4191d53effc33d5186da7093af95698809f2fec89fe37ce01898628d598e7a1215704875ec06ebd9fcaf84b11517310ee46631fb7a2c6b4a2a17fb3ad8b94
7
- data.tar.gz: 5ce7ec0abedca471a25e45883ba4b8d315eb1e78879ed5a8c563dc6a58a7e128bbe9f78f878a8d9956dd90254718634bc67323b57039cb33e87d6e1552cac392
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/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
- def urlsafe_decode64(str)
36
- if !str.end_with?("=") && str.length % 4 != 0
37
- str = str.ljust((str.length + 3) & ~3, "=")
38
- str.tr!("-_", "+/")
39
- else
40
- str = str.tr("-_", "+/")
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
 
@@ -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
 
@@ -78,13 +59,12 @@ module HTTP2
78
59
  attr_accessor :active_stream_count
79
60
 
80
61
  # Initializes new connection object.
81
- #
82
62
  def initialize(settings = {})
83
- @local_settings = DEFAULT_CONNECTION_SETTINGS.merge(settings)
84
- @remote_settings = SPEC_DEFAULT_CONNECTION_SETTINGS.dup
63
+ @local_settings = Settings.new(**settings)
64
+ @remote_settings = Settings.new(settings_max_concurrent_streams: Framer::MAX_STREAM_ID)
85
65
 
86
- @compressor = Header::Compressor.new(settings)
87
- @decompressor = Header::Decompressor.new(settings)
66
+ @compressor = Header::Compressor.new(@local_settings)
67
+ @decompressor = Header::Decompressor.new(@local_settings)
88
68
 
89
69
  @active_stream_count = 0
90
70
  @last_stream_id = 0
@@ -93,11 +73,11 @@ module HTTP2
93
73
  @oldest_stream_recently_closed = nil
94
74
  @pending_settings = []
95
75
 
96
- @framer = Framer.new(@local_settings[:settings_max_frame_size])
76
+ @framer = Framer.new(@local_settings.settings_max_frame_size)
97
77
 
98
- @local_window_limit = @local_settings[:settings_initial_window_size]
78
+ @local_window_limit = @local_settings.settings_initial_window_size
99
79
  @local_window = @local_window_limit
100
- @remote_window_limit = @remote_settings[:settings_initial_window_size]
80
+ @remote_window_limit = @remote_settings.settings_initial_window_size
101
81
  @remote_window = @remote_window_limit
102
82
 
103
83
  @recv_buffer = "".b
@@ -137,7 +117,7 @@ module HTTP2
137
117
 
138
118
  stream = activate_stream(
139
119
  id: @stream_id,
140
- max_concurrent_streams: @remote_settings[:settings_max_concurrent_streams],
120
+ max_concurrent_streams: @remote_settings.settings_max_concurrent_streams,
141
121
  **args
142
122
  )
143
123
  @last_stream_id = stream.id
@@ -213,7 +193,7 @@ module HTTP2
213
193
  elsif read_str(@recv_buffer, 24) == CONNECTION_PREFACE_MAGIC
214
194
  # MAGIC is OK. Send our settings
215
195
  @state = :waiting_connection_preface
216
- 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] }
217
197
  settings(payload)
218
198
  else
219
199
  raise HandshakeError
@@ -254,7 +234,7 @@ module HTTP2
254
234
  # prevent HTTP/2 CONTINUATION FLOOD
255
235
  # same heuristic as the one from HAProxy: https://www.haproxy.com/blog/haproxy-is-resilient-to-the-http-2-continuation-flood
256
236
  # different mitigation (connection closed, instead of 400 response)
257
- unless @continuation_size < @local_settings[:settings_max_frame_size]
237
+ unless @continuation_size < @local_settings.settings_max_frame_size
258
238
  connection_error(:protocol_error,
259
239
  msg: "too many continuations received")
260
240
  end
@@ -752,7 +732,7 @@ module HTTP2
752
732
  #: @type var payload: String
753
733
  headers_frame[:payload] = payload
754
734
 
755
- max_frame_size = @remote_settings[:settings_max_frame_size]
735
+ max_frame_size = @remote_settings.settings_max_frame_size
756
736
 
757
737
  # if single frame, return immediately
758
738
  if payload.bytesize <= max_frame_size
@@ -796,7 +776,7 @@ module HTTP2
796
776
  # @param priority [Integer]
797
777
  # @param window [Integer]
798
778
  # @param parent [Stream]
799
- def activate_stream(id:, max_concurrent_streams: @local_settings[:settings_max_concurrent_streams], **args)
779
+ def activate_stream(id:, max_concurrent_streams: @local_settings.settings_max_concurrent_streams, **args)
800
780
  connection_error(msg: "Stream ID already exists") if @streams.key?(id)
801
781
 
802
782
  # SETTINGS_MAX_CONCURRENT_STREAMS limits the number of concurrent streams that the sender
@@ -835,6 +815,8 @@ module HTTP2
835
815
  true
836
816
  end
837
817
  @oldest_stream_recently_closed = new_oldest
818
+ else
819
+ @oldest_stream_recently_closed ||= now
838
820
  end
839
821
 
840
822
  @streams_recently_closed[id] = now
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 too large" if stream_id > MAX_STREAM_ID
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] > MAX_WINDOWINC
136
- raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
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
- # @param options [Hash] encoding options
11
- def initialize(options = {})
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 provided value via integer representation.
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.options[:huffman]
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
- # @param str [String]
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
- # @param str [String]
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
- # @param buffer [String]
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
- # @param options [Hash] decoding options. Only :table_size is effective.
19
- def initialize(options = {})
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 buffer.
48
+ # Decodes string value from provided +buf+.
59
49
  #
60
- # @param buf [String]
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 buffer.
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 buffer.
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 options
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
- # defaults and maximum size of the dynamic table.
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
- @options = DEFAULT_OPTIONS.merge(options)
124
- @limit = @options[:table_size]
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(@options)
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 != @options[:table_size]
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
- if type == :incremental && size_check?(name.bytesize + value.bytesize + 32)
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 += name.bytesize + value.bytesize + 32
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 header compression according to +@options [:index]+
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?(@options[:index])
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 = @options[:index]
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 value via huffman encoding.
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
- # @param buf [Buffer]
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] |= END_HEADERS
103
+ headers_frame[:flags] |= END_STREAM
104
104
  stream << headers_frame
105
105
  else
106
106
  stream << headers_frame
@@ -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: (2 << 30) - 1 # unlimited
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: (2 << 30) - 1
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 each_setting
48
+ def connection_settings
49
+ return enum_for(__method__) unless block_given?
50
+
35
51
  each_pair do |k, v|
36
- next if v == SPEC_DEFAULT_CONNECTION_SETTINGS[k]
52
+ next if ENCODING_CONTEXT_SETTINGS.include?(k)
37
53
 
38
54
  yield k, v
39
55
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2
4
- VERSION = "1.2.2"
4
+ VERSION = "1.2.3"
5
5
  end
data/lib/http/2.rb CHANGED
@@ -13,6 +13,7 @@ require "http/2/emitter"
13
13
  require "http/2/flow_buffer"
14
14
  require "http/2/header"
15
15
  require "http/2/framer"
16
+ require "http/2/settings"
16
17
  require "http/2/connection"
17
18
  require "http/2/client"
18
19
  require "http/2/server"
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: settings_hash
25
- attr_reader local_settings: settings_hash
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
@@ -87,7 +77,7 @@ module HTTP2
87
77
 
88
78
  def connection_frame?: (frame) -> bool
89
79
 
90
- def connection_management: (connection_frame frame) -> void
80
+ def connection_management: (frame frame) -> void
91
81
 
92
82
  def ping_management: (frame) -> void
93
83
 
@@ -18,7 +18,7 @@ module HTTP2
18
18
 
19
19
  private
20
20
 
21
- def initialize: (?connection_opts options) -> void
21
+ def initialize: (?Settings settings) -> void
22
22
 
23
23
  def huffman_string: (String str, ?String buffer) -> String
24
24
 
@@ -19,7 +19,7 @@ module HTTP2
19
19
  | (String) -> Array[header_pair]
20
20
  private
21
21
 
22
- def initialize: (?connection_opts options) -> void
22
+ def initialize: (?Settings settings) -> void
23
23
  end
24
24
  end
25
25
  end
@@ -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 options: context_hash
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: (?connection_opts options) -> void
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
@@ -1,5 +1,7 @@
1
1
  module HTTP2
2
2
  class Server < Connection
3
+ @origin_set: Array[String]
4
+ @origins_sent: bool
3
5
 
4
6
  def upgrade: (String settings, Enumerable[header_pair] headers, String body) -> void
5
7
 
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.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: