Skip to content

Commit ab1cbc9

Browse files
committed
Merge pull request #173 from KeepSafe/client_connector_refactoring
Client connector refactoring
2 parents 7ab52b0 + 18a281e commit ab1cbc9

File tree

4 files changed

+117
-11
lines changed

4 files changed

+117
-11
lines changed

aiohttp/connector.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ def loop(self):
3535

3636
def close(self):
3737
if self._transport is not None:
38-
self._transport.close()
38+
self._connector._release(
39+
self._key, self._request, self._transport, self._protocol,
40+
should_close=True)
3941
self._transport = None
4042
self._wr = None
4143

@@ -173,21 +175,22 @@ def _get(self, key):
173175

174176
return None, None
175177

176-
def _release(self, key, req, transport, protocol):
178+
def _release(self, key, req, transport, protocol, *, should_close=False):
177179
resp = req.response
178-
should_close = False
179180

180-
if resp is not None:
181-
if resp.message is None:
182-
should_close = True
183-
else:
184-
should_close = resp.message.should_close
181+
if not should_close:
182+
if resp is not None:
183+
if resp.message is None:
184+
should_close = True
185+
else:
186+
should_close = resp.message.should_close
185187

186-
if self._force_close:
187-
should_close = True
188+
if self._force_close:
189+
should_close = True
188190

189191
reader = protocol.reader
190192
if should_close or (reader.output and not reader.output.at_eof()):
193+
self._conns.pop(key, None)
191194
transport.close()
192195
else:
193196
conns = self._conns.get(key)

aiohttp/protocol.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ class HttpResponseParser(HttpParser):
204204
Returns RawResponseMessage"""
205205

206206
def __call__(self, out, buf):
207+
try:
208+
yield from buf.wait(1)
209+
except aiohttp.EofStream:
210+
raise errors.ClientConnectionError(
211+
'Connection closed by server') from None
207212
try:
208213
# read http message (response line + headers)
209214
try:

tests/test_client_functional.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,102 @@ def go(url):
10201020
url = httpd.url('keepalive')
10211021
self.loop.run_until_complete(go(url))
10221022

1023+
def test_server_close_keepalive_connection(self):
1024+
1025+
class Proto(asyncio.Protocol):
1026+
1027+
def connection_made(self, transport):
1028+
self.transp = transport
1029+
self.data = b''
1030+
1031+
def data_received(self, data):
1032+
self.data += data
1033+
if data.endswith(b'\r\n\r\n'):
1034+
self.transp.write(
1035+
b'HTTP/1.1 200 OK\r\n'
1036+
b'CONTENT-LENGTH: 2\r\n'
1037+
b'CONNECTION: close\r\n'
1038+
b'\r\n'
1039+
b'ok')
1040+
self.transp.close()
1041+
1042+
def connection_lost(self, exc):
1043+
self.transp = None
1044+
1045+
@asyncio.coroutine
1046+
def go():
1047+
server = yield from self.loop.create_server(
1048+
Proto, '127.0.0.1')
1049+
1050+
addr = server.sockets[0].getsockname()
1051+
1052+
connector = aiohttp.TCPConnector(loop=self.loop)
1053+
1054+
url = 'http://{}:{}/'.format(*addr)
1055+
for i in range(2):
1056+
r = yield from client.request('GET', url,
1057+
connector=connector,
1058+
loop=self.loop)
1059+
yield from r.read()
1060+
self.assertEqual(0, len(connector._conns))
1061+
connector.close()
1062+
server.close()
1063+
yield from server.wait_closed()
1064+
1065+
self.loop.run_until_complete(go())
1066+
1067+
def test_handle_keepalive_on_closed_connection(self):
1068+
1069+
class Proto(asyncio.Protocol):
1070+
1071+
def connection_made(self, transport):
1072+
self.transp = transport
1073+
self.data = b''
1074+
1075+
def data_received(self, data):
1076+
self.data += data
1077+
if data.endswith(b'\r\n\r\n'):
1078+
self.transp.write(
1079+
b'HTTP/1.1 200 OK\r\n'
1080+
b'CONTENT-LENGTH: 2\r\n'
1081+
b'\r\n'
1082+
b'ok')
1083+
self.transp.close()
1084+
1085+
def connection_lost(self, exc):
1086+
self.transp = None
1087+
1088+
@asyncio.coroutine
1089+
def go():
1090+
server = yield from self.loop.create_server(
1091+
Proto, '127.0.0.1')
1092+
1093+
addr = server.sockets[0].getsockname()
1094+
1095+
connector = aiohttp.TCPConnector(loop=self.loop)
1096+
1097+
url = 'http://{}:{}/'.format(*addr)
1098+
1099+
r = yield from client.request('GET', url,
1100+
connector=connector,
1101+
loop=self.loop)
1102+
yield from r.read()
1103+
self.assertEqual(1, len(connector._conns))
1104+
1105+
with self.assertRaisesRegex(
1106+
aiohttp.ClientConnectionError,
1107+
'Connection closed by server'):
1108+
yield from client.request('GET', url,
1109+
connector=connector,
1110+
loop=self.loop)
1111+
self.assertEqual(0, len(connector._conns))
1112+
1113+
connector.close()
1114+
server.close()
1115+
yield from server.wait_closed()
1116+
1117+
self.loop.run_until_complete(go())
1118+
10231119

10241120
class Functional(test_utils.Router):
10251121

tests/test_connector.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ def test_close(self):
3939
self.connector, self.key, self.request,
4040
self.transport, self.protocol, self.loop)
4141
conn.close()
42-
self.assertTrue(self.transport.close.called)
4342
self.assertIsNone(conn._transport)
43+
self.connector._release.assert_called_with(
44+
self.key, self.request, self.transport, self.protocol,
45+
should_close=True)
4446

4547
def test_release(self):
4648
conn = Connection(

0 commit comments

Comments
 (0)