http-2 0.8.4 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rspec +1 -0
- data/.rubocop.yml +44 -8
- data/.rubocop_todo.yml +7 -4
- data/.travis.yml +1 -1
- data/Gemfile +1 -1
- data/Guardfile +0 -1
- data/LICENSE +21 -0
- data/README.md +13 -19
- data/example/client.rb +5 -1
- data/example/server.rb +2 -4
- data/example/upgrade_client.rb +153 -0
- data/example/upgrade_server.rb +2 -2
- data/http-2.gemspec +2 -3
- data/lib/http/2/client.rb +18 -1
- data/lib/http/2/compressor.rb +26 -11
- data/lib/http/2/connection.rb +62 -16
- data/lib/http/2/flow_buffer.rb +31 -0
- data/lib/http/2/framer.rb +3 -2
- data/lib/http/2/stream.rb +27 -15
- data/lib/http/2/version.rb +1 -1
- data/spec/client_spec.rb +105 -9
- data/spec/compressor_spec.rb +157 -26
- data/spec/connection_spec.rb +153 -76
- data/spec/framer_spec.rb +5 -5
- data/spec/helper.rb +125 -107
- data/spec/server_spec.rb +2 -1
- data/spec/stream_spec.rb +131 -115
- metadata +9 -8
data/example/upgrade_server.rb
CHANGED
@@ -39,7 +39,7 @@ end
|
|
39
39
|
|
40
40
|
class UpgradeHandler
|
41
41
|
VALID_UPGRADE_METHODS = %w(GET OPTIONS).freeze
|
42
|
-
UPGRADE_RESPONSE =
|
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
|
data/http-2.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
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'
|
21
|
+
spec.add_development_dependency 'bundler'
|
23
22
|
end
|
data/lib/http/2/client.rb
CHANGED
@@ -38,14 +38,31 @@ 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
|
+
|
46
|
+
# sends the preface and initializes the first stream in half-closed state
|
47
|
+
def upgrade
|
48
|
+
fail ProtocolError unless @stream_id == 1
|
49
|
+
send_connection_preface
|
50
|
+
new_stream(state: :half_closed_local)
|
51
|
+
end
|
52
|
+
|
41
53
|
# Emit the connection preface if not yet
|
42
54
|
def send_connection_preface
|
43
55
|
return unless @state == :waiting_connection_preface
|
44
56
|
@state = :connected
|
45
57
|
emit(:frame, CONNECTION_PREFACE_MAGIC)
|
46
58
|
|
47
|
-
payload = @local_settings.
|
59
|
+
payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
|
48
60
|
settings(payload)
|
49
61
|
end
|
62
|
+
|
63
|
+
def self.settings_header(**settings)
|
64
|
+
frame = Framer.new.generate(type: :settings, stream: 0, payload: settings)
|
65
|
+
Base64.urlsafe_encode64(frame[9..-1])
|
66
|
+
end
|
50
67
|
end
|
51
68
|
end
|
data/lib/http/2/compressor.rb
CHANGED
@@ -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
|
@@ -199,8 +203,12 @@ module HTTP2
|
|
199
203
|
commands = []
|
200
204
|
# Literals commands are marked with :noindex when index is not used
|
201
205
|
noindex = [:static, :never].include?(@options[:index])
|
202
|
-
headers.each do |
|
203
|
-
|
206
|
+
headers.each do |field, value|
|
207
|
+
# Literal header names MUST be translated to lowercase before
|
208
|
+
# encoding and transmission.
|
209
|
+
field = field.downcase
|
210
|
+
value = '/' if field == ':path' && value.empty?
|
211
|
+
cmd = addcmd(field, value)
|
204
212
|
cmd[:type] = :noindex if noindex && cmd[:type] == :incremental
|
205
213
|
commands << cmd
|
206
214
|
process(cmd)
|
@@ -220,7 +228,7 @@ module HTTP2
|
|
220
228
|
#
|
221
229
|
# @param header [Array] +[name, value]+
|
222
230
|
# @return [Hash] command
|
223
|
-
def addcmd(header)
|
231
|
+
def addcmd(*header)
|
224
232
|
exact = nil
|
225
233
|
name_only = nil
|
226
234
|
|
@@ -441,11 +449,8 @@ module HTTP2
|
|
441
449
|
# @return [Buffer]
|
442
450
|
def encode(headers)
|
443
451
|
buffer = Buffer.new
|
444
|
-
|
445
|
-
|
446
|
-
# encoding and transmission.
|
447
|
-
headers.map! { |hk, hv| [hk.downcase, hv] }
|
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
|
-
|
553
|
-
|
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
|
data/lib/http/2/connection.rb
CHANGED
@@ -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,
|
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.
|
@@ -49,9 +52,6 @@ module HTTP2
|
|
49
52
|
# Connection state (:new, :closed).
|
50
53
|
attr_reader :state
|
51
54
|
|
52
|
-
# Last connection error if connection is aborted.
|
53
|
-
attr_reader :error
|
54
|
-
|
55
55
|
# Size of current connection flow control window (by default, set to
|
56
56
|
# infinity, but is automatically updated on receipt of peer settings).
|
57
57
|
attr_reader :local_window
|
@@ -95,6 +95,13 @@ module HTTP2
|
|
95
95
|
@send_buffer = []
|
96
96
|
@continuation = []
|
97
97
|
@error = nil
|
98
|
+
|
99
|
+
@h2c_upgrade = nil
|
100
|
+
@closed_since = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def closed?
|
104
|
+
@state == :closed
|
98
105
|
end
|
99
106
|
|
100
107
|
# Allocates new stream for current connection.
|
@@ -141,12 +148,14 @@ module HTTP2
|
|
141
148
|
send(type: :goaway, last_stream: last_stream,
|
142
149
|
error: error, payload: payload)
|
143
150
|
@state = :closed
|
151
|
+
@closed_since = Time.now
|
144
152
|
end
|
145
153
|
|
146
154
|
# Sends a WINDOW_UPDATE frame to the peer.
|
147
155
|
#
|
148
156
|
# @param increment [Integer]
|
149
157
|
def window_update(increment)
|
158
|
+
@local_window += increment
|
150
159
|
send(type: :window_update, stream: 0, increment: increment)
|
151
160
|
end
|
152
161
|
|
@@ -312,6 +321,10 @@ module HTTP2
|
|
312
321
|
else
|
313
322
|
if (stream = @streams[frame[:stream]])
|
314
323
|
stream << frame
|
324
|
+
if frame[:type] == :data
|
325
|
+
update_local_window(frame)
|
326
|
+
calculate_window_update(@local_window_limit)
|
327
|
+
end
|
315
328
|
else
|
316
329
|
case frame[:type]
|
317
330
|
# The PRIORITY frame can be sent for a stream in the "idle" or
|
@@ -346,11 +359,14 @@ module HTTP2
|
|
346
359
|
end
|
347
360
|
end
|
348
361
|
|
349
|
-
rescue => e
|
362
|
+
rescue StandardError => e
|
350
363
|
raise if e.is_a?(Error::Error)
|
351
364
|
connection_error(e: e)
|
352
365
|
end
|
353
|
-
|
366
|
+
|
367
|
+
def <<(*args)
|
368
|
+
receive(*args)
|
369
|
+
end
|
354
370
|
|
355
371
|
private
|
356
372
|
|
@@ -438,12 +454,22 @@ module HTTP2
|
|
438
454
|
# the connection, although a new connection can be established
|
439
455
|
# for new streams.
|
440
456
|
@state = :closed
|
457
|
+
@closed_since = Time.now
|
441
458
|
emit(:goaway, frame[:last_stream], frame[:error], frame[:payload])
|
442
|
-
when :altsvc
|
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
|
443
467
|
emit(frame[:type], frame)
|
444
468
|
else
|
445
469
|
connection_error
|
446
470
|
end
|
471
|
+
when :closed
|
472
|
+
connection_error if (Time.now - @closed_since) > 15
|
447
473
|
else
|
448
474
|
connection_error
|
449
475
|
end
|
@@ -488,7 +514,7 @@ module HTTP2
|
|
488
514
|
# allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
|
489
515
|
# Values outside this range MUST be treated as a connection error
|
490
516
|
# (Section 5.4.1) of type PROTOCOL_ERROR.
|
491
|
-
unless
|
517
|
+
unless v >= 16_384 && v <= 16_777_215
|
492
518
|
return ProtocolError.new("invalid #{key} value")
|
493
519
|
end
|
494
520
|
when :settings_max_header_list_size
|
@@ -598,8 +624,12 @@ module HTTP2
|
|
598
624
|
frame[:payload] = @decompressor.decode(frame[:payload])
|
599
625
|
end
|
600
626
|
|
601
|
-
rescue => e
|
627
|
+
rescue CompressionError => e
|
602
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)
|
603
633
|
end
|
604
634
|
|
605
635
|
# Encode headers payload and update connection compressor state.
|
@@ -629,7 +659,7 @@ module HTTP2
|
|
629
659
|
|
630
660
|
frames
|
631
661
|
|
632
|
-
rescue => e
|
662
|
+
rescue StandardError => e
|
633
663
|
connection_error(:compression_error, e: e)
|
634
664
|
nil
|
635
665
|
end
|
@@ -651,24 +681,38 @@ module HTTP2
|
|
651
681
|
# permitted to open.
|
652
682
|
stream.once(:active) { @active_stream_count += 1 }
|
653
683
|
stream.once(:close) do
|
654
|
-
@streams.delete id
|
655
684
|
@active_stream_count -= 1
|
656
685
|
|
657
686
|
# Store a reference to the closed stream, such that we can respond
|
658
687
|
# to any in-flight frames while close is registered on both sides.
|
659
688
|
# References to such streams will be purged whenever another stream
|
660
|
-
# is closed, with a
|
661
|
-
@streams_recently_closed
|
662
|
-
|
689
|
+
# is closed, with a defined RTT time window.
|
690
|
+
@streams_recently_closed[id] = Time.now.to_i
|
691
|
+
cleanup_recently_closed
|
663
692
|
end
|
664
693
|
|
665
694
|
stream.on(:promise, &method(:promise)) if self.is_a? Server
|
666
695
|
stream.on(:frame, &method(:send))
|
667
|
-
stream.on(:window_update, &method(:window_update))
|
668
696
|
|
669
697
|
@streams[id] = stream
|
670
698
|
end
|
671
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
|
+
|
672
716
|
# Emit GOAWAY error indicating to peer that the connection is being
|
673
717
|
# aborted, and once sent, raise a local exception.
|
674
718
|
#
|
@@ -689,9 +733,11 @@ module HTTP2
|
|
689
733
|
backtrace = (e && e.backtrace) || []
|
690
734
|
fail Error.const_get(klass), msg, backtrace
|
691
735
|
end
|
736
|
+
alias error connection_error
|
692
737
|
|
693
738
|
def manage_state(_)
|
694
739
|
yield
|
695
740
|
end
|
696
741
|
end
|
742
|
+
# rubocop:enable ClassLength
|
697
743
|
end
|
data/lib/http/2/flow_buffer.rb
CHANGED
@@ -13,6 +13,37 @@ module HTTP2
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
+
def update_local_window(frame)
|
17
|
+
frame_size = frame[:payload].bytesize
|
18
|
+
frame_size += frame[:padding] || 0
|
19
|
+
@local_window -= frame_size
|
20
|
+
end
|
21
|
+
|
22
|
+
def calculate_window_update(window_max_size)
|
23
|
+
# If DATA frame is received with length > 0 and
|
24
|
+
# current received window size + delta length is strictly larger than
|
25
|
+
# local window size, it throws a flow control error.
|
26
|
+
#
|
27
|
+
error(:flow_control_error) if @local_window < 0
|
28
|
+
|
29
|
+
# Send WINDOW_UPDATE if the received window size goes over
|
30
|
+
# the local window size / 2.
|
31
|
+
#
|
32
|
+
# The HTTP/2 spec mandates that every DATA frame received
|
33
|
+
# generates a WINDOW_UPDATE to send. In some cases however,
|
34
|
+
# (ex: DATA frames with short payloads),
|
35
|
+
# the noise generated by flow control frames creates enough
|
36
|
+
# congestion for this to be deemed very inefficient.
|
37
|
+
#
|
38
|
+
# This heuristic was inherited from nghttp, which delays the
|
39
|
+
# WINDOW_UPDATE until at least half the window is exhausted.
|
40
|
+
# This works because the sender doesn't need those increments
|
41
|
+
# until the receiver window is exhausted, after which he'll be
|
42
|
+
# waiting for the WINDOW_UPDATE frame.
|
43
|
+
return unless @local_window <= (window_max_size / 2)
|
44
|
+
window_update(window_max_size - @local_window)
|
45
|
+
end
|
46
|
+
|
16
47
|
# Buffers outgoing DATA frames and applies flow control logic to split
|
17
48
|
# and emit DATA frames based on current flow control window. If the
|
18
49
|
# window is large enough, the data is sent immediately. Otherwise, the
|
data/lib/http/2/framer.rb
CHANGED
@@ -359,14 +359,14 @@ module HTTP2
|
|
359
359
|
if frame[:flags].include? :priority
|
360
360
|
e_sd = payload.read_uint32
|
361
361
|
frame[:stream_dependency] = e_sd & RBIT
|
362
|
-
frame[:exclusive] = (e_sd & EBIT) != 0
|
362
|
+
frame[:exclusive] = (e_sd & EBIT) != 0
|
363
363
|
frame[:weight] = payload.getbyte + 1
|
364
364
|
end
|
365
365
|
frame[:payload] = payload.read(frame[:length])
|
366
366
|
when :priority
|
367
367
|
e_sd = payload.read_uint32
|
368
368
|
frame[:stream_dependency] = e_sd & RBIT
|
369
|
-
frame[:exclusive] = (e_sd & EBIT) != 0
|
369
|
+
frame[:exclusive] = (e_sd & EBIT) != 0
|
370
370
|
frame[:weight] = payload.getbyte + 1
|
371
371
|
when :rst_stream
|
372
372
|
frame[:error] = unpack_error payload.read_uint32
|
@@ -442,4 +442,5 @@ module HTTP2
|
|
442
442
|
name || error
|
443
443
|
end
|
444
444
|
end
|
445
|
+
# rubocop:enable ClassLength
|
445
446
|
end
|
data/lib/http/2/stream.rb
CHANGED
@@ -71,22 +71,28 @@ module HTTP2
|
|
71
71
|
# @param exclusive [Boolean]
|
72
72
|
# @param window [Integer]
|
73
73
|
# @param parent [Stream]
|
74
|
-
|
74
|
+
# @param state [Symbol]
|
75
|
+
def initialize(connection:, id:, weight: 16, dependency: 0, exclusive: false, parent: nil, state: :idle)
|
75
76
|
@connection = connection
|
76
77
|
@id = id
|
77
78
|
@weight = weight
|
78
79
|
@dependency = dependency
|
79
80
|
process_priority(weight: weight, stream_dependency: dependency, exclusive: exclusive)
|
81
|
+
@local_window_max_size = connection.local_settings[:settings_initial_window_size]
|
80
82
|
@local_window = connection.local_settings[:settings_initial_window_size]
|
81
83
|
@remote_window = connection.remote_settings[:settings_initial_window_size]
|
82
84
|
@parent = parent
|
83
|
-
@state =
|
85
|
+
@state = state
|
84
86
|
@error = false
|
85
87
|
@closed = false
|
86
88
|
@send_buffer = []
|
87
89
|
|
88
90
|
on(:window) { |v| @remote_window = v }
|
89
|
-
on(:local_window) { |v| @local_window = v }
|
91
|
+
on(:local_window) { |v| @local_window_max_size = @local_window = v }
|
92
|
+
end
|
93
|
+
|
94
|
+
def closed?
|
95
|
+
@state == :closed
|
90
96
|
end
|
91
97
|
|
92
98
|
# Processes incoming HTTP 2.0 frames. The frames must be decoded upstream.
|
@@ -97,21 +103,27 @@ module HTTP2
|
|
97
103
|
|
98
104
|
case frame[:type]
|
99
105
|
when :data
|
100
|
-
|
101
|
-
|
102
|
-
@local_window -= window_size
|
106
|
+
update_local_window(frame)
|
107
|
+
# Emit DATA frame
|
103
108
|
emit(:data, frame[:payload]) unless frame[:ignore]
|
104
|
-
|
105
|
-
|
106
|
-
# assuming that emit(:data) can now receive next data
|
107
|
-
window_update(window_size) if window_size > 0
|
108
|
-
when :headers, :push_promise
|
109
|
+
calculate_window_update(@local_window_max_size)
|
110
|
+
when :headers
|
109
111
|
emit(:headers, frame[:payload]) unless frame[:ignore]
|
112
|
+
when :push_promise
|
113
|
+
emit(:promise_headers, frame[:payload]) unless frame[:ignore]
|
110
114
|
when :priority
|
111
115
|
process_priority(frame)
|
112
116
|
when :window_update
|
113
117
|
process_window_update(frame)
|
114
|
-
when :altsvc
|
118
|
+
when :altsvc
|
119
|
+
# 4. The ALTSVC HTTP/2 Frame
|
120
|
+
# An ALTSVC frame on a
|
121
|
+
# stream other than stream 0 containing non-empty "Origin" information
|
122
|
+
# is invalid and MUST be ignored.
|
123
|
+
if !frame[:origin] || frame[:origin].empty?
|
124
|
+
emit(frame[:type], frame)
|
125
|
+
end
|
126
|
+
when :blocked
|
115
127
|
emit(frame[:type], frame)
|
116
128
|
end
|
117
129
|
|
@@ -144,6 +156,7 @@ module HTTP2
|
|
144
156
|
end
|
145
157
|
|
146
158
|
# Sends a HEADERS frame containing HTTP response headers.
|
159
|
+
# All pseudo-header fields MUST appear in the header block before regular header fields.
|
147
160
|
#
|
148
161
|
# @param headers [Array or Hash] Array of key-value pairs or Hash
|
149
162
|
# @param end_headers [Boolean] indicates that no more headers will be sent
|
@@ -153,7 +166,7 @@ module HTTP2
|
|
153
166
|
flags << :end_headers if end_headers
|
154
167
|
flags << :end_stream if end_stream
|
155
168
|
|
156
|
-
send(type: :headers, flags: flags, payload: headers
|
169
|
+
send(type: :headers, flags: flags, payload: headers)
|
157
170
|
end
|
158
171
|
|
159
172
|
def promise(headers, end_headers: true, &block)
|
@@ -228,8 +241,6 @@ module HTTP2
|
|
228
241
|
#
|
229
242
|
# @param increment [Integer]
|
230
243
|
def window_update(increment)
|
231
|
-
# always emit connection-level WINDOW_UPDATE
|
232
|
-
emit(:window_update, increment)
|
233
244
|
# emit stream-level WINDOW_UPDATE unless stream is closed
|
234
245
|
return if @state == :closed || @state == :remote_closed
|
235
246
|
send(type: :window_update, increment: increment)
|
@@ -602,6 +613,7 @@ module HTTP2
|
|
602
613
|
klass = error.to_s.split('_').map(&:capitalize).join
|
603
614
|
fail Error.const_get(klass), msg
|
604
615
|
end
|
616
|
+
alias error stream_error
|
605
617
|
|
606
618
|
def manage_state(frame)
|
607
619
|
transition(frame, true)
|