http-2 0.11.0 → 0.12.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -2
  3. data/lib/http/2/buffer.rb +6 -4
  4. data/lib/http/2/client.rb +5 -1
  5. data/lib/http/2/compressor.rb +42 -34
  6. data/lib/http/2/connection.rb +72 -86
  7. data/lib/http/2/emitter.rb +4 -1
  8. data/lib/http/2/error.rb +2 -0
  9. data/lib/http/2/flow_buffer.rb +8 -3
  10. data/lib/http/2/framer.rb +83 -94
  11. data/lib/http/2/huffman.rb +19 -17
  12. data/lib/http/2/server.rb +9 -7
  13. data/lib/http/2/stream.rb +48 -48
  14. data/lib/http/2/version.rb +3 -1
  15. data/lib/http/2.rb +2 -0
  16. metadata +7 -60
  17. data/.autotest +0 -20
  18. data/.coveralls.yml +0 -1
  19. data/.gitignore +0 -20
  20. data/.gitmodules +0 -3
  21. data/.rspec +0 -5
  22. data/.rubocop.yml +0 -93
  23. data/.rubocop_todo.yml +0 -131
  24. data/.travis.yml +0 -17
  25. data/Gemfile +0 -16
  26. data/Guardfile +0 -18
  27. data/Guardfile.h2spec +0 -12
  28. data/Rakefile +0 -49
  29. data/example/Gemfile +0 -3
  30. data/example/README.md +0 -44
  31. data/example/client.rb +0 -122
  32. data/example/helper.rb +0 -19
  33. data/example/keys/server.crt +0 -20
  34. data/example/keys/server.key +0 -27
  35. data/example/server.rb +0 -139
  36. data/example/upgrade_client.rb +0 -153
  37. data/example/upgrade_server.rb +0 -203
  38. data/http-2.gemspec +0 -22
  39. data/lib/tasks/generate_huffman_table.rb +0 -166
  40. data/spec/buffer_spec.rb +0 -28
  41. data/spec/client_spec.rb +0 -188
  42. data/spec/compressor_spec.rb +0 -666
  43. data/spec/connection_spec.rb +0 -681
  44. data/spec/emitter_spec.rb +0 -54
  45. data/spec/framer_spec.rb +0 -487
  46. data/spec/h2spec/h2spec.darwin +0 -0
  47. data/spec/h2spec/output/non_secure.txt +0 -317
  48. data/spec/helper.rb +0 -147
  49. data/spec/hpack_test_spec.rb +0 -84
  50. data/spec/huffman_spec.rb +0 -68
  51. data/spec/server_spec.rb +0 -52
  52. data/spec/stream_spec.rb +0 -878
  53. data/spec/support/deep_dup.rb +0 -55
  54. data/spec/support/duplicable.rb +0 -98
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5617138c0740b55546b79e29a06978a9fcb9aae208ccb2f695c32939eca5f88a
4
- data.tar.gz: 193880583c0a80aa569f433977af1798c98be25a86fb782a410e16eb38d5116c
3
+ metadata.gz: d1788e5559bb1f36b98896322d1c846ef73b0fba4a7a45b524845735deacb9aa
4
+ data.tar.gz: 62ea957139ea576aa4553303f6db141cd9297a8ce75624553e79a90a69f74717
5
5
  SHA512:
6
- metadata.gz: 46fde2633ab7d074136a32e7ae8cdf90ecd037f3a95bfeabab9557d72a13487a47a3e298339bc74df4d89c6b802537b9c35a1f7709fd6cf1f4fde44eecbe468e
7
- data.tar.gz: 6eadeb79f3bbf6fa1911933e2dc399bfe50ae08c598cb72278a37e0c42102bf27ccbed77b7acb5e7f1e85a55bd6967e63a8950fa3184fb7c9eef750c8a9e123c
6
+ metadata.gz: fb252eeff936187cf2ebebdf93e69fb6f82dba50a0fece880f40a8235b790165ad5d3f167b0c3948adf040388fc7cd49bc1bd1a24b754447c83dd2020be78eaa
7
+ data.tar.gz: 0657b1b61f526041167473faf29e50a0cbf4bbfcbac80a458a98a205ab5a01f409da7d62010e5620e5e5fe6cb6e7d1bd5a2ae2a831fcac7da1c1ab2192511c1e
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # HTTP-2
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/http-2.svg)](http://rubygems.org/gems/http-2)
4
- [![Build Status](https://travis-ci.org/igrigorik/http-2.svg?branch=master)](https://travis-ci.org/igrigorik/http-2)
5
4
  [![Coverage Status](https://coveralls.io/repos/igrigorik/http-2/badge.svg)](https://coveralls.io/r/igrigorik/http-2)
6
- [![Analytics](https://ga-beacon.appspot.com/UA-71196-10/http-2/readme)](https://github.com/igrigorik/ga-beacon)
7
5
 
8
6
  Pure Ruby, framework and transport agnostic, implementation of HTTP/2 protocol and HPACK header compression with support for:
9
7
 
data/lib/http/2/buffer.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module HTTP2
@@ -6,11 +8,11 @@ module HTTP2
6
8
  class Buffer
7
9
  extend Forwardable
8
10
 
9
- def_delegators :@buffer, :ord, :encoding, :setbyte, :unpack,
11
+ def_delegators :@buffer, :ord, :encoding, :setbyte, :unpack, :unpack1,
10
12
  :size, :each_byte, :to_str, :to_s, :length, :inspect,
11
13
  :[], :[]=, :empty?, :bytesize, :include?
12
14
 
13
- UINT32 = 'N'.freeze
15
+ UINT32 = 'N'
14
16
  private_constant :UINT32
15
17
 
16
18
  # Forces binary encoding on the string
@@ -59,12 +61,12 @@ module HTTP2
59
61
  # Slice unsigned 32-bit integer from buffer.
60
62
  # @return [Integer]
61
63
  def read_uint32
62
- read(4).unpack(UINT32).first
64
+ read(4).unpack1(UINT32)
63
65
  end
64
66
 
65
67
  # Ensures that data that is added is binary encoded as well,
66
68
  # otherwise this could lead to the Buffer instance changing its encoding.
67
- [:<<, :prepend].each do |mutating_method|
69
+ %i[<< prepend].each do |mutating_method|
68
70
  define_method(mutating_method) do |string|
69
71
  string = string.dup if string.frozen?
70
72
  @buffer.send mutating_method, string.force_encoding(Encoding::BINARY)
data/lib/http/2/client.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # HTTP 2.0 client connection class that implements appropriate header
3
5
  # compression / decompression algorithms and stream management logic.
@@ -45,7 +47,8 @@ module HTTP2
45
47
 
46
48
  # sends the preface and initializes the first stream in half-closed state
47
49
  def upgrade
48
- fail ProtocolError unless @stream_id == 1
50
+ raise ProtocolError unless @stream_id == 1
51
+
49
52
  send_connection_preface
50
53
  new_stream(state: :half_closed_local)
51
54
  end
@@ -53,6 +56,7 @@ module HTTP2
53
56
  # Emit the connection preface if not yet
54
57
  def send_connection_preface
55
58
  return unless @state == :waiting_connection_preface
59
+
56
60
  @state = :connected
57
61
  emit(:frame, CONNECTION_PREFACE_MAGIC)
58
62
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Implementation of header compression for HTTP 2.0 (HPACK) format adapted
3
5
  # to efficiently represent HTTP headers in the context of HTTP 2.0.
@@ -74,7 +76,7 @@ module HTTP2
74
76
  ['user-agent', ''],
75
77
  ['vary', ''],
76
78
  ['via', ''],
77
- ['www-authenticate', ''],
79
+ ['www-authenticate', '']
78
80
  ].each { |pair| pair.each(&:freeze).freeze }.freeze
79
81
 
80
82
  # Current table of header key-value pairs.
@@ -96,9 +98,9 @@ module HTTP2
96
98
  # :index Symbol :all, :static, :never
97
99
  def initialize(**options)
98
100
  default_options = {
99
- huffman: :shorter,
100
- index: :all,
101
- table_size: 4096,
101
+ huffman: :shorter,
102
+ index: :all,
103
+ table_size: 4096
102
104
  }
103
105
  @table = []
104
106
  @options = default_options.merge(options)
@@ -112,7 +114,7 @@ module HTTP2
112
114
  t = @table
113
115
  l = @limit
114
116
  other.instance_eval do
115
- @table = t.dup # shallow copy
117
+ @table = t.dup # shallow copy
116
118
  @limit = l
117
119
  end
118
120
  other
@@ -131,7 +133,8 @@ module HTTP2
131
133
  def dereference(index)
132
134
  # NOTE: index is zero-based in this module.
133
135
  value = STATIC_TABLE[index] || @table[index - STATIC_TABLE.size]
134
- fail CompressionError, 'Index too large' unless value
136
+ raise CompressionError, 'Index too large' unless value
137
+
135
138
  value
136
139
  end
137
140
 
@@ -146,9 +149,8 @@ module HTTP2
146
149
 
147
150
  case cmd[:type]
148
151
  when :changetablesize
149
- if cmd[:value] > @limit
150
- fail CompressionError, 'dynamic table size update exceed limit'
151
- end
152
+ raise CompressionError, 'dynamic table size update exceed limit' if cmd[:value] > @limit
153
+
152
154
  self.table_size = cmd[:value]
153
155
 
154
156
  when :indexed
@@ -186,7 +188,7 @@ module HTTP2
186
188
  add_to_table(emit) if cmd[:type] == :incremental
187
189
 
188
190
  else
189
- fail CompressionError, "Invalid type: #{cmd[:type]}"
191
+ raise CompressionError, "Invalid type: #{cmd[:type]}"
190
192
  end
191
193
 
192
194
  emit
@@ -202,7 +204,7 @@ module HTTP2
202
204
  def encode(headers)
203
205
  commands = []
204
206
  # Literals commands are marked with :noindex when index is not used
205
- noindex = [:static, :never].include?(@options[:index])
207
+ noindex = %i[static never].include?(@options[:index])
206
208
  headers.each do |field, value|
207
209
  # Literal header names MUST be translated to lowercase before
208
210
  # encoding and transmission.
@@ -232,7 +234,7 @@ module HTTP2
232
234
  exact = nil
233
235
  name_only = nil
234
236
 
235
- if [:all, :static].include?(@options[:index])
237
+ if %i[all static].include?(@options[:index])
236
238
  STATIC_TABLE.each_index do |i|
237
239
  if STATIC_TABLE[i] == header
238
240
  exact ||= i
@@ -284,6 +286,7 @@ module HTTP2
284
286
  # @param cmd [Array] +[name, value]+
285
287
  def add_to_table(cmd)
286
288
  return unless size_check(cmd)
289
+
287
290
  @table.unshift(cmd)
288
291
  end
289
292
 
@@ -309,11 +312,11 @@ module HTTP2
309
312
 
310
313
  # Header representation as defined by the spec.
311
314
  HEADREP = {
312
- indexed: { prefix: 7, pattern: 0x80 },
313
- incremental: { prefix: 6, pattern: 0x40 },
314
- noindex: { prefix: 4, pattern: 0x00 },
315
+ indexed: { prefix: 7, pattern: 0x80 },
316
+ incremental: { prefix: 6, pattern: 0x40 },
317
+ noindex: { prefix: 4, pattern: 0x00 },
315
318
  neverindexed: { prefix: 4, pattern: 0x10 },
316
- changetablesize: { prefix: 5, pattern: 0x20 },
319
+ changetablesize: { prefix: 5, pattern: 0x20 }
317
320
  }.each_value(&:freeze).freeze
318
321
 
319
322
  # Predefined options set for Compressor
@@ -330,6 +333,7 @@ module HTTP2
330
333
  # Responsible for encoding header key-value pairs using HPACK algorithm.
331
334
  class Compressor
332
335
  # @param options [Hash] encoding options
336
+ # @see EncodingContext#initialize
333
337
  def initialize(**options)
334
338
  @cc = EncodingContext.new(**options)
335
339
  end
@@ -356,14 +360,14 @@ module HTTP2
356
360
  # @param n [Integer] number of available bits
357
361
  # @return [String] binary string
358
362
  def integer(i, n)
359
- limit = 2**n - 1
363
+ limit = (2**n) - 1
360
364
  return [i].pack('C') if i < limit
361
365
 
362
366
  bytes = []
363
367
  bytes.push limit unless n.zero?
364
368
 
365
369
  i -= limit
366
- while (i >= 128)
370
+ while i >= 128
367
371
  bytes.push((i % 128) + 128)
368
372
  i /= 128
369
373
  end
@@ -393,7 +397,8 @@ module HTTP2
393
397
  # @param str [String]
394
398
  # @return [String] binary string
395
399
  def string(str)
396
- plain, huffman = nil, nil
400
+ plain = nil
401
+ huffman = nil
397
402
  unless @cc.options[:huffman] == :always
398
403
  plain = integer(str.bytesize, 7) << str.dup.force_encoding(Encoding::BINARY)
399
404
  end
@@ -463,12 +468,9 @@ module HTTP2
463
468
  # Responsible for decoding received headers and maintaining compression
464
469
  # context of the opposing peer. Decompressor must be initialized with
465
470
  # appropriate starting context based on local role: client or server.
466
- #
467
- # @example
468
- # server_role = Decompressor.new(:request)
469
- # client_role = Decompressor.new(:response)
470
471
  class Decompressor
471
472
  # @param options [Hash] decoding options. Only :table_size is effective.
473
+ # @see EncodingContext#initialize
472
474
  def initialize(**options)
473
475
  @cc = EncodingContext.new(**options)
474
476
  end
@@ -485,16 +487,18 @@ module HTTP2
485
487
  # @param n [Integer] number of available bits
486
488
  # @return [Integer]
487
489
  def integer(buf, n)
488
- limit = 2**n - 1
489
- i = !n.zero? ? (buf.getbyte & limit) : 0
490
+ limit = (2**n) - 1
491
+ i = n.zero? ? 0 : (buf.getbyte & limit)
490
492
 
491
493
  m = 0
492
- while (byte = buf.getbyte)
493
- i += ((byte & 127) << m)
494
- m += 7
494
+ if i == limit
495
+ while (byte = buf.getbyte)
496
+ i += ((byte & 127) << m)
497
+ m += 7
495
498
 
496
- break if (byte & 128).zero?
497
- end if (i == limit)
499
+ break if (byte & 128).zero?
500
+ end
501
+ end
498
502
 
499
503
  i
500
504
  end
@@ -508,7 +512,8 @@ module HTTP2
508
512
  huffman = (buf.readbyte(0) & 0x80) == 0x80
509
513
  len = integer(buf, 7)
510
514
  str = buf.read(len)
511
- fail CompressionError, 'string too short' unless str.bytesize == len
515
+ raise CompressionError, 'string too short' unless str.bytesize == len
516
+
512
517
  str = Huffman.new.decode(Buffer.new(str)) if huffman
513
518
  str.force_encoding(Encoding::UTF_8)
514
519
  end
@@ -526,13 +531,14 @@ module HTTP2
526
531
  mask == desc[:pattern]
527
532
  end
528
533
 
529
- fail CompressionError unless header[:type]
534
+ raise CompressionError unless header[:type]
530
535
 
531
536
  header[:name] = integer(buf, type[:prefix])
532
537
 
533
538
  case header[:type]
534
539
  when :indexed
535
- fail CompressionError if (header[:name]).zero?
540
+ raise CompressionError if (header[:name]).zero?
541
+
536
542
  header[:name] -= 1
537
543
  when :changetablesize
538
544
  header[:value] = header[:name]
@@ -558,10 +564,12 @@ module HTTP2
558
564
  until buf.empty?
559
565
  next_header = @cc.process(header(buf))
560
566
  next if next_header.nil?
567
+
561
568
  is_pseudo_header = next_header.first.start_with? ':'
562
569
  if !decoding_pseudo_headers && is_pseudo_header
563
- fail ProtocolError, 'one or more pseudo headers encountered after regular headers'
570
+ raise ProtocolError, 'one or more pseudo headers encountered after regular headers'
564
571
  end
572
+
565
573
  decoding_pseudo_headers = is_pseudo_header
566
574
  list << next_header
567
575
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Default connection and stream flow control window (64KB).
3
5
  DEFAULT_FLOW_WINDOW = 65_535
@@ -10,28 +12,28 @@ module HTTP2
10
12
 
11
13
  # Default values for SETTINGS frame, as defined by the spec.
12
14
  SPEC_DEFAULT_CONNECTION_SETTINGS = {
13
- settings_header_table_size: 4096,
14
- settings_enable_push: 1, # enabled for servers
15
- settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
16
- settings_initial_window_size: 65_535,
17
- settings_max_frame_size: 16_384,
18
- settings_max_header_list_size: 2**31 - 1, # unlimited
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**31) - 1 # unlimited
19
21
  }.freeze
20
22
 
21
23
  DEFAULT_CONNECTION_SETTINGS = {
22
- settings_header_table_size: 4096,
23
- settings_enable_push: 1, # enabled for servers
24
- settings_max_concurrent_streams: 100,
25
- settings_initial_window_size: 65_535,
26
- settings_max_frame_size: 16_384,
27
- settings_max_header_list_size: 2**31 - 1, # unlimited
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**31) - 1 # unlimited
28
30
  }.freeze
29
31
 
30
32
  # Default stream priority (lower values are higher priority).
31
- DEFAULT_WEIGHT = 16
33
+ DEFAULT_WEIGHT = 16
32
34
 
33
35
  # Default connection "fast-fail" preamble string as defined by the spec.
34
- CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
36
+ CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
35
37
 
36
38
  # Time to hold recently closed streams until purge (seconds)
37
39
  RECENTLY_CLOSED_STREAMS_TTL = 15
@@ -43,7 +45,7 @@ module HTTP2
43
45
  # Note that this class should not be used directly. Instead, you want to
44
46
  # use either Client or Server class to drive the HTTP 2.0 exchange.
45
47
  #
46
- # rubocop:disable ClassLength
48
+ # rubocop:disable Metrics/ClassLength
47
49
  class Connection
48
50
  include FlowBuffer
49
51
  include Emitter
@@ -55,12 +57,11 @@ module HTTP2
55
57
  # Size of current connection flow control window (by default, set to
56
58
  # infinity, but is automatically updated on receipt of peer settings).
57
59
  attr_reader :local_window
58
- attr_reader :remote_window
60
+ attr_reader :remote_window, :remote_settings
59
61
  alias window local_window
60
62
 
61
63
  # Current settings value for local and peer
62
64
  attr_reader :local_settings
63
- attr_reader :remote_settings
64
65
 
65
66
  # Pending settings value
66
67
  # Sent but not ack'ed settings
@@ -110,8 +111,8 @@ module HTTP2
110
111
  # @param window [Integer]
111
112
  # @param parent [Stream]
112
113
  def new_stream(**args)
113
- fail ConnectionClosed if @state == :closed
114
- fail StreamLimitExceeded if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams]
114
+ raise ConnectionClosed if @state == :closed
115
+ raise StreamLimitExceeded if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams]
115
116
 
116
117
  stream = activate_stream(id: @stream_id, **args)
117
118
  @stream_id += 2
@@ -140,10 +141,10 @@ module HTTP2
140
141
  # @param payload [String]
141
142
  def goaway(error = :no_error, payload = nil)
142
143
  last_stream = if (max = @streams.max)
143
- max.first
144
- else
145
- 0
146
- end
144
+ max.first
145
+ else
146
+ 0
147
+ end
147
148
 
148
149
  send(type: :goaway, last_stream: last_stream,
149
150
  error: error, payload: payload)
@@ -188,18 +189,17 @@ module HTTP2
188
189
  # SETTINGS frame. Server connection header is SETTINGS frame only.
189
190
  if @state == :waiting_magic
190
191
  if @recv_buffer.size < 24
191
- if !CONNECTION_PREFACE_MAGIC.start_with? @recv_buffer
192
- fail HandshakeError
193
- else
194
- return # maybe next time
195
- end
192
+ raise HandshakeError unless CONNECTION_PREFACE_MAGIC.start_with? @recv_buffer
193
+
194
+ return # maybe next time
195
+
196
196
  elsif @recv_buffer.read(24) == CONNECTION_PREFACE_MAGIC
197
197
  # MAGIC is OK. Send our settings
198
198
  @state = :waiting_connection_preface
199
199
  payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
200
200
  settings(payload)
201
201
  else
202
- fail HandshakeError
202
+ raise HandshakeError
203
203
  end
204
204
  end
205
205
 
@@ -209,9 +209,7 @@ module HTTP2
209
209
  # Header blocks MUST be transmitted as a contiguous sequence of frames
210
210
  # with no interleaved frames of any other type, or from any other stream.
211
211
  unless @continuation.empty?
212
- unless frame[:type] == :continuation && frame[:stream] == @continuation.first[:stream]
213
- connection_error
214
- end
212
+ connection_error unless frame[:type] == :continuation && frame[:stream] == @continuation.first[:stream]
215
213
 
216
214
  @continuation << frame
217
215
  return unless frame[:flags].include? :end_headers
@@ -238,7 +236,7 @@ module HTTP2
238
236
  when :headers
239
237
  # When server receives even-numbered stream identifier,
240
238
  # the endpoint MUST respond with a connection error of type PROTOCOL_ERROR.
241
- connection_error if frame[:stream].even? && self.is_a?(Server)
239
+ connection_error if frame[:stream].even? && is_a?(Server)
242
240
 
243
241
  # The last frame in a sequence of HEADERS/CONTINUATION
244
242
  # frames MUST have the END_HEADERS flag set.
@@ -258,10 +256,10 @@ module HTTP2
258
256
  stream = @streams[frame[:stream]]
259
257
  if stream.nil?
260
258
  stream = activate_stream(
261
- id: frame[:stream],
262
- weight: frame[:weight] || DEFAULT_WEIGHT,
259
+ id: frame[:stream],
260
+ weight: frame[:weight] || DEFAULT_WEIGHT,
263
261
  dependency: frame[:dependency] || 0,
264
- exclusive: frame[:exclusive] || false,
262
+ exclusive: frame[:exclusive] || false
265
263
  )
266
264
  emit(:stream, stream)
267
265
  end
@@ -333,10 +331,10 @@ module HTTP2
333
331
  # unused or closed parent stream.
334
332
  when :priority
335
333
  stream = activate_stream(
336
- id: frame[:stream],
337
- weight: frame[:weight] || DEFAULT_WEIGHT,
334
+ id: frame[:stream],
335
+ weight: frame[:weight] || DEFAULT_WEIGHT,
338
336
  dependency: frame[:dependency] || 0,
339
- exclusive: frame[:exclusive] || false,
337
+ exclusive: frame[:exclusive] || false
340
338
  )
341
339
 
342
340
  emit(:stream, stream)
@@ -358,9 +356,9 @@ module HTTP2
358
356
  end
359
357
  end
360
358
  end
361
-
362
359
  rescue StandardError => e
363
360
  raise if e.is_a?(Error::Error)
361
+
364
362
  connection_error(e: e)
365
363
  end
366
364
 
@@ -381,17 +379,15 @@ module HTTP2
381
379
  if frame[:type] == :data
382
380
  send_data(frame, true)
383
381
 
384
- else
382
+ elsif frame[:type] == :rst_stream && frame[:error] == :protocol_error
385
383
  # An endpoint can end a connection at any time. In particular, an
386
384
  # endpoint MAY choose to treat a stream error as a connection error.
387
- if frame[:type] == :rst_stream && frame[:error] == :protocol_error
388
- goaway(frame[:error])
389
- else
390
- # HEADERS and PUSH_PROMISE may generate CONTINUATION. Also send
391
- # RST_STREAM that are not protocol errors
392
- frames = encode(frame)
393
- frames.each { |f| emit(:frame, f) }
394
- end
385
+ goaway(frame[:error])
386
+ else
387
+ # HEADERS and PUSH_PROMISE may generate CONTINUATION. Also send
388
+ # RST_STREAM that are not protocol errors
389
+ frames = encode(frame)
390
+ frames.each { |f| emit(:frame, f) }
395
391
  end
396
392
  end
397
393
 
@@ -401,10 +397,10 @@ module HTTP2
401
397
  # @return [Array of Buffer] encoded frame
402
398
  def encode(frame)
403
399
  frames = if frame[:type] == :headers || frame[:type] == :push_promise
404
- encode_headers(frame) # HEADERS and PUSH_PROMISE may create more than one frame
405
- else
406
- [frame] # otherwise one frame
407
- end
400
+ encode_headers(frame) # HEADERS and PUSH_PROMISE may create more than one frame
401
+ else
402
+ [frame] # otherwise one frame
403
+ end
408
404
 
409
405
  frames.map { |f| @framer.generate(f) }
410
406
  end
@@ -460,9 +456,7 @@ module HTTP2
460
456
  # 4. The ALTSVC HTTP/2 Frame
461
457
  # An ALTSVC frame on stream 0 with empty (length 0) "Origin"
462
458
  # information is invalid and MUST be ignored.
463
- if frame[:origin] && !frame[:origin].empty?
464
- emit(frame[:type], frame)
465
- end
459
+ emit(frame[:type], frame) if frame[:origin] && !frame[:origin].empty?
466
460
  when :blocked
467
461
  emit(frame[:type], frame)
468
462
  else
@@ -495,9 +489,7 @@ module HTTP2
495
489
  when :client
496
490
  # Any value other than 0 or 1 MUST be treated as a
497
491
  # connection error (Section 5.4.1) of type PROTOCOL_ERROR.
498
- unless v.zero? || v == 1
499
- return ProtocolError.new("invalid #{key} value")
500
- end
492
+ return ProtocolError.new("invalid #{key} value") unless v.zero? || v == 1
501
493
  end
502
494
  when :settings_max_concurrent_streams
503
495
  # Any value is valid
@@ -505,18 +497,14 @@ module HTTP2
505
497
  # Values above the maximum flow control window size of 2^31-1 MUST
506
498
  # be treated as a connection error (Section 5.4.1) of type
507
499
  # FLOW_CONTROL_ERROR.
508
- unless v <= 0x7fffffff
509
- return FlowControlError.new("invalid #{key} value")
510
- end
500
+ return FlowControlError.new("invalid #{key} value") unless v <= 0x7fffffff
511
501
  when :settings_max_frame_size
512
502
  # The initial value is 2^14 (16,384) octets. The value advertised
513
503
  # by an endpoint MUST be between this initial value and the maximum
514
504
  # allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
515
505
  # Values outside this range MUST be treated as a connection error
516
506
  # (Section 5.4.1) of type PROTOCOL_ERROR.
517
- unless v >= 16_384 && v <= 16_777_215
518
- return ProtocolError.new("invalid #{key} value")
519
- end
507
+ return ProtocolError.new("invalid #{key} value") unless v >= 16_384 && v <= 16_777_215
520
508
  when :settings_max_header_list_size
521
509
  # Any value is valid
522
510
  # else # ignore unknown settings
@@ -536,12 +524,12 @@ module HTTP2
536
524
  # local: previously sent and pended our settings should be effective
537
525
  # remote: just received peer settings should immediately be effective
538
526
  settings, side = if frame[:flags].include?(:ack)
539
- # Process pending settings we have sent.
540
- [@pending_settings.shift, :local]
541
- else
542
- connection_error if validate_settings(@remote_role, frame[:payload])
543
- [frame[:payload], :remote]
544
- end
527
+ # Process pending settings we have sent.
528
+ [@pending_settings.shift, :local]
529
+ else
530
+ connection_error if validate_settings(@remote_role, frame[:payload])
531
+ [frame[:payload], :remote]
532
+ end
545
533
 
546
534
  settings.each do |key, v|
547
535
  case side
@@ -565,14 +553,14 @@ module HTTP2
565
553
  case side
566
554
  when :local
567
555
  @local_window = @local_window - @local_window_limit + v
568
- @streams.each do |_id, stream|
556
+ @streams.each_value do |stream|
569
557
  stream.emit(:local_window, stream.local_window - @local_window_limit + v)
570
558
  end
571
559
 
572
560
  @local_window_limit = v
573
561
  when :remote
574
562
  @remote_window = @remote_window - @remote_window_limit + v
575
- @streams.each do |_id, stream|
563
+ @streams.each_value do |stream|
576
564
  # Event name is :window, not :remote_window
577
565
  stream.emit(:window, stream.remote_window - @remote_window_limit + v)
578
566
  end
@@ -621,10 +609,7 @@ module HTTP2
621
609
  #
622
610
  # @param frame [Hash]
623
611
  def decode_headers(frame)
624
- if frame[:payload].is_a? Buffer
625
- frame[:payload] = @decompressor.decode(frame[:payload])
626
- end
627
-
612
+ frame[:payload] = @decompressor.decode(frame[:payload]) if frame[:payload].is_a? Buffer
628
613
  rescue CompressionError => e
629
614
  connection_error(:compression_error, e: e)
630
615
  rescue ProtocolError => e
@@ -643,7 +628,7 @@ module HTTP2
643
628
 
644
629
  frames = []
645
630
 
646
- while payload.bytesize > 0
631
+ while payload.bytesize.positive?
647
632
  cont = frame.dup
648
633
  cont[:type] = :continuation
649
634
  cont[:flags] = []
@@ -659,7 +644,6 @@ module HTTP2
659
644
  end
660
645
 
661
646
  frames
662
-
663
647
  rescue StandardError => e
664
648
  connection_error(:compression_error, e: e)
665
649
  nil
@@ -675,7 +659,7 @@ module HTTP2
675
659
  def activate_stream(id: nil, **args)
676
660
  connection_error(msg: 'Stream ID already exists') if @streams.key?(id)
677
661
 
678
- stream = Stream.new(**{ connection: self, id: id }.merge(args))
662
+ stream = Stream.new(connection: self, id: id, **args)
679
663
 
680
664
  # Streams that are in the "open" state, or either of the "half closed"
681
665
  # states count toward the maximum number of streams that an endpoint is
@@ -692,7 +676,7 @@ module HTTP2
692
676
  cleanup_recently_closed
693
677
  end
694
678
 
695
- stream.on(:promise, &method(:promise)) if self.is_a? Server
679
+ stream.on(:promise, &method(:promise)) if is_a? Server
696
680
  stream.on(:frame, &method(:send))
697
681
 
698
682
  @streams[id] = stream
@@ -705,6 +689,7 @@ module HTTP2
705
689
  @streams_recently_closed.each do |stream_id, ts|
706
690
  # Ruby Hash enumeration is ordered, so once fresh stream is met we can stop searching.
707
691
  break if now_ts - ts < RECENTLY_CLOSED_STREAMS_TTL
692
+
708
693
  to_delete << stream_id
709
694
  end
710
695
 
@@ -728,11 +713,12 @@ module HTTP2
728
713
  def connection_error(error = :protocol_error, msg: nil, e: nil)
729
714
  goaway(error) unless @state == :closed || @state == :new
730
715
 
731
- @state, @error = :closed, error
716
+ @state = :closed
717
+ @error = error
732
718
  klass = error.to_s.split('_').map(&:capitalize).join
733
- msg ||= e && e.message
734
- backtrace = (e && e.backtrace) || []
735
- fail Error.const_get(klass), msg, backtrace
719
+ msg ||= e&.message
720
+ backtrace = e&.backtrace || []
721
+ raise Error.const_get(klass), msg, backtrace
736
722
  end
737
723
  alias error connection_error
738
724
 
@@ -740,5 +726,5 @@ module HTTP2
740
726
  yield
741
727
  end
742
728
  end
743
- # rubocop:enable ClassLength
729
+ # rubocop:enable Metrics/ClassLength
744
730
  end