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.
- checksums.yaml +4 -4
- data/README.md +0 -2
- data/lib/http/2/buffer.rb +6 -4
- data/lib/http/2/client.rb +5 -1
- data/lib/http/2/compressor.rb +44 -36
- data/lib/http/2/connection.rb +76 -89
- data/lib/http/2/emitter.rb +4 -1
- data/lib/http/2/error.rb +2 -0
- data/lib/http/2/flow_buffer.rb +8 -3
- data/lib/http/2/framer.rb +83 -94
- data/lib/http/2/huffman.rb +19 -17
- data/lib/http/2/server.rb +9 -7
- data/lib/http/2/stream.rb +48 -48
- data/lib/http/2/version.rb +3 -1
- data/lib/http/2.rb +2 -0
- metadata +10 -63
- data/.autotest +0 -20
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -20
- data/.gitmodules +0 -3
- data/.rspec +0 -5
- data/.rubocop.yml +0 -93
- data/.rubocop_todo.yml +0 -122
- data/.travis.yml +0 -14
- data/Gemfile +0 -16
- data/Guardfile +0 -18
- data/Guardfile.h2spec +0 -12
- data/Rakefile +0 -49
- data/example/Gemfile +0 -3
- data/example/README.md +0 -44
- data/example/client.rb +0 -122
- data/example/helper.rb +0 -19
- data/example/keys/server.crt +0 -20
- data/example/keys/server.key +0 -27
- data/example/server.rb +0 -139
- data/example/upgrade_client.rb +0 -153
- data/example/upgrade_server.rb +0 -203
- data/http-2.gemspec +0 -22
- data/lib/tasks/generate_huffman_table.rb +0 -166
- data/spec/buffer_spec.rb +0 -28
- data/spec/client_spec.rb +0 -188
- data/spec/compressor_spec.rb +0 -666
- data/spec/connection_spec.rb +0 -665
- data/spec/emitter_spec.rb +0 -54
- data/spec/framer_spec.rb +0 -487
- data/spec/h2spec/h2spec.darwin +0 -0
- data/spec/h2spec/output/non_secure.txt +0 -317
- data/spec/helper.rb +0 -147
- data/spec/hpack_test_spec.rb +0 -84
- data/spec/huffman_spec.rb +0 -68
- data/spec/server_spec.rb +0 -52
- data/spec/stream_spec.rb +0 -878
- data/spec/support/deep_dup.rb +0 -55
- data/spec/support/duplicable.rb +0 -98
data/lib/http/2/connection.rb
CHANGED
@@ -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:
|
14
|
-
settings_enable_push:
|
15
|
-
settings_max_concurrent_streams:
|
16
|
-
settings_initial_window_size:
|
17
|
-
settings_max_frame_size:
|
18
|
-
settings_max_header_list_size:
|
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:
|
23
|
-
settings_enable_push:
|
24
|
-
settings_max_concurrent_streams:
|
25
|
-
settings_initial_window_size:
|
26
|
-
settings_max_frame_size:
|
27
|
-
settings_max_header_list_size:
|
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
|
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"
|
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
|
-
|
114
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
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? &&
|
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:
|
262
|
-
weight:
|
259
|
+
id: frame[:stream],
|
260
|
+
weight: frame[:weight] || DEFAULT_WEIGHT,
|
263
261
|
dependency: frame[:dependency] || 0,
|
264
|
-
exclusive:
|
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:
|
337
|
-
weight:
|
334
|
+
id: frame[:stream],
|
335
|
+
weight: frame[:weight] || DEFAULT_WEIGHT,
|
338
336
|
dependency: frame[:dependency] || 0,
|
339
|
-
exclusive:
|
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
|
-
|
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
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
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
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
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
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
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.
|
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.
|
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
|
-
#
|
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
|
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(
|
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
|
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
|
716
|
+
@state = :closed
|
717
|
+
@error = error
|
731
718
|
klass = error.to_s.split('_').map(&:capitalize).join
|
732
|
-
msg ||= e
|
733
|
-
backtrace =
|
734
|
-
|
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
|
data/lib/http/2/emitter.rb
CHANGED
@@ -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
|
-
|
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
data/lib/http/2/flow_buffer.rb
CHANGED
@@ -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
|
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
|
65
|
+
while @remote_window.positive? && !@send_buffer.empty?
|
63
66
|
frame = @send_buffer.shift
|
64
67
|
|
65
|
-
sent
|
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
|