Module: Protocol::HTTP2::FlowControl

Included in:
Connection, Stream
Defined in:
lib/protocol/http2/flow_control.rb

Instance Method Summary collapse

Instance Method Details

#available_frame_size(maximum_frame_size = self.maximum_frame_size) ⇒ Object

This could be negative if the window has been overused due to a change in initial window size.



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/protocol/http2/flow_control.rb', line 32

def available_frame_size(maximum_frame_size = self.maximum_frame_size)
	available_size = self.available_size
	
	# puts "available_size=#{available_size} maximum_frame_size=#{maximum_frame_size}"
	
	if available_size < maximum_frame_size
		return available_size
	else
		return maximum_frame_size
	end
end

#available_sizeObject



27
28
29
# File 'lib/protocol/http2/flow_control.rb', line 27

def available_size
	@remote_window.available
end

#consume_local_window(frame) ⇒ Object



63
64
65
66
# File 'lib/protocol/http2/flow_control.rb', line 63

def consume_local_window(frame)
	amount = frame.length
	@local_window.consume(amount)
end

#consume_remote_window(frame) ⇒ Object

Keep track of the amount of data sent, and fail if is too much.



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/protocol/http2/flow_control.rb', line 45

def consume_remote_window(frame)
	amount = frame.length
	
	# Frames with zero length with the END_STREAM flag set (that is, an empty DATA frame) MAY be sent if there is no available space in either flow-control window.
	if amount.zero? and frame.end_stream?
		# It's okay, we can send. No need to consume, it's empty anyway.
	elsif amount >= 0 and amount <= @remote_window.available
		@remote_window.consume(amount)
	else
		raise FlowControlError, "Trying to send #{frame.length} bytes, exceeded window size: #{@remote_window.available} (#{@remote_window})"
	end
end

#consume_window(size = self.available_size) ⇒ Object

TODO:

This function can get slow when there are a lot of children [INEFFICIENT].

Traverse active streams in order of priority and allow them to consume the available flow-control window.

Parameters:

  • amount (Integer)

    the amount of data to write. Defaults to the current window capacity.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/protocol/http2/flow_control.rb', line 108

def consume_window(size = self.available_size)
	# Don't consume more than the available window size:
	size = [self.available_size, size].min
	# puts "consume_window(#{size}) local_window=#{@local_window} remote_window=#{@remote_window}"
	
	# Return if there is no window to consume:
	return unless size > 0
	
	# Allow the current flow-controlled instance to use up the window:
	if !self.window_updated(size) and children = self.children
		children = children.values.sort_by(&:weight)
		
		# This must always be at least >= `children.size`, since stream weight can't be 0.
		total = children.sum(&:weight)
		
		children.each do |child|
			# Compute the proportional allocation:
			allocated = (child.weight * size) / total
			child.consume_window(allocated)
		end
	end
end

#receive_window_update(frame) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/protocol/http2/flow_control.rb', line 84

def receive_window_update(frame)
	amount = frame.unpack
	
	# puts "expanding remote_window=#{@remote_window} by #{amount}"
	
	if amount != 0
		@remote_window.expand(amount)
	else
		raise ProtocolError, "Invalid window size increment: #{amount}!"
	end
	
	# puts "expanded remote_window=#{@remote_window} by #{amount}"
end

#request_window_updateObject



68
69
70
71
72
# File 'lib/protocol/http2/flow_control.rb', line 68

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

#send_window_update(window_increment) ⇒ Object

Notify the remote end that we are prepared to receive more data:



75
76
77
78
79
80
81
82
# File 'lib/protocol/http2/flow_control.rb', line 75

def send_window_update(window_increment)
	frame = WindowUpdateFrame.new(self.id)
	frame.pack window_increment
	
	write_frame(frame)
	
	@local_window.expand(window_increment)
end

#update_local_window(frame) ⇒ Object



58
59
60
61
# File 'lib/protocol/http2/flow_control.rb', line 58

def update_local_window(frame)
	consume_local_window(frame)
	request_window_update
end

#window_updated(size = self.available_size) ⇒ Boolean

The window has been expanded by the given amount.

Parameters:

  • size (Integer) (defaults to: self.available_size)

    the maximum amount of data to send.

Returns:

  • (Boolean)

    whether the window update was used or not.



101
102
103
# File 'lib/protocol/http2/flow_control.rb', line 101

def window_updated(size = self.available_size)
	return false
end