http-2 1.1.2 → 1.2.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.
data/lib/http/2/framer.rb CHANGED
@@ -1,6 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2
4
+ # Frame flags as defined by the spec (max 255 bits)
5
+ # DATA: ( X X COMPRESSED X PADDED X X END_STREAM )
6
+ # HEADERS: ( X X PRIORITY X PADDED END_HEADERS X END_STREAM )
7
+ # PRIORITY: ( X X X X X X X X )
8
+ # RST_STREAM: ( X X X X X X X X )
9
+ # SETTINGS: ( X X X X X X X ACK )
10
+ # PUSH_PROMISE: ( X X X X PADDED END_HEADERS X X )
11
+ # PING: ( X X X X X X X ACK )
12
+ # GOAWAY: ( X X X X X X X X )
13
+ # WINDOW_UPDATE: ( X X X X X X X X )
14
+ # CONTINUATION: ( X X X X X END_HEADERS X X )
15
+ # ALTSVC: ( X X X X X X X X )
16
+ # ORIGIN: ( RESERVED4 X X RESERVED3 X RESERVED2 RESERVED X )
17
+ END_STREAM = ACK = 0b0001 # 1
18
+ RESERVED = 0b0010 # 2
19
+ END_HEADERS = 0b0100 # 4
20
+ PADDED = 0b1000 # 8
21
+ PRIORITY = 0b0010_0000 # 32
22
+
4
23
  # Performs encoding, decoding, and validation of binary HTTP/2 frames.
5
24
  #
6
25
  class Framer
@@ -40,39 +59,6 @@ module HTTP2
40
59
 
41
60
  FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
42
61
 
43
- # Per frame flags as defined by the spec
44
- FRAME_FLAGS = {
45
- data: {
46
- end_stream: 0,
47
- padded: 3,
48
- compressed: 5
49
- },
50
- headers: {
51
- end_stream: 0,
52
- end_headers: 2,
53
- padded: 3,
54
- priority: 5
55
- },
56
- priority: {},
57
- rst_stream: {},
58
- settings: { ack: 0 },
59
- push_promise: {
60
- end_headers: 2,
61
- padded: 3
62
- },
63
- ping: { ack: 0 },
64
- goaway: {},
65
- window_update: {},
66
- continuation: { end_headers: 2 },
67
- altsvc: {},
68
- origin: {
69
- reserved: 1,
70
- reserved2: 2,
71
- reserved3: 4,
72
- reserved4: 8
73
- }
74
- }.each_value(&:freeze).freeze
75
-
76
62
  # Default settings as defined by the spec
77
63
  DEFINED_SETTINGS = {
78
64
  settings_header_table_size: 1,
@@ -83,23 +69,25 @@ module HTTP2
83
69
  settings_max_header_list_size: 6
84
70
  }.freeze
85
71
 
86
- # Default error types as defined by the spec
87
- DEFINED_ERRORS = {
88
- no_error: 0,
89
- protocol_error: 1,
90
- internal_error: 2,
91
- flow_control_error: 3,
92
- settings_timeout: 4,
93
- stream_closed: 5,
94
- frame_size_error: 6,
95
- refused_stream: 7,
96
- cancel: 8,
97
- compression_error: 9,
98
- connect_error: 10,
99
- enhance_your_calm: 11,
100
- inadequate_security: 12,
101
- http_1_1_required: 13
102
- }.freeze
72
+ DEFINED_SETTINGS_BY_ID = DEFINED_SETTINGS.invert.freeze
73
+
74
+ # Default error types as defined by the spec (the code is the array index)
75
+ DEFINED_ERRORS = %i[
76
+ no_error
77
+ protocol_error
78
+ internal_error
79
+ flow_control_error
80
+ settings_timeout
81
+ stream_closed
82
+ frame_size_error
83
+ refused_stream
84
+ cancel
85
+ compression_error
86
+ connect_error
87
+ enhance_your_calm
88
+ inadequate_security
89
+ http_1_1_required
90
+ ].freeze
103
91
 
104
92
  RBIT = 0x7fffffff
105
93
  RBYTE = 0x0fffffff
@@ -108,10 +96,12 @@ module HTTP2
108
96
  UINT16 = "n"
109
97
  UINT8 = "C"
110
98
  HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
99
+ PRIORITYPACK = (UINT32 + UINT8).freeze
100
+ ALTSVCPACK = (UINT32 + UINT16).freeze
111
101
  FRAME_LENGTH_HISHIFT = 16
112
102
  FRAME_LENGTH_LOMASK = 0xFFFF
113
103
 
114
- private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :UINT32, :UINT16, :UINT8
104
+ private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :PRIORITYPACK, :UINT32, :UINT16, :UINT8
115
105
 
116
106
  # Initializes new framer object.
117
107
  #
@@ -146,6 +136,10 @@ module HTTP2
146
136
  raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
147
137
  end
148
138
 
139
+ flags = frame[:flags]
140
+
141
+ raise CompressionError, "Invalid frame flag (#{flags}) for #{type}" unless flags.between?(0, 255)
142
+
149
143
  header = buffer
150
144
 
151
145
  # make sure the buffer is binary and unfrozen
@@ -160,12 +154,7 @@ module HTTP2
160
154
  (length >> FRAME_LENGTH_HISHIFT),
161
155
  (length & FRAME_LENGTH_LOMASK),
162
156
  FRAME_TYPES[type],
163
- frame[:flags].reduce(0) do |acc, f|
164
- position = FRAME_FLAGS[type][f]
165
- raise CompressionError, "Invalid frame flag (#{f}) for #{type}" unless position
166
-
167
- acc | (1 << position)
168
- end,
157
+ flags,
169
158
  stream_id
170
159
  ], HEADERPACK, buffer: header, offset: 0) # 8+16,8,8,32
171
160
  end
@@ -184,9 +173,7 @@ module HTTP2
184
173
 
185
174
  {
186
175
  type: type,
187
- flags: FRAME_FLAGS[type].filter_map do |name, pos|
188
- name if flags.anybits?(1 << pos)
189
- end,
176
+ flags: flags,
190
177
  length: length,
191
178
  stream: stream & RBIT
192
179
  }
@@ -198,10 +185,11 @@ module HTTP2
198
185
  # @param frame [Hash]
199
186
  def generate(frame)
200
187
  length = 0
201
- frame[:flags] ||= EMPTY
188
+ frame[:flags] ||= 0
202
189
 
203
190
  case frame[:type]
204
191
  when :data, :continuation
192
+ # @type var frame: data_frame | continuation_frame
205
193
  bytes = frame[:payload]
206
194
  length = bytes.bytesize
207
195
 
@@ -213,14 +201,16 @@ module HTTP2
213
201
  raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
214
202
  end
215
203
 
216
- frame[:flags] += [:priority] unless frame[:flags].include?(:priority)
204
+ frame[:flags] |= PRIORITY
217
205
  end
218
206
 
219
- if frame[:flags].include?(:priority)
207
+ if frame[:flags].anybits?(PRIORITY)
220
208
  length = 5 + headers.bytesize
221
209
  bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
222
- pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
223
- pack([frame[:weight] - 1], UINT8, buffer: bytes)
210
+ pack(
211
+ [(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT), frame[:weight] - 1],
212
+ PRIORITYPACK, buffer: bytes
213
+ )
224
214
  append_str(bytes, headers)
225
215
  else
226
216
  length = headers.bytesize
@@ -234,8 +224,10 @@ module HTTP2
234
224
 
235
225
  length = 5
236
226
  bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
237
- pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
238
- pack([frame[:weight] - 1], UINT8, buffer: bytes)
227
+ pack(
228
+ [(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT), frame[:weight] - 1],
229
+ PRIORITYPACK, buffer: bytes
230
+ )
239
231
 
240
232
  when :rst_stream
241
233
  length = 4
@@ -248,20 +240,27 @@ module HTTP2
248
240
  end
249
241
 
250
242
  settings = frame[:payload]
251
- bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
252
243
 
253
- settings.each do |(k, v)|
254
- if k.is_a? Integer # rubocop:disable Style/GuardClause
255
- DEFINED_SETTINGS.value?(k) || next
256
- else
257
- k = DEFINED_SETTINGS[k]
244
+ case settings
245
+ when String
246
+ length = settings.bytesize
247
+ bytes = settings
248
+ else
249
+ bytes = String.new("", encoding: Encoding::BINARY, capacity: settings.size * 6)
250
+
251
+ settings.each do |(k, v)|
252
+ if k.is_a? Integer # rubocop:disable Style/GuardClause
253
+ DEFINED_SETTINGS.value?(k) || next
254
+ else
255
+ k = DEFINED_SETTINGS[k]
258
256
 
259
- raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
260
- end
257
+ raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
258
+ end
261
259
 
262
- pack([k], UINT16, buffer: bytes)
263
- pack([v], UINT32, buffer: bytes)
264
- length += 6
260
+ pack([k], UINT16, buffer: bytes)
261
+ pack([v], UINT32, buffer: bytes)
262
+ length += 6
263
+ end
265
264
  end
266
265
 
267
266
  when :push_promise
@@ -295,7 +294,7 @@ module HTTP2
295
294
  when :altsvc
296
295
  length = 6
297
296
  bytes = String.new("", encoding: Encoding::BINARY, capacity: length)
298
- pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes)
297
+ pack([frame[:max_age], frame[:port]], ALTSVCPACK, buffer: bytes)
299
298
  if frame[:proto]
300
299
  raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
301
300
 
@@ -354,7 +353,7 @@ module HTTP2
354
353
 
355
354
  length += padlen
356
355
  pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
357
- frame[:flags] += [:padded]
356
+ frame[:flags] |= PADDED
358
357
 
359
358
  # Padding: Padding octets that contain no application semantic value.
360
359
  # Padding octets MUST be set to zero when sending and ignored when
@@ -375,9 +374,9 @@ module HTTP2
375
374
 
376
375
  frame = read_common_header(buf)
377
376
 
378
- type = frame[:type]
379
- length = frame[:length]
380
- flags = frame[:flags]
377
+ type = frame[:type] #: Symbol
378
+ length = frame[:length] #: Integer
379
+ flags = frame[:flags] #: Integer
381
380
 
382
381
  return if buf.size < 9 + length
383
382
 
@@ -394,15 +393,16 @@ module HTTP2
394
393
  # Process padding
395
394
  padlen = 0
396
395
  if FRAME_TYPES_WITH_PADDING.include?(type)
397
- padded = flags.include?(:padded)
396
+ padded = flags.anybits?(PADDED)
398
397
  if padded
399
398
  padlen = read_str(payload, 1).unpack1(UINT8)
400
- frame[:padding] = padlen + 1
401
399
  raise ProtocolError, "padding too long" if padlen > payload.bytesize
402
400
 
401
+ frame[:padding] = padlen + 1
402
+
403
403
  payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
404
404
  frame[:length] -= frame[:padding]
405
- flags.delete(:padded)
405
+ frame[:flags] ^= PADDED
406
406
  end
407
407
  end
408
408
 
@@ -410,11 +410,11 @@ module HTTP2
410
410
  when :data, :ping, :continuation
411
411
  frame[:payload] = read_str(payload, length)
412
412
  when :headers
413
- if flags.include?(:priority)
413
+ if flags.anybits?(PRIORITY)
414
414
  e_sd = read_uint32(payload)
415
415
  frame[:dependency] = e_sd & RBIT
416
416
  frame[:exclusive] = e_sd.anybits?(EBIT)
417
- weight = payload.byteslice(0, 1).ord + 1
417
+ weight = payload.getbyte(0) + 1
418
418
  frame[:weight] = weight
419
419
  payload = payload.byteslice(1..-1)
420
420
  end
@@ -425,7 +425,7 @@ module HTTP2
425
425
  e_sd = read_uint32(payload)
426
426
  frame[:dependency] = e_sd & RBIT
427
427
  frame[:exclusive] = e_sd.anybits?(EBIT)
428
- weight = payload.byteslice(0, 1).ord + 1
428
+ weight = payload.getbyte(0) + 1
429
429
  frame[:weight] = weight
430
430
  payload = payload.byteslice(1..-1)
431
431
  when :rst_stream
@@ -436,19 +436,19 @@ module HTTP2
436
436
  when :settings
437
437
  # NOTE: frame[:length] might not match the number of frame[:payload]
438
438
  # because unknown extensions are ignored.
439
- frame[:payload] = []
440
439
  raise ProtocolError, "Invalid settings payload length" unless (length % 6).zero?
441
440
 
442
441
  raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if frame[:stream].nonzero?
443
442
 
444
- (frame[:length] / 6).times do
443
+ frame[:payload] = (frame[:length] / 6).times.filter_map do
445
444
  id = read_str(payload, 2).unpack1(UINT16)
446
445
  val = read_uint32(payload)
447
446
 
448
447
  # Unsupported or unrecognized settings MUST be ignored.
449
448
  # Here we send it along.
450
- name, = DEFINED_SETTINGS.find { |_name, v| v == id }
451
- frame[:payload] << [name, val] if name
449
+ if (name = DEFINED_SETTINGS_BY_ID[id])
450
+ [name, val]
451
+ end
452
452
  end
453
453
  when :push_promise
454
454
  frame[:promise_stream] = read_uint32(payload) & RBIT
@@ -464,13 +464,13 @@ module HTTP2
464
464
 
465
465
  frame[:increment] = read_uint32(payload) & RBIT
466
466
  when :altsvc
467
- frame[:max_age], frame[:port] = read_str(payload, 6).unpack(UINT32 + UINT16)
467
+ frame[:max_age], frame[:port] = read_str(payload, 6).unpack(ALTSVCPACK)
468
468
 
469
- len = payload.byteslice(0, 1).ord
469
+ len = payload.getbyte(0)
470
470
  payload = payload.byteslice(1..-1)
471
471
  frame[:proto] = read_str(payload, len) if len > 0
472
472
 
473
- len = payload.byteslice(0, 1).ord
473
+ len = payload.getbyte(0)
474
474
  payload = payload.byteslice(1..-1)
475
475
  frame[:host] = read_str(payload, len) if len > 0
476
476
 
@@ -495,7 +495,7 @@ module HTTP2
495
495
 
496
496
  def pack_error(error, buffer:)
497
497
  unless error.is_a? Integer
498
- error = DEFINED_ERRORS[error]
498
+ error = DEFINED_ERRORS.index(error)
499
499
 
500
500
  raise CompressionError, "Unknown error ID for #{error}" unless error
501
501
  end
@@ -504,7 +504,7 @@ module HTTP2
504
504
  end
505
505
 
506
506
  def unpack_error(error)
507
- DEFINED_ERRORS.key(error) || error
507
+ DEFINED_ERRORS.fetch(error, error)
508
508
  end
509
509
  end
510
510
  end
@@ -83,8 +83,10 @@ module HTTP2
83
83
  huffman = Huffman.encode(str)
84
84
  if huffman.bytesize < str.bytesize
85
85
  huffman_offset = buffer.bytesize
86
+ integer(huffman.bytesize, 7, buffer: buffer)
87
+ buffer.setbyte(huffman_offset, buffer.getbyte(huffman_offset) | 0x80)
86
88
  append_str(buffer, huffman)
87
- set_huffman_size(buffer, huffman_offset)
89
+ buffer
88
90
  else
89
91
  plain_string(str, buffer)
90
92
  end
@@ -119,7 +121,7 @@ module HTTP2
119
121
  end
120
122
 
121
123
  # set header representation pattern on first byte
122
- fb = buffer[offset].ord | rep[:pattern]
124
+ fb = buffer.getbyte(offset) | rep[:pattern]
123
125
  buffer.setbyte(offset, fb)
124
126
 
125
127
  buffer
@@ -147,8 +149,17 @@ module HTTP2
147
149
  # @return [String] binary string
148
150
  def huffman_string(str, buffer = "".b)
149
151
  huffman_offset = buffer.bytesize
152
+ buffer << "\x00".b
150
153
  Huffman.encode(str, buffer)
151
- set_huffman_size(buffer, huffman_offset)
154
+ size = buffer.bytesize - huffman_offset - 1
155
+
156
+ if size < 127
157
+ buffer.setbyte(huffman_offset, 0x80 | size)
158
+ else
159
+ buffer.slice!(huffman_offset, 1)
160
+ set_huffman_size(buffer, huffman_offset)
161
+ end
162
+ buffer
152
163
  end
153
164
 
154
165
  # @param str [String]
@@ -165,7 +176,7 @@ module HTTP2
165
176
  # @return [String] binary string
166
177
  def set_huffman_size(buffer, huffman_offset)
167
178
  integer(buffer.bytesize - huffman_offset, 7, buffer: buffer, offset: huffman_offset)
168
- buffer.setbyte(huffman_offset, buffer[huffman_offset].ord | 0x80)
179
+ buffer.setbyte(huffman_offset, buffer.getbyte(huffman_offset) | 0x80)
169
180
  buffer
170
181
  end
171
182
  end
@@ -33,24 +33,25 @@ module HTTP2
33
33
  # @return [Integer]
34
34
  def integer(buf, n)
35
35
  limit = (1 << n) - 1
36
- i = n.zero? ? 0 : (shift_byte(buf) & limit)
36
+ if n.zero?
37
+ i = 0
38
+ consumed = 0
39
+ else
40
+ i = buf.getbyte(0) & limit
41
+ consumed = 1
42
+ end
37
43
 
38
- m = 0
39
44
  if i == limit
40
- offset = 0
41
-
42
- buf.each_byte.with_index do |byte, idx|
43
- offset = idx
44
- # while (byte = shift_byte(buf))
45
+ m = 0
46
+ while (byte = buf.getbyte(consumed))
45
47
  i += ((byte & 127) << m)
46
48
  m += 7
47
-
49
+ consumed += 1
48
50
  break if byte.nobits?(128)
49
51
  end
50
-
51
- read_str(buf, offset + 1)
52
52
  end
53
53
 
54
+ read_str(buf, consumed)
54
55
  i
55
56
  end
56
57
 
@@ -118,6 +118,8 @@ module HTTP2
118
118
  # :index Symbol :all, :static, :never
119
119
  def initialize(options = {})
120
120
  @table = []
121
+ @table_by_field = Hash.new { |hs, k| hs[k] = [] }
122
+ @unshifts = 0
121
123
  @options = DEFAULT_OPTIONS.merge(options)
122
124
  @limit = @options[:table_size]
123
125
  @_table_updated = false
@@ -129,9 +131,13 @@ module HTTP2
129
131
  def dup
130
132
  other = EncodingContext.new(@options)
131
133
  t = @table
134
+ tbf = @table_by_field.transform_values(&:dup)
135
+ unshifts = @unshifts
132
136
  l = @limit
133
137
  other.instance_eval do
134
138
  @table = t.dup # shallow copy
139
+ @table_by_field = tbf
140
+ @unshifts = unshifts
135
141
  @limit = l
136
142
  end
137
143
  other
@@ -213,6 +219,8 @@ module HTTP2
213
219
  # add to table
214
220
  if type == :incremental && size_check?(name.bytesize + value.bytesize + 32)
215
221
  @table.unshift(emit)
222
+ @unshifts += 1
223
+ @table_by_field[name].unshift([value, @unshifts])
216
224
  @current_table_size += name.bytesize + value.bytesize + 32
217
225
  @_table_updated = true
218
226
  end
@@ -279,14 +287,14 @@ module HTTP2
279
287
  end
280
288
 
281
289
  if index_type == :all && !exact
282
- @table.each_with_index do |(hfield, hvalue), i|
283
- next unless field == hfield
290
+ field_entries = @table_by_field[field]
284
291
 
292
+ field_entries&.each do |hvalue, unshift_id|
293
+ abs_index = (@unshifts - unshift_id) + STATIC_TABLE_SIZE
294
+ name_only ||= abs_index
285
295
  if value == hvalue
286
- exact = i + STATIC_TABLE_SIZE
296
+ exact = abs_index
287
297
  break
288
- else
289
- name_only ||= i + STATIC_TABLE_SIZE
290
298
  end
291
299
  end
292
300
  end
@@ -317,9 +325,13 @@ module HTTP2
317
325
  return if @table.empty?
318
326
 
319
327
  while @current_table_size + cmdsize > @limit
320
-
321
328
  name, value = @table.pop
322
329
  @current_table_size -= name.bytesize + value.bytesize + 32
330
+
331
+ field_arr = @table_by_field[name]
332
+ field_arr.pop
333
+ @table_by_field.delete(name) if field_arr.empty?
334
+
323
335
  break if @table.empty?
324
336
 
325
337
  end
@@ -20,6 +20,8 @@ module HTTP2
20
20
  EOS = 256
21
21
  private_constant :EOS
22
22
 
23
+ EOS_PADDING = (0..7).map { |n| ("1" * n).b.freeze }.freeze
24
+
23
25
  # Encodes provided value via huffman encoding.
24
26
  # Length is not encoded in this method.
25
27
  #
@@ -29,7 +31,7 @@ module HTTP2
29
31
  def encode(str, buffer = "".b)
30
32
  bitstring = String.new("", encoding: Encoding::BINARY, capacity: (str.bytesize * 30) + ((8 - str.size) % 8))
31
33
  str.each_byte { |chr| append_str(bitstring, ENCODE_TABLE[chr]) }
32
- append_str(bitstring, "1" * ((8 - bitstring.size) % 8))
34
+ append_str(bitstring, EOS_PADDING[(8 - bitstring.size) % 8])
33
35
  pack([bitstring], "B*", buffer: buffer)
34
36
  end
35
37
 
@@ -54,13 +56,13 @@ module HTTP2
54
56
  first, state = MACHINE.dig(state, branch)
55
57
  raise CompressionError, "Huffman decode error (EOS found)" if first == EOS
56
58
 
57
- append_str(emit, first.chr) if first
59
+ emit << first if first
58
60
  end
59
61
  end
60
62
  # Check whether partial input is correctly filled
61
63
  raise CompressionError, "Huffman decode error (EOS invalid)" unless state <= MAX_FINAL_STATE
62
64
 
63
- emit.force_encoding(Encoding::BINARY)
65
+ emit
64
66
  end
65
67
 
66
68
  # Huffman table as specified in
@@ -325,7 +327,7 @@ module HTTP2
325
327
  [0x3fffffff, 30]
326
328
  ].each(&:freeze).freeze
327
329
 
328
- ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1] }.each(&:freeze).freeze
330
+ ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1].b.freeze }.freeze
329
331
  end
330
332
  end
331
333
  end
data/lib/http/2/server.rb CHANGED
@@ -78,18 +78,12 @@ module HTTP2
78
78
  receive(CONNECTION_PREFACE_MAGIC)
79
79
 
80
80
  # Process received HTTP2-Settings payload
81
- buf = "".b
82
- append_str(buf, Base64.urlsafe_decode64(settings.to_s))
83
- @framer.common_header(
84
- {
85
- length: buf.bytesize,
86
- type: :settings,
87
- stream: 0,
88
- flags: []
89
- },
90
- buffer: buf
91
- )
92
- receive(buf)
81
+ receive(@framer.generate(
82
+ type: :settings,
83
+ stream: 0,
84
+ flags: 0,
85
+ payload: Base64.urlsafe_decode64(settings.to_s)
86
+ ))
93
87
 
94
88
  # Activate stream (id: 1) with on HTTP/1.1 request parameters
95
89
  stream = activate_stream(id: 1)
@@ -97,7 +91,7 @@ module HTTP2
97
91
 
98
92
  headers_frame = {
99
93
  type: :headers,
100
- flags: [:end_headers],
94
+ flags: END_HEADERS,
101
95
  stream: 1,
102
96
  weight: DEFAULT_WEIGHT,
103
97
  dependency: 0,
@@ -106,11 +100,11 @@ module HTTP2
106
100
  }
107
101
 
108
102
  if body.empty?
109
- headers_frame[:flags] << [:end_stream]
103
+ headers_frame[:flags] |= END_HEADERS
110
104
  stream << headers_frame
111
105
  else
112
106
  stream << headers_frame
113
- stream << { type: :data, stream: 1, payload: body, flags: [:end_stream] }
107
+ stream << { type: :data, stream: 1, payload: body, flags: END_STREAM }
114
108
  end
115
109
 
116
110
  # Mark h2c upgrade as finished
@@ -135,7 +129,7 @@ module HTTP2
135
129
 
136
130
  def connection_settings(frame)
137
131
  super
138
- return unless frame[:flags].include?(:ack) && !@origins_sent
132
+ return unless frame[:flags].anybits?(ACK) && !@origins_sent
139
133
 
140
134
  send(type: :origin, stream: 0, payload: @origin_set)
141
135
  end
@@ -148,7 +142,7 @@ module HTTP2
148
142
  #
149
143
  # @param parent [Stream]
150
144
  # @param headers [Enumerable[String, String]]
151
- # @param flags [Array[Symbol]]
145
+ # @param flags Integer
152
146
  # @param callback [Proc]
153
147
  def promise(parent, headers, flags)
154
148
  promise = new_stream(parent: parent)
@@ -157,7 +151,7 @@ module HTTP2
157
151
  flags: flags,
158
152
  stream: parent.id,
159
153
  promise_stream: promise.id,
160
- payload: headers.to_a
154
+ payload: headers
161
155
  )
162
156
 
163
157
  yield(promise)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTP2
4
+ # Default values for SETTINGS frame, as defined by the spec.
5
+ SPEC_DEFAULT_CONNECTION_SETTINGS = {
6
+ settings_header_table_size: 4096,
7
+ settings_enable_push: 1, # enabled for servers
8
+ settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
9
+ settings_initial_window_size: 65_535,
10
+ settings_max_frame_size: 16_384,
11
+ settings_max_header_list_size: (2 << 30) - 1 # unlimited
12
+ }.freeze
13
+
14
+ Settings = Struct.new(
15
+ :settings_header_table_size,
16
+ :settings_enable_push,
17
+ :settings_max_concurrent_streams,
18
+ :settings_initial_window_size,
19
+ :settings_max_frame_size,
20
+ :settings_max_header_list_size,
21
+ keyword_init: true
22
+ ) do
23
+ def initialize(
24
+ settings_header_table_size: 4096,
25
+ settings_enable_push: 1,
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
30
+ )
31
+ super
32
+ end
33
+
34
+ def each_setting
35
+ each_pair do |k, v|
36
+ next if v == SPEC_DEFAULT_CONNECTION_SETTINGS[k]
37
+
38
+ yield k, v
39
+ end
40
+ end
41
+ end
42
+ end