http-2 0.9.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 60d5e715ae277807ca53b015a14a0e8c7f6676b7
4
- data.tar.gz: 17c562bfb62b6db7209962ace7bd698a59fa2c91
2
+ SHA256:
3
+ metadata.gz: 8dd78ba6a3468b6a3ceda2ebabc37ceb895a90d675d9fecc035b74d2f479a457
4
+ data.tar.gz: d70542d69e57050ab9ee3c837b6123fea81424a03bf3b99ffdc889a54a2fd20c
5
5
  SHA512:
6
- metadata.gz: c8d072f9ac4302506d4770fd48fa153567812b2beaa2e0bc4e1f3d04b0cc7560ebf7bd6bc22335e0465814a08a70cdca9f52546b62ffbc362edea5ecfaf41103
7
- data.tar.gz: 950e2e78ff84257d62ddaf9d246b31e68689fd8125ee5fb180d239dbeaabe72d373c5a166353000a27d46f3f41c3924de19fa2287c195f7b75215204ae05e78f
6
+ metadata.gz: f678316f6235ed93ce5a3902ef3bf4c48854605de0875054d1f3e05196fc161bdfe49d149ff930413bef5ca76c61ab2d193092867a383f1f5dbe309cfef0f012
7
+ data.tar.gz: 52fce71379a9299df1cd8c93d44964d668d09c199564482193513b109a6339f220b4ae1e724c5965892b3e53855820a810e95405b8eb198797ea130611d39a05
@@ -8,25 +8,42 @@ AllCops:
8
8
  - 'vendor/**/*'
9
9
  - '**/huffman_statemachine.rb'
10
10
 
11
+ Layout/IndentHeredoc:
12
+ Exclude:
13
+ - 'lib/tasks/generate_huffman_table.rb'
14
+ - 'example/*'
15
+
11
16
  Metrics/LineLength:
12
17
  Max: 120
13
18
 
14
- Lint/EndAlignment:
15
- AlignWith: variable
19
+ Metrics/BlockLength:
20
+ Max: 700
21
+
22
+ Layout/EndAlignment:
23
+ EnforcedStyleAlignWith: variable
16
24
 
17
- Style/CaseIndentation:
18
- IndentWhenRelativeTo: end
25
+ Layout/CaseIndentation:
26
+ EnforcedStyle: end
19
27
 
20
- Style/IndentHash:
28
+ Layout/IndentHash:
21
29
  EnforcedStyle: consistent
22
30
 
23
- Style/TrailingCommaInLiteral:
31
+ Style/TrailingCommaInArrayLiteral:
24
32
  EnforcedStyleForMultiline: comma
25
33
 
26
- Style/SpaceAroundOperators:
34
+ Style/TrailingCommaInHashLiteral:
35
+ EnforcedStyleForMultiline: comma
36
+
37
+ Layout/SpaceAroundOperators:
27
38
  Enabled: false
28
39
 
29
- Style/ExtraSpacing:
40
+ Layout/ExtraSpacing:
41
+ Enabled: false
42
+
43
+ Layout/EmptyLinesAroundExceptionHandlingKeywords:
44
+ Enabled: false
45
+
46
+ Naming/UncommunicativeMethodParamName:
30
47
  Enabled: false
31
48
 
32
49
  Style/SignalException:
@@ -44,14 +61,33 @@ Style/ParenthesesAroundCondition:
44
61
  Style/IfInsideElse:
45
62
  Enabled: false
46
63
 
64
+ Style/IfUnlessModifier:
65
+ Enabled: false
66
+
67
+ Style/MultilineIfModifier:
68
+ Enabled: false
69
+
70
+ Lint/EmptyWhen:
71
+ Enabled: false
72
+
47
73
  Style/TrailingCommaInArguments:
48
74
  Enabled: false
49
75
 
50
76
  Style/TrailingUnderscoreVariable:
51
77
  Enabled: false
52
78
 
79
+ Style/SymbolArray:
80
+ Enabled: false
81
+
82
+ Style/CommentedKeyword:
83
+ Enabled: false
84
+
85
+ Style/PercentLiteralDelimiters:
86
+ Enabled: false
87
+
53
88
  Performance/TimesMap:
54
89
  Enabled: false
55
90
 
56
91
  Performance/RedundantBlockCall:
57
92
  Enabled: false
93
+
@@ -19,6 +19,9 @@ Metrics/BlockNesting:
19
19
  Metrics/ClassLength:
20
20
  Max: 325
21
21
 
22
+ Metrics/ModuleLength:
23
+ Max: 120
24
+
22
25
  # Offense count: 12
23
26
  Metrics/CyclomaticComplexity:
24
27
  Max: 60
@@ -75,14 +78,14 @@ Style/GuardClause:
75
78
  # Cop supports --auto-correct.
76
79
  # Configuration parameters: SupportedStyles, IndentationWidth.
77
80
  # SupportedStyles: special_inside_parentheses, consistent, align_brackets
78
- Style/IndentArray:
81
+ Layout/IndentArray:
79
82
  EnforcedStyle: consistent
80
83
 
81
84
  # Offense count: 1
82
85
  # Cop supports --auto-correct.
83
86
  # Configuration parameters: EnforcedStyle, SupportedStyles.
84
87
  # SupportedStyles: symmetrical, new_line, same_line
85
- Style/MultilineArrayBraceLayout:
88
+ Layout/MultilineArrayBraceLayout:
86
89
  Exclude:
87
90
  - 'spec/compressor_spec.rb'
88
91
 
@@ -90,7 +93,7 @@ Style/MultilineArrayBraceLayout:
90
93
  # Cop supports --auto-correct.
91
94
  # Configuration parameters: EnforcedStyle, SupportedStyles.
92
95
  # SupportedStyles: symmetrical, new_line, same_line
93
- Style/MultilineHashBraceLayout:
96
+ Layout/MultilineHashBraceLayout:
94
97
  Exclude:
95
98
  - 'spec/compressor_spec.rb'
96
99
 
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.2
6
6
  - 2.3.0
7
7
  - 2.4.0
8
- - jruby-9.1.8.0
8
+ - jruby-9.2.0.0 # latest stable
9
9
  - jruby-head
10
10
  - rbx-2
11
11
  matrix:
data/Gemfile CHANGED
@@ -10,7 +10,7 @@ group :test do
10
10
  gem 'pry-byebug', platform: :mri
11
11
  gem 'rspec', '~> 3.4.0'
12
12
  gem 'rspec-autotest'
13
- gem 'rubocop', '0.43.0'
13
+ gem 'rubocop', '0.57.2'
14
14
  end
15
15
 
16
16
  gemspec
data/Guardfile CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  guard :process, name: 'HTTP/2 Server', command: 'ruby example/server.rb', stop_signal: 'TERM' do
3
2
  watch(%r{^example/(.+)\.rb$})
4
3
  watch(%r{^lib/http/2/(.+)\.rb$})
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2013 Ilya Grigorik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # HTTP-2
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/http-2.png)](http://rubygems.org/gems/http-2)
4
- [![Build Status](https://travis-ci.org/igrigorik/http-2.png?branch=master)](https://travis-ci.org/igrigorik/http-2)
5
- [![Coverage Status](https://coveralls.io/repos/igrigorik/http-2/badge.png)](https://coveralls.io/r/igrigorik/http-2)
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
+ [![Coverage Status](https://coveralls.io/repos/igrigorik/http-2/badge.svg)](https://coveralls.io/r/igrigorik/http-2)
6
6
  [![Analytics](https://ga-beacon.appspot.com/UA-71196-10/http-2/readme)](https://github.com/igrigorik/ga-beacon)
7
7
 
8
8
  Pure Ruby, framework and transport agnostic, implementation of HTTP/2 protocol and HPACK header compression with support for:
9
9
 
10
- * [Binary framing](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#_binary_framing_layer) parsing and encoding
11
- * [Stream multiplexing](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_STREAMS_MESSAGES_FRAMES) and [prioritization](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_PRIORITIZATION)
12
- * Connection and stream [flow control](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#_flow_control)
13
- * [Header compression](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_HEADER_COMPRESSION) and [server push](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_PUSH)
10
+ * [Binary framing](https://hpbn.co/http2/#binary-framing-layer) parsing and encoding
11
+ * [Stream multiplexing](https://hpbn.co/http2/#streams-messages-and-frames) and [prioritization](https://hpbn.co/http2/#stream-prioritization)
12
+ * Connection and stream [flow control](https://hpbn.co/http2/#flow-control)
13
+ * [Header compression](https://hpbn.co/http2/#header-compression) and [server push](https://hpbn.co/http2/#server-push)
14
14
  * Connection and stream management
15
15
  * And more... see [API docs](http://www.rubydoc.info/github/igrigorik/http-2/frames)
16
16
 
@@ -88,7 +88,7 @@ Events emitted by the connection object:
88
88
 
89
89
  ### Stream lifecycle management
90
90
 
91
- A single HTTP/2 connection can [multiplex multiple streams](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#REQUEST_RESPONSE_MULTIPLEXING) in parallel: multiple requests and responses can be in flight simultaneously and stream data can be interleaved and prioritized. Further, the specification provides a well-defined lifecycle for each stream (see below).
91
+ A single HTTP/2 connection can [multiplex multiple streams](https://hpbn.co/http2/#request-and-response-multiplexing) in parallel: multiple requests and responses can be in flight simultaneously and stream data can be interleaved and prioritized. Further, the specification provides a well-defined lifecycle for each stream (see below).
92
92
 
93
93
  The good news is, all of the stream management, and state transitions, and error checking is handled by the library. All you have to do is subscribe to appropriate events (marked with ":" prefix in diagram below) and provide your application logic to handle request and response processing.
94
94
 
@@ -186,7 +186,7 @@ Events emitted by the [Stream object](http://www.rubydoc.info/github/igrigorik/h
186
186
 
187
187
  ### Prioritization
188
188
 
189
- Each HTTP/2 [stream has a priority value](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_PRIORITIZATION) that can be sent when the new stream is initialized, and optionally reprioritized later:
189
+ Each HTTP/2 [stream has a priority value](https://hpbn.co/http2/#stream-prioritization) that can be sent when the new stream is initialized, and optionally reprioritized later:
190
190
 
191
191
  ```ruby
192
192
  client = HTTP2::Client.new
@@ -203,7 +203,7 @@ On the opposite side, the server can optimize its stream processing order or res
203
203
 
204
204
  ### Flow control
205
205
 
206
- Multiplexing multiple streams over the same TCP connection introduces contention for shared bandwidth resources. Stream priorities can help determine the relative order of delivery, but priorities alone are insufficient to control how the resource allocation is performed between multiple streams. To address this, HTTP/2 provides a simple mechanism for [stream and connection flow control](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#_flow_control).
206
+ Multiplexing multiple streams over the same TCP connection introduces contention for shared bandwidth resources. Stream priorities can help determine the relative order of delivery, but priorities alone are insufficient to control how the resource allocation is performed between multiple streams. To address this, HTTP/2 provides a simple mechanism for [stream and connection flow control](https://hpbn.co/http2/#flow-control).
207
207
 
208
208
  Connection and stream flow control is handled by the library: all streams are initialized with the default window size (64KB), and send/receive window updates are automatically processed - i.e. window is decremented on outgoing data transfers, and incremented on receipt of window frames. Similarly, if the window is exceeded, then data frames are automatically buffered until window is updated.
209
209
 
@@ -219,16 +219,10 @@ stream.window # check current window size
219
219
  stream.window_update(2048) # increment stream window by 2048 bytes
220
220
  ```
221
221
 
222
- Alternatively, flow control can be disabled by emitting an appropriate settings frame on the connection:
223
-
224
- ```ruby
225
- # limit number of concurrent streams to 100 and disable flow control
226
- conn.settings(streams: 100, window: Float::INFINITY)
227
- ```
228
222
 
229
223
  ### Server push
230
224
 
231
- An HTTP/2 server can [send multiple replies](http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_PUSH) to a single client request. To do so, first it emits a "push promise" frame which contains the headers of the promised resource, followed by the response to the original request, as well as promised resource payloads (which may be interleaved). A simple example is in order:
225
+ An HTTP/2 server can [send multiple replies](https://hpbn.co/http2/#server-push) to a single client request. To do so, first it emits a "push promise" frame which contains the headers of the promised resource, followed by the response to the original request, as well as promised resource payloads (which may be interleaved). A simple example is in order:
232
226
 
233
227
  ```ruby
234
228
  conn = HTTP2::Server.new
@@ -276,10 +270,10 @@ conn.on(:promise) do |push|
276
270
  end
277
271
  ```
278
272
 
279
- The client can cancel any given push stream (via `.close`), or disable server push entirely by sending the appropriate settings frame (note that below setting only impacts server > client direction):
273
+ The client can cancel any given push stream (via `.close`), or disable server push entirely by sending the appropriate settings frame:
280
274
 
281
275
  ```ruby
282
- client.settings(streams: 0) # setting max limit to 0 disables server push
276
+ client.settings(settings_enable_push: 0)
283
277
  ```
284
278
  ### Specs
285
279
 
@@ -114,7 +114,7 @@ while !sock.closed? && !sock.eof?
114
114
 
115
115
  begin
116
116
  conn << data
117
- rescue => e
117
+ rescue StandardError => e
118
118
  puts "#{e.class} exception: #{e.message} - closing socket."
119
119
  e.backtrace.each { |l| puts "\t" + l }
120
120
  sock.close
@@ -130,7 +130,7 @@ loop do
130
130
 
131
131
  begin
132
132
  conn << data
133
- rescue => e
133
+ rescue StandardError => e
134
134
  puts "#{e.class} exception: #{e.message} - closing socket."
135
135
  e.backtrace.each { |l| puts "\t" + l }
136
136
  sock.close
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  # upgrader module
36
36
  class UpgradeHandler
37
- UPGRADE_REQUEST = <<-RESP.freeze
37
+ UPGRADE_REQUEST = <<RESP.freeze
38
38
  GET %s HTTP/1.1
39
39
  Connection: Upgrade, HTTP2-Settings
40
40
  HTTP2-Settings: #{HTTP2::Client.settings_header(settings_max_concurrent_streams: 100)}
@@ -73,9 +73,9 @@ RESP
73
73
  @complete = true
74
74
  end
75
75
 
76
- def on_headers_complete(h)
77
- @headers.merge!(h)
78
- puts "received headers: #{h}"
76
+ def on_headers_complete(headers)
77
+ @headers.merge!(headers)
78
+ puts "received headers: #{headers}"
79
79
  end
80
80
 
81
81
  def on_body(chunk)
@@ -144,8 +144,7 @@ while !sock.closed? && !sock.eof?
144
144
  elsif uh.complete
145
145
  conn << data
146
146
  end
147
-
148
- rescue => e
147
+ rescue StandardError => e
149
148
  puts "#{e.class} exception: #{e.message} - closing socket."
150
149
  e.backtrace.each { |l| puts "\t" + l }
151
150
  conn.close
@@ -39,7 +39,7 @@ end
39
39
 
40
40
  class UpgradeHandler
41
41
  VALID_UPGRADE_METHODS = %w(GET OPTIONS).freeze
42
- UPGRADE_RESPONSE = <<-RESP
42
+ UPGRADE_RESPONSE = <<RESP.freeze
43
43
  HTTP/1.1 101 Switching Protocols
44
44
  Connection: Upgrade
45
45
  Upgrade: h2c
@@ -191,7 +191,7 @@ loop do
191
191
  conn << data
192
192
  end
193
193
 
194
- rescue => e
194
+ rescue StandardError => e
195
195
  puts "Exception: #{e}, #{e.message} - closing socket."
196
196
  puts e.backtrace.last(10).join("\n")
197
197
  sock.close
@@ -1,5 +1,4 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('./lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'http/2/version'
5
4
 
@@ -19,5 +18,5 @@ Gem::Specification.new do |spec|
19
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
19
  spec.require_paths = ['lib']
21
20
 
22
- spec.add_development_dependency 'bundler', '~> 1.3'
21
+ spec.add_development_dependency 'bundler'
23
22
  end
@@ -38,6 +38,11 @@ module HTTP2
38
38
  super(frame)
39
39
  end
40
40
 
41
+ def receive(frame)
42
+ send_connection_preface
43
+ super(frame)
44
+ end
45
+
41
46
  # sends the preface and initializes the first stream in half-closed state
42
47
  def upgrade
43
48
  fail ProtocolError unless @stream_id == 1
@@ -51,7 +56,7 @@ module HTTP2
51
56
  @state = :connected
52
57
  emit(:frame, CONNECTION_PREFACE_MAGIC)
53
58
 
54
- payload = @local_settings.select { |k, v| v != SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
59
+ payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
55
60
  settings(payload)
56
61
  end
57
62
 
@@ -139,12 +139,16 @@ module HTTP2
139
139
  # - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-4.1
140
140
  #
141
141
  # @param cmd [Hash] { type:, name:, value:, index: }
142
- # @return [Array] +[name, value]+ header field that is added to the decoded header list
142
+ # @return [Array, nil] +[name, value]+ header field that is added to the decoded header list,
143
+ # or nil if +cmd[:type]+ is +:changetablesize+
143
144
  def process(cmd)
144
145
  emit = nil
145
146
 
146
147
  case cmd[:type]
147
148
  when :changetablesize
149
+ if cmd[:value] > @limit
150
+ fail CompressionError, 'dynamic table size update exceed limit'
151
+ end
148
152
  self.table_size = cmd[:value]
149
153
 
150
154
  when :indexed
@@ -445,7 +449,8 @@ module HTTP2
445
449
  # @return [Buffer]
446
450
  def encode(headers)
447
451
  buffer = Buffer.new
448
-
452
+ pseudo_headers, regular_headers = headers.partition { |f, _| f.start_with? ':' }
453
+ headers = [*pseudo_headers, *regular_headers]
449
454
  commands = @cc.encode(headers)
450
455
  commands.each do |cmd|
451
456
  buffer << header(cmd)
@@ -549,8 +554,18 @@ module HTTP2
549
554
  # @return [Array] +[[name, value], ...]+
550
555
  def decode(buf)
551
556
  list = []
552
- list << @cc.process(header(buf)) until buf.empty?
553
- list.compact
557
+ decoding_pseudo_headers = true
558
+ until buf.empty?
559
+ next_header = @cc.process(header(buf))
560
+ next if next_header.nil?
561
+ is_pseudo_header = next_header.first.start_with? ':'
562
+ if !decoding_pseudo_headers && is_pseudo_header
563
+ fail ProtocolError, 'one or more pseudo headers encountered after regular headers'
564
+ end
565
+ decoding_pseudo_headers = is_pseudo_header
566
+ list << next_header
567
+ end
568
+ list
554
569
  end
555
570
  end
556
571
  end
@@ -20,9 +20,9 @@ module HTTP2
20
20
 
21
21
  DEFAULT_CONNECTION_SETTINGS = {
22
22
  settings_header_table_size: 4096,
23
- settings_enable_push: 1, # enabled for servers
23
+ settings_enable_push: 1, # enabled for servers
24
24
  settings_max_concurrent_streams: 100,
25
- settings_initial_window_size: 65_535, #
25
+ settings_initial_window_size: 65_535,
26
26
  settings_max_frame_size: 16_384,
27
27
  settings_max_header_list_size: 2**31 - 1, # unlimited
28
28
  }.freeze
@@ -33,6 +33,9 @@ module HTTP2
33
33
  # Default connection "fast-fail" preamble string as defined by the spec.
34
34
  CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
35
35
 
36
+ # Time to hold recently closed streams until purge (seconds)
37
+ RECENTLY_CLOSED_STREAMS_TTL = 15
38
+
36
39
  # Connection encapsulates all of the connection, stream, flow-control,
37
40
  # error management, and other processing logic required for a well-behaved
38
41
  # HTTP 2.0 endpoint.
@@ -94,6 +97,11 @@ module HTTP2
94
97
  @error = nil
95
98
 
96
99
  @h2c_upgrade = nil
100
+ @closed_since = nil
101
+ end
102
+
103
+ def closed?
104
+ @state == :closed
97
105
  end
98
106
 
99
107
  # Allocates new stream for current connection.
@@ -140,6 +148,7 @@ module HTTP2
140
148
  send(type: :goaway, last_stream: last_stream,
141
149
  error: error, payload: payload)
142
150
  @state = :closed
151
+ @closed_since = Time.now
143
152
  end
144
153
 
145
154
  # Sends a WINDOW_UPDATE frame to the peer.
@@ -350,11 +359,14 @@ module HTTP2
350
359
  end
351
360
  end
352
361
 
353
- rescue => e
362
+ rescue StandardError => e
354
363
  raise if e.is_a?(Error::Error)
355
364
  connection_error(e: e)
356
365
  end
357
- alias << receive
366
+
367
+ def <<(*args)
368
+ receive(*args)
369
+ end
358
370
 
359
371
  private
360
372
 
@@ -442,12 +454,22 @@ module HTTP2
442
454
  # the connection, although a new connection can be established
443
455
  # for new streams.
444
456
  @state = :closed
457
+ @closed_since = Time.now
445
458
  emit(:goaway, frame[:last_stream], frame[:error], frame[:payload])
446
- when :altsvc, :blocked
459
+ when :altsvc
460
+ # 4. The ALTSVC HTTP/2 Frame
461
+ # An ALTSVC frame on stream 0 with empty (length 0) "Origin"
462
+ # information is invalid and MUST be ignored.
463
+ if frame[:origin] && !frame[:origin].empty?
464
+ emit(frame[:type], frame)
465
+ end
466
+ when :blocked
447
467
  emit(frame[:type], frame)
448
468
  else
449
469
  connection_error
450
470
  end
471
+ when :closed
472
+ connection_error if (Time.now - @closed_since) > 15
451
473
  else
452
474
  connection_error
453
475
  end
@@ -492,7 +514,7 @@ module HTTP2
492
514
  # allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
493
515
  # Values outside this range MUST be treated as a connection error
494
516
  # (Section 5.4.1) of type PROTOCOL_ERROR.
495
- unless 16_384 <= v && v <= 16_777_215
517
+ unless v >= 16_384 && v <= 16_777_215
496
518
  return ProtocolError.new("invalid #{key} value")
497
519
  end
498
520
  when :settings_max_header_list_size
@@ -602,8 +624,12 @@ module HTTP2
602
624
  frame[:payload] = @decompressor.decode(frame[:payload])
603
625
  end
604
626
 
605
- rescue => e
627
+ rescue CompressionError => e
606
628
  connection_error(:compression_error, e: e)
629
+ rescue ProtocolError => e
630
+ connection_error(:protocol_error, e: e)
631
+ rescue StandardError => e
632
+ connection_error(:internal_error, e: e)
607
633
  end
608
634
 
609
635
  # Encode headers payload and update connection compressor state.
@@ -633,7 +659,7 @@ module HTTP2
633
659
 
634
660
  frames
635
661
 
636
- rescue => e
662
+ rescue StandardError => e
637
663
  connection_error(:compression_error, e: e)
638
664
  nil
639
665
  end
@@ -660,13 +686,9 @@ module HTTP2
660
686
  # Store a reference to the closed stream, such that we can respond
661
687
  # to any in-flight frames while close is registered on both sides.
662
688
  # References to such streams will be purged whenever another stream
663
- # is closed, with a minimum of 15s RTT time window.
664
- @streams_recently_closed[id] = Time.now
665
- to_delete = @streams_recently_closed.select { |_, v| (Time.now - v) > 15 }
666
- to_delete.each do |stream_id|
667
- @streams.delete stream_id
668
- @streams_recently_closed.delete stream_id
669
- end
689
+ # is closed, with a defined RTT time window.
690
+ @streams_recently_closed[id] = Time.now.to_i
691
+ cleanup_recently_closed
670
692
  end
671
693
 
672
694
  stream.on(:promise, &method(:promise)) if self.is_a? Server
@@ -675,6 +697,22 @@ module HTTP2
675
697
  @streams[id] = stream
676
698
  end
677
699
 
700
+ # Purge recently streams closed within defined RTT time window.
701
+ def cleanup_recently_closed
702
+ now_ts = Time.now.to_i
703
+ to_delete = []
704
+ @streams_recently_closed.each do |stream_id, ts|
705
+ # Ruby Hash enumeration is ordered, so once fresh stream is met we can stop searching.
706
+ break if now_ts - ts < RECENTLY_CLOSED_STREAMS_TTL
707
+ to_delete << stream_id
708
+ end
709
+
710
+ to_delete.each do |stream_id|
711
+ @streams.delete stream_id
712
+ @streams_recently_closed.delete stream_id
713
+ end
714
+ end
715
+
678
716
  # Emit GOAWAY error indicating to peer that the connection is being
679
717
  # aborted, and once sent, raise a local exception.
680
718
  #
@@ -701,4 +739,5 @@ module HTTP2
701
739
  yield
702
740
  end
703
741
  end
742
+ # rubocop:enable ClassLength
704
743
  end