http-2 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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