Skip to content

Commit

Permalink
Improve flow control handling allowing connection local window to hav…
Browse files Browse the repository at this point in the history
…e desired high water mark.
  • Loading branch information
ioquatix committed Mar 31, 2020
1 parent d9a367d commit f65fb86
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 15 deletions.
13 changes: 4 additions & 9 deletions lib/protocol/http2/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def initialize(framer, local_stream_id)
@decoder = HPACK::Context.new
@encoder = HPACK::Context.new

@local_window = Window.new(@local_settings.initial_window_size)
@remote_window = Window.new(@remote_settings.initial_window_size)
@local_window = LocalWindow.new
@remote_window = Window.new
end

def id
Expand Down Expand Up @@ -183,13 +183,6 @@ def send_settings(changes)
frame.pack(changes)

write_frame(frame)

# If the initial window size is set to something bigger than the default, we want to increase it.
difference = @local_settings.pending.initial_window_size - @local_window.capacity

if difference > 0
send_window_update(difference)
end
end

# Transition the connection into the closed state.
Expand Down Expand Up @@ -235,6 +228,8 @@ def update_local_settings(changes)
@streams.each_value do |stream|
stream.local_window.capacity = capacity
end

@local_window.desired = capacity
end

def update_remote_settings(changes)
Expand Down
3 changes: 2 additions & 1 deletion lib/protocol/http2/flow_controlled.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ def update_local_window(frame)
end

def consume_local_window(frame)
# For flow-control calculations, the 9-octet frame header is not counted.
amount = frame.length
@local_window.consume(amount)
end

def request_window_update
if @local_window.limited?
self.send_window_update(@local_window.used)
self.send_window_update(@local_window.wanted)
end
end

Expand Down
32 changes: 28 additions & 4 deletions lib/protocol/http2/window_update_frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ def initialize(capacity = 0xFFFF)
@capacity = capacity
end

def dup
return self.class.new(@capacity)
end

# The window is completely full?
def full?
@available <= 0
Expand Down Expand Up @@ -73,6 +69,10 @@ def expand(amount)
end
end

def wanted
@used
end

def limited?
@available < (@capacity / 2)
end
Expand All @@ -82,6 +82,30 @@ def to_s
end
end

# This is a window which efficiently maintains a desired capacity.
class LocalWindow < Window
def initialize(capacity = 0xFFFF, desired: nil)
super(capacity)

@desired = desired
end

attr_accessor :desired

def wanted
if @desired
# We must send an update which allows at least @desired bytes to be sent.
(@desired - @capacity) + @used
else
@used
end
end

def limited?
@available < ((@desired || @capacity) / 2)
end
end

# The WINDOW_UPDATE frame is used to implement flow control.
#
# +-+-------------------------------------------------------------+
Expand Down
2 changes: 1 addition & 1 deletion spec/protocol/http2/window_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
expect(frame.unpack).to eq 120
end

context '#receive_window_update' do
describe '#receive_window_update' do
it "should be invoked when window update is received" do
# Write 200 bytes of data (client -> server) which exhausts server local window
stream.send_data("*" * 200)
Expand Down

0 comments on commit f65fb86

Please sign in to comment.