http-2 0.10.2 → 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 +44 -36
  6. data/lib/http/2/connection.rb +76 -89
  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 +10 -63
  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 -122
  24. data/.travis.yml +0 -14
  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 -665
  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
@@ -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
@@ -76,8 +77,8 @@ module HTTP2
76
77
  @local_settings = DEFAULT_CONNECTION_SETTINGS.merge(settings)
77
78
  @remote_settings = SPEC_DEFAULT_CONNECTION_SETTINGS.dup
78
79
 
79
- @compressor = Header::Compressor.new(settings)
80
- @decompressor = Header::Decompressor.new(settings)
80
+ @compressor = Header::Compressor.new(**settings)
81
+ @decompressor = Header::Decompressor.new(**settings)
81
82
 
82
83
  @active_stream_count = 0
83
84
  @streams = {}
@@ -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(check) 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
@@ -593,7 +581,8 @@ module HTTP2
593
581
  # nothing to do
594
582
 
595
583
  when :settings_max_frame_size
596
- # nothing to do
584
+ # update framer max_frame_size
585
+ @framer.max_frame_size = v
597
586
 
598
587
  # else # ignore unknown settings
599
588
  end
@@ -620,10 +609,7 @@ module HTTP2
620
609
  #
621
610
  # @param frame [Hash]
622
611
  def decode_headers(frame)
623
- if frame[:payload].is_a? Buffer
624
- frame[:payload] = @decompressor.decode(frame[:payload])
625
- end
626
-
612
+ frame[:payload] = @decompressor.decode(frame[:payload]) if frame[:payload].is_a? Buffer
627
613
  rescue CompressionError => e
628
614
  connection_error(:compression_error, e: e)
629
615
  rescue ProtocolError => e
@@ -642,7 +628,7 @@ module HTTP2
642
628
 
643
629
  frames = []
644
630
 
645
- while payload.bytesize > 0
631
+ while payload.bytesize.positive?
646
632
  cont = frame.dup
647
633
  cont[:type] = :continuation
648
634
  cont[:flags] = []
@@ -658,7 +644,6 @@ module HTTP2
658
644
  end
659
645
 
660
646
  frames
661
-
662
647
  rescue StandardError => e
663
648
  connection_error(:compression_error, e: e)
664
649
  nil
@@ -674,7 +659,7 @@ module HTTP2
674
659
  def activate_stream(id: nil, **args)
675
660
  connection_error(msg: 'Stream ID already exists') if @streams.key?(id)
676
661
 
677
- stream = Stream.new({ connection: self, id: id }.merge(args))
662
+ stream = Stream.new(connection: self, id: id, **args)
678
663
 
679
664
  # Streams that are in the "open" state, or either of the "half closed"
680
665
  # states count toward the maximum number of streams that an endpoint is
@@ -691,7 +676,7 @@ module HTTP2
691
676
  cleanup_recently_closed
692
677
  end
693
678
 
694
- stream.on(:promise, &method(:promise)) if self.is_a? Server
679
+ stream.on(:promise, &method(:promise)) if is_a? Server
695
680
  stream.on(:frame, &method(:send))
696
681
 
697
682
  @streams[id] = stream
@@ -704,6 +689,7 @@ module HTTP2
704
689
  @streams_recently_closed.each do |stream_id, ts|
705
690
  # Ruby Hash enumeration is ordered, so once fresh stream is met we can stop searching.
706
691
  break if now_ts - ts < RECENTLY_CLOSED_STREAMS_TTL
692
+
707
693
  to_delete << stream_id
708
694
  end
709
695
 
@@ -727,11 +713,12 @@ module HTTP2
727
713
  def connection_error(error = :protocol_error, msg: nil, e: nil)
728
714
  goaway(error) unless @state == :closed || @state == :new
729
715
 
730
- @state, @error = :closed, error
716
+ @state = :closed
717
+ @error = error
731
718
  klass = error.to_s.split('_').map(&:capitalize).join
732
- msg ||= e && e.message
733
- backtrace = (e && e.backtrace) || []
734
- fail Error.const_get(klass), msg, backtrace
719
+ msg ||= e&.message
720
+ backtrace = e&.backtrace || []
721
+ raise Error.const_get(klass), msg, backtrace
735
722
  end
736
723
  alias error connection_error
737
724
 
@@ -739,5 +726,5 @@ module HTTP2
739
726
  yield
740
727
  end
741
728
  end
742
- # rubocop:enable ClassLength
729
+ # rubocop:enable Metrics/ClassLength
743
730
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Basic event emitter implementation with support for persistent and
3
5
  # one-time event callbacks.
@@ -8,7 +10,8 @@ module HTTP2
8
10
  # @param event [Symbol]
9
11
  # @param block [Proc] callback function
10
12
  def add_listener(event, &block)
11
- fail ArgumentError, 'must provide callback' unless block_given?
13
+ raise ArgumentError, 'must provide callback' unless block_given?
14
+
12
15
  listeners(event.to_sym).push block
13
16
  end
14
17
  alias on add_listener
data/lib/http/2/error.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Stream, connection, and compressor exceptions.
3
5
  module Error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Implementation of stream and connection DATA flow control: frames may
3
5
  # be split and / or may be buffered based on current flow control window.
@@ -24,7 +26,7 @@ module HTTP2
24
26
  # current received window size + delta length is strictly larger than
25
27
  # local window size, it throws a flow control error.
26
28
  #
27
- error(:flow_control_error) if @local_window < 0
29
+ error(:flow_control_error) if @local_window.negative?
28
30
 
29
31
  # Send WINDOW_UPDATE if the received window size goes over
30
32
  # the local window size / 2.
@@ -41,6 +43,7 @@ module HTTP2
41
43
  # until the receiver window is exhausted, after which he'll be
42
44
  # waiting for the WINDOW_UPDATE frame.
43
45
  return unless @local_window <= (window_max_size / 2)
46
+
44
47
  window_update(window_max_size - @local_window)
45
48
  end
46
49
 
@@ -59,10 +62,11 @@ module HTTP2
59
62
  # FIXME: Frames with zero length with the END_STREAM flag set (that
60
63
  # is, an empty DATA frame) MAY be sent if there is no available space
61
64
  # in either flow control window.
62
- while @remote_window > 0 && !@send_buffer.empty?
65
+ while @remote_window.positive? && !@send_buffer.empty?
63
66
  frame = @send_buffer.shift
64
67
 
65
- sent, frame_size = 0, frame[:payload].bytesize
68
+ sent = 0
69
+ frame_size = frame[:payload].bytesize
66
70
 
67
71
  if frame_size > @remote_window
68
72
  payload = frame.delete(:payload)
@@ -93,6 +97,7 @@ module HTTP2
93
97
 
94
98
  def process_window_update(frame)
95
99
  return if frame[:ignore]
100
+
96
101
  @remote_window += frame[:increment]
97
102
  send_data
98
103
  end