Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit 2da7440

Browse files
committed
Test flow control.
This also fixes a couple of bugs I found while I was testing this, such as the fact that the server should always send a SettingsFrame.
1 parent d18b643 commit 2da7440

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed

hyper/http20/connection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ def connect(self):
120120
f.settings[SettingsFrame.ENABLE_PUSH] = 0
121121
self._send_cb(f)
122122

123+
# The server will also send an initial settings frame, so get it.
124+
self._recv_cb()
125+
123126
return
124127

125128
def close(self):
@@ -249,7 +252,7 @@ def _update_settings(self, frame):
249252
oldsize = self._settings[SettingsFrame.INITIAL_WINDOW_SIZE]
250253
delta = newsize - oldsize
251254

252-
for stream in self.streams.keys():
255+
for stream in self.streams.values():
253256
stream._out_flow_control_window += delta
254257

255258
self._settings[SettingsFrame.INITIAL_WINDOW_SIZE] = newsize

test/test_integration.py

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,21 @@
1010
import threading
1111
import hyper
1212
from hyper import HTTP20Connection
13-
from hyper.http20.frame import Frame, SettingsFrame
13+
from hyper.http20.frame import (
14+
Frame, SettingsFrame, WindowUpdateFrame, DataFrame
15+
)
1416
from server import SocketLevelTest
1517

1618
# Turn off certificate verification for the tests.
1719
hyper.http20.tls._context = hyper.http20.tls._init_context()
1820
hyper.http20.tls._context.verify_mode = ssl.CERT_NONE
1921

22+
def decode_frame(frame_data):
23+
f, length = Frame.parse_frame_header(frame_data[:8])
24+
f.parse_body(frame_data[8:8 + length])
25+
assert 8 + length == len(frame_data)
26+
return f
27+
2028
class TestHyperIntegration(SocketLevelTest):
2129
def test_connection_string(self):
2230
self.set_up()
@@ -36,6 +44,10 @@ def socket_handler(listener):
3644
data.append(first)
3745
data.append(second)
3846

47+
# We need to send back a SettingsFrame.
48+
f = SettingsFrame(0)
49+
sock.send(f.serialize())
50+
3951
send_event.set()
4052
sock.close()
4153

@@ -66,6 +78,10 @@ def socket_handler(listener):
6678
data.append(first)
6779
data.append(second)
6880

81+
# We need to send back a SettingsFrame.
82+
f = SettingsFrame(0)
83+
sock.send(f.serialize())
84+
6985
send_event.set()
7086
sock.close()
7187

@@ -76,11 +92,76 @@ def socket_handler(listener):
7692

7793
# Get the second chunk of data and decode it into a frame.
7894
data = data[1]
79-
f, length = Frame.parse_frame_header(data[:8])
80-
f.parse_body(data[8:])
95+
f = decode_frame(data)
8196

8297
assert isinstance(f, SettingsFrame)
8398
assert f.stream_id == 0
8499
assert f.settings == {SettingsFrame.ENABLE_PUSH: 0}
85100

86101
self.tear_down()
102+
103+
def test_stream_level_window_management(self):
104+
self.set_up()
105+
data = []
106+
send_event = threading.Event()
107+
108+
def socket_handler(listener):
109+
sock = listener.accept()[0]
110+
111+
# Dispose of the first two packets.
112+
sock.recv(65535)
113+
sock.recv(65535)
114+
115+
# Send a Settings frame that reduces the flow-control window to
116+
# 64 bytes.
117+
f = SettingsFrame(0)
118+
f.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 64
119+
sock.send(f.serialize())
120+
121+
# Grab three frames, the settings ACK, the initial headers frame,
122+
# and the first data frame.
123+
for x in range(0, 3):
124+
data.append(sock.recv(65535))
125+
126+
# Send a WindowUpdate giving more window room to the stream.
127+
f = WindowUpdateFrame(1)
128+
f.window_increment = 64
129+
sock.send(f.serialize())
130+
131+
# Reeive the remaining frame.
132+
data.append(sock.recv(65535))
133+
send_event.set()
134+
135+
# We're done.
136+
sock.close()
137+
138+
self._start_server(socket_handler)
139+
conn = HTTP20Connection(self.host, self.port)
140+
141+
conn.putrequest('GET', '/')
142+
conn.endheaders()
143+
144+
# Send the first data chunk. This is 32 bytes.
145+
sd = b'a' * 32
146+
conn.send(sd)
147+
148+
# Send the second one. This should block until the WindowUpdate comes
149+
# in.
150+
sd = sd * 2
151+
conn.send(sd, final=True)
152+
assert send_event.wait(0.3)
153+
154+
# Decode the frames.
155+
print(data)
156+
frames = [decode_frame(d) for d in data]
157+
158+
# We care about the last two. The first should be a data frame
159+
# containing 32 bytes.
160+
assert isinstance(frames[-2], DataFrame)
161+
assert len(frames[-2].data) == 32
162+
163+
# The second should be a data frame containing 64 bytes.
164+
assert isinstance(frames[-1], DataFrame)
165+
assert len(frames[-1].data) == 64
166+
167+
self.tear_down()

0 commit comments

Comments
 (0)