http-2 0.10.2 → 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 +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