http-2 1.0.0 → 1.0.2

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: c5b015395ca278b22ea3af7f2c0c149071ddb1ad4cd5db5c52329554de87fe27
4
- data.tar.gz: 3f7a6d671bd23efe5b60f16dc39294b6cd7a45d6087ff1be70f0624f682895a5
3
+ metadata.gz: c96822381e28e32127984c2dbb7b25b3ad5dd87d69283a752378a9f8778c72ae
4
+ data.tar.gz: beb68a1eb2670d1d0b742224b5ad65bd2bc299656433b120ff31299a481297ce
5
5
  SHA512:
6
- metadata.gz: ce8dff286d18b7607a338572b9b85ab2ff88eb8a28b8de8a6139344d0cc2f3ebc700ca9affc8398526b88fb95b7fe5da90209c5c6fbdff155c319f9e8691062b
7
- data.tar.gz: b7f920fc81747d811855eec66a8f63e5a18d5a8ca49d81efe8f6567c6c72a1275c4846219a1532855834717453a7e0d914d3138296fb24876f5af3d9c3151772
6
+ metadata.gz: 173e0163e2b05ff3eefdfcc420f8c3913c97702873964ef6c653f0329726443c4c10d73d7ffff31583b06567ef12c8f6545eae0f3d32c52d9b13d98322c09e32
7
+ data.tar.gz: 43b38b2bdd3ed6ac90382b98c96b03fd76edefbbf4a385c67c3c8db67e6f755382f09c8a3af848a1d8ed55335ad2925d05793ad71c16c5bb831b07f86e0228be
@@ -52,8 +52,7 @@ module HTTP2
52
52
  include FlowBuffer
53
53
  include Emitter
54
54
  include Error
55
-
56
- using StringExtensions
55
+ include BufferUtils
57
56
 
58
57
  # Connection state (:new, :closed).
59
58
  attr_reader :state
@@ -202,7 +201,7 @@ module HTTP2
202
201
  raise HandshakeError unless CONNECTION_PREFACE_MAGIC.start_with? @recv_buffer
203
202
 
204
203
  return # maybe next time
205
- elsif @recv_buffer.read(24) == CONNECTION_PREFACE_MAGIC
204
+ elsif read_str(@recv_buffer, 24) == CONNECTION_PREFACE_MAGIC
206
205
  # MAGIC is OK. Send our settings
207
206
  @state = :waiting_connection_preface
208
207
  payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
@@ -294,13 +293,13 @@ module HTTP2
294
293
  if stream.nil?
295
294
  verify_pseudo_headers(frame)
296
295
 
296
+ verify_stream_order(frame[:stream])
297
297
  stream = activate_stream(
298
298
  id: frame[:stream],
299
299
  weight: frame[:weight] || DEFAULT_WEIGHT,
300
300
  dependency: frame[:dependency] || 0,
301
301
  exclusive: frame[:exclusive] || false
302
302
  )
303
- verify_stream_order(stream.id)
304
303
  emit(:stream, stream)
305
304
  end
306
305
 
@@ -354,8 +353,8 @@ module HTTP2
354
353
  end
355
354
 
356
355
  _verify_pseudo_headers(frame, REQUEST_MANDATORY_HEADERS)
356
+ verify_stream_order(pid)
357
357
  stream = activate_stream(id: pid, parent: parent)
358
- verify_stream_order(stream.id)
359
358
  emit(:promise, stream)
360
359
  stream << frame
361
360
  else
@@ -391,6 +390,13 @@ module HTTP2
391
390
  stream = @streams_recently_closed[frame[:stream]]
392
391
  connection_error(:protocol_error, msg: "sent window update on idle stream") unless stream
393
392
  process_window_update(frame: frame, encode: true)
393
+ # Endpoints MUST ignore
394
+ # WINDOW_UPDATE or RST_STREAM frames received in this state (closed), though
395
+ # endpoints MAY choose to treat frames that arrive a significant
396
+ # time after sending END_STREAM as a connection error.
397
+ when :rst_stream
398
+ stream = @streams_recently_closed[frame[:stream]]
399
+ connection_error(:protocol_error, msg: "sent window update on idle stream") unless stream
394
400
  else
395
401
  # An endpoint that receives an unexpected stream identifier
396
402
  # MUST respond with a connection error of type PROTOCOL_ERROR.
@@ -744,6 +750,8 @@ module HTTP2
744
750
  stream = Stream.new(connection: self, id: id, **args)
745
751
 
746
752
  stream.once(:close) do
753
+ @streams.delete(id)
754
+
747
755
  # Store a reference to the closed stream, such that we can respond
748
756
  # to any in-flight frames while close is registered on both sides.
749
757
  # References to such streams will be purged whenever another stream
@@ -767,7 +775,7 @@ module HTTP2
767
775
  def verify_stream_order(id)
768
776
  return unless id.odd?
769
777
 
770
- connection_error(msg: "Stream ID smaller than previous") if @last_stream_id > id
778
+ connection_error(msg: "Stream ID smaller than previous") if @last_stream_id >= id
771
779
  @last_stream_id = id
772
780
  end
773
781
 
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2
4
- module StringExtensions
5
- refine String do
6
- def read(n)
7
- return "".b if n == 0
4
+ module BufferUtils
5
+ def read_str(str, n)
6
+ return "".b if n == 0
8
7
 
9
- chunk = byteslice(0..n - 1)
10
- remaining = byteslice(n..-1)
11
- remaining ? replace(remaining) : clear
12
- chunk
13
- end
8
+ chunk = str.byteslice(0..n - 1)
9
+ remaining = str.byteslice(n..-1)
10
+ remaining ? str.replace(remaining) : str.clear
11
+ chunk
12
+ end
14
13
 
15
- def read_uint32
16
- read(4).unpack1("N")
17
- end
14
+ def read_uint32(str)
15
+ read_str(str, 4).unpack1("N")
16
+ end
18
17
 
19
- def shift_byte
20
- read(1).ord
21
- end
18
+ def shift_byte(str)
19
+ read_str(str, 1).ord
22
20
  end
23
21
  end
24
22
 
data/lib/http/2/framer.rb CHANGED
@@ -4,10 +4,9 @@ module HTTP2
4
4
  # Performs encoding, decoding, and validation of binary HTTP/2 frames.
5
5
  #
6
6
  class Framer
7
- using StringExtensions
8
-
9
7
  include Error
10
8
  include PackingExtensions
9
+ include BufferUtils
11
10
 
12
11
  # Default value of max frame size (16384 bytes)
13
12
  DEFAULT_MAX_FRAME_SIZE = 2 << 13
@@ -165,7 +164,7 @@ module HTTP2
165
164
  frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
166
165
  if frame[:type]
167
166
  frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
168
- acc << name if (flags & (1 << pos)) > 0
167
+ acc << name if flags.anybits?((1 << pos))
169
168
  end
170
169
  end
171
170
 
@@ -344,8 +343,8 @@ module HTTP2
344
343
 
345
344
  raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
346
345
 
347
- buf.read(9)
348
- payload = buf.read(frame[:length])
346
+ read_str(buf, 9)
347
+ payload = read_str(buf, frame[:length])
349
348
 
350
349
  # Implementations MUST discard frames
351
350
  # that have unknown or unsupported types.
@@ -357,7 +356,7 @@ module HTTP2
357
356
  if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
358
357
  padded = frame[:flags].include?(:padded)
359
358
  if padded
360
- padlen = payload.read(1).unpack1(UINT8)
359
+ padlen = read_str(payload, 1).unpack1(UINT8)
361
360
  frame[:padding] = padlen + 1
362
361
  raise ProtocolError, "padding too long" if padlen > payload.bytesize
363
362
 
@@ -369,30 +368,30 @@ module HTTP2
369
368
 
370
369
  case frame[:type]
371
370
  when :data, :ping, :continuation
372
- frame[:payload] = payload.read(frame[:length])
371
+ frame[:payload] = read_str(payload, frame[:length])
373
372
  when :headers
374
373
  if frame[:flags].include? :priority
375
- e_sd = payload.read_uint32
374
+ e_sd = read_uint32(payload)
376
375
  frame[:dependency] = e_sd & RBIT
377
- frame[:exclusive] = (e_sd & EBIT) != 0
376
+ frame[:exclusive] = e_sd.anybits?(EBIT)
378
377
  weight = payload.byteslice(0, 1).ord + 1
379
378
  frame[:weight] = weight
380
379
  payload = payload.byteslice(1..-1)
381
380
  end
382
- frame[:payload] = payload.read(frame[:length])
381
+ frame[:payload] = read_str(payload, frame[:length])
383
382
  when :priority
384
383
  raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
385
384
 
386
- e_sd = payload.read_uint32
385
+ e_sd = read_uint32(payload)
387
386
  frame[:dependency] = e_sd & RBIT
388
- frame[:exclusive] = (e_sd & EBIT) != 0
387
+ frame[:exclusive] = e_sd.anybits?(EBIT)
389
388
  weight = payload.byteslice(0, 1).ord + 1
390
389
  frame[:weight] = weight
391
390
  payload = payload.byteslice(1..-1)
392
391
  when :rst_stream
393
392
  raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
394
393
 
395
- frame[:error] = unpack_error payload.read_uint32
394
+ frame[:error] = unpack_error read_uint32(payload)
396
395
 
397
396
  when :settings
398
397
  # NOTE: frame[:length] might not match the number of frame[:payload]
@@ -403,8 +402,8 @@ module HTTP2
403
402
  raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
404
403
 
405
404
  (frame[:length] / 6).times do
406
- id = payload.read(2).unpack1(UINT16)
407
- val = payload.read_uint32
405
+ id = read_str(payload, 2).unpack1(UINT16)
406
+ val = read_uint32(payload)
408
407
 
409
408
  # Unsupported or unrecognized settings MUST be ignored.
410
409
  # Here we send it along.
@@ -412,39 +411,39 @@ module HTTP2
412
411
  frame[:payload] << [name, val] if name
413
412
  end
414
413
  when :push_promise
415
- frame[:promise_stream] = payload.read_uint32 & RBIT
416
- frame[:payload] = payload.read(frame[:length])
414
+ frame[:promise_stream] = read_uint32(payload) & RBIT
415
+ frame[:payload] = read_str(payload, frame[:length])
417
416
  when :goaway
418
- frame[:last_stream] = payload.read_uint32 & RBIT
419
- frame[:error] = unpack_error payload.read_uint32
417
+ frame[:last_stream] = read_uint32(payload) & RBIT
418
+ frame[:error] = unpack_error read_uint32(payload)
420
419
 
421
420
  size = frame[:length] - 8 # for last_stream and error
422
- frame[:payload] = payload.read(size) if size > 0
421
+ frame[:payload] = read_str(payload, size) if size > 0
423
422
  when :window_update
424
423
  if frame[:length] % 4 != 0
425
424
  raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)"
426
425
  end
427
426
 
428
- frame[:increment] = payload.read_uint32 & RBIT
427
+ frame[:increment] = read_uint32(payload) & RBIT
429
428
  when :altsvc
430
- frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
429
+ frame[:max_age], frame[:port] = read_str(payload, 6).unpack(UINT32 + UINT16)
431
430
 
432
431
  len = payload.byteslice(0, 1).ord
433
432
  payload = payload.byteslice(1..-1)
434
- frame[:proto] = payload.read(len) if len > 0
433
+ frame[:proto] = read_str(payload, len) if len > 0
435
434
 
436
435
  len = payload.byteslice(0, 1).ord
437
436
  payload = payload.byteslice(1..-1)
438
- frame[:host] = payload.read(len) if len > 0
437
+ frame[:host] = read_str(payload, len) if len > 0
439
438
 
440
- frame[:origin] = payload.read(payload.size) unless payload.empty?
439
+ frame[:origin] = read_str(payload, payload.size) unless payload.empty?
441
440
 
442
441
  when :origin
443
442
  origins = []
444
443
 
445
444
  until payload.empty?
446
- len = payload.read(2).unpack1(UINT16)
447
- origins << payload.read(len)
445
+ len = read_str(payload, 2).unpack1(UINT16)
446
+ origins << read_str(payload, len)
448
447
  end
449
448
 
450
449
  frame[:payload] = origins
@@ -2,7 +2,6 @@
2
2
 
3
3
  module HTTP2
4
4
  module Header
5
- using StringExtensions
6
5
  # Responsible for decoding received headers and maintaining compression
7
6
  # context of the opposing peer. Decompressor must be initialized with
8
7
  # appropriate starting context based on local role: client or server.
@@ -12,6 +11,7 @@ module HTTP2
12
11
  # client_role = Decompressor.new(:response)
13
12
  class Decompressor
14
13
  include Error
14
+ include BufferUtils
15
15
 
16
16
  # @param options [Hash] decoding options. Only :table_size is effective.
17
17
  def initialize(options = {})
@@ -31,15 +31,15 @@ module HTTP2
31
31
  # @return [Integer]
32
32
  def integer(buf, n)
33
33
  limit = (2**n) - 1
34
- i = n.zero? ? 0 : (buf.shift_byte & limit)
34
+ i = n.zero? ? 0 : (shift_byte(buf) & limit)
35
35
 
36
36
  m = 0
37
37
  if i == limit
38
- while (byte = buf.shift_byte)
38
+ while (byte = shift_byte(buf))
39
39
  i += ((byte & 127) << m)
40
40
  m += 7
41
41
 
42
- break if (byte & 128).zero?
42
+ break if byte.nobits?(128)
43
43
  end
44
44
  end
45
45
 
@@ -54,9 +54,9 @@ module HTTP2
54
54
  def string(buf)
55
55
  raise CompressionError, "invalid header block fragment" if buf.empty?
56
56
 
57
- huffman = (buf.getbyte(0) & 0x80) == 0x80
57
+ huffman = buf.getbyte(0).allbits?(0x80)
58
58
  len = integer(buf, 7)
59
- str = buf.read(len)
59
+ str = read_str(buf, len)
60
60
  raise CompressionError, "string too short" unless str.bytesize == len
61
61
 
62
62
  str = Huffman.new.decode(str) if huffman
@@ -75,12 +75,14 @@ module HTTP2
75
75
  ["vary", ""],
76
76
  ["via", ""],
77
77
  ["www-authenticate", ""]
78
- ].each { |pair| pair.each(&:freeze).freeze }.freeze
79
-
80
- STATIC_TABLE_BY_FIELD = STATIC_TABLE
81
- .each_with_object({})
82
- .with_index { |((field, value), hs), idx| (hs[field] ||= []) << [idx, value] }
83
- .each { |pair| pair.each(&:freeze).freeze }.freeze
78
+ ].each(&:freeze).freeze
79
+
80
+ STATIC_TABLE_BY_FIELD =
81
+ STATIC_TABLE
82
+ .each_with_object({})
83
+ .with_index { |((field, value), hs), idx| (hs[field] ||= []) << [idx, value].freeze }
84
+ .each_value(&:freeze)
85
+ .freeze
84
86
 
85
87
  STATIC_TABLE_SIZE = STATIC_TABLE.size
86
88
 
@@ -10,8 +10,6 @@ module HTTP2
10
10
  module Header
11
11
  # Huffman encoder/decoder
12
12
  class Huffman
13
- using StringExtensions
14
-
15
13
  include Error
16
14
  include PackingExtensions
17
15
 
@@ -268,7 +268,7 @@ module HTTP2
268
268
  [[28, 29], [28, 5], [29, 29], [29, 5], [30, 29], [30, 5], [31, 29], [31, 5], [127, 29], [127, 5], [220, 29], [220, 5], [249, 29], [249, 5], [nil, 254], [nil, 255]],
269
269
  [[10, 17], [10, 18], [10, 19], [10, 20], [10, 21], [10, 22], [10, 23], [10, 7], [13, 17], [13, 18], [13, 19], [13, 20], [13, 21], [13, 22], [13, 23], [13, 7]],
270
270
  [[22, 17], [22, 18], [22, 19], [22, 20], [22, 21], [22, 22], [22, 23], [22, 7], [256, 17], [256, 18], [256, 19], [256, 20], [256, 21], [256, 22], [256, 23], [256, 7]],
271
- ].each { |arr| arr.each { |subarr| subarr.each(&:freeze) }.freeze }.freeze
271
+ ].each { |arr| arr.each { |subarr| subarr.freeze }.freeze }.freeze
272
272
  end
273
273
  end
274
274
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.2"
5
5
  end
data/sig/connection.rbs CHANGED
@@ -2,6 +2,7 @@ module HTTP2
2
2
  class Connection
3
3
  include FlowBuffer
4
4
  include Emitter
5
+ include BufferUtils
5
6
 
6
7
  REQUEST_MANDATORY_HEADERS: Array[String]
7
8
  RESPONSE_MANDATORY_HEADERS: Array[String]
data/sig/extensions.rbs CHANGED
@@ -1,4 +1,12 @@
1
1
  module HTTP2
2
+ module BufferUtils
3
+ def read_str: (String str, Integer n) -> String
4
+
5
+ def read_uint32: (String str) -> Integer
6
+
7
+ def shift_byte: (String str) -> Integer
8
+ end
9
+
2
10
  module PackingExtensions
3
11
  def pack: (Array[Integer] array_to_pack, String template, buffer: String, ?offset: Integer) -> String
4
12
  end
data/sig/framer.rbs CHANGED
@@ -1,6 +1,7 @@
1
1
  module HTTP2
2
2
  class Framer
3
3
  include PackingExtensions
4
+ include BufferUtils
4
5
 
5
6
  DEFAULT_MAX_FRAME_SIZE: Integer
6
7
 
@@ -1,6 +1,8 @@
1
1
  module HTTP2
2
2
  module Header
3
3
  class Decompressor
4
+ include BufferUtils
5
+
4
6
  @cc: EncodingContext
5
7
 
6
8
  def table_size=: (Integer) -> void
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.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-06-28 00:00:00.000000000 Z
13
+ date: 2024-11-05 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Pure-ruby HTTP 2.0 protocol implementation
16
16
  email: