Skip to content

Commit

Permalink
Close a connection if an unexpected exception occurs while sending a …
Browse files Browse the repository at this point in the history
…request (#2828)
  • Loading branch information
brycedrennan authored and asvetlov committed Mar 14, 2018
1 parent d2961fc commit 4534021
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 4 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Expand Up @@ -14,6 +14,8 @@
.cache
.coverage
.coverage.*
.direnv
.envrc
.idea
.installed.cfg
.noseids
Expand Down Expand Up @@ -48,4 +50,4 @@ virtualenv.py
.flake
.python-version
.pytest_cache
.vscode
.vscode
1 change: 1 addition & 0 deletions CHANGES/2827.bugfix
@@ -0,0 +1 @@
Close a connection if an unexpected exception occurs while sending a request
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Expand Up @@ -42,6 +42,7 @@ Boyi Chen
Brett Cannon
Brian C. Lane
Brian Muller
Bryce Drennan
Carl George
Cecile Tonglet
Chien-Wei Huang
Expand Down
9 changes: 6 additions & 3 deletions aiohttp/client.py
Expand Up @@ -326,11 +326,14 @@ async def _request(self, method, url, *,
tcp_nodelay(conn.transport, True)
tcp_cork(conn.transport, False)
try:
resp = await req.send(conn)
try:
await resp.start(conn, read_until_eof)
resp = await req.send(conn)
try:
await resp.start(conn, read_until_eof)
except BaseException:
resp.close()
raise
except BaseException:
resp.close()
conn.close()
raise
except ClientError:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_client_session.py
Expand Up @@ -371,6 +371,7 @@ async def create_connection(req, traces=None):
# return self.transport, self.protocol
return mock.Mock()
session._connector._create_connection = create_connection
session._connector._release = mock.Mock()

with pytest.raises(aiohttp.ClientOSError) as ctx:
await session.request('get', 'http://example.com')
Expand All @@ -379,6 +380,43 @@ async def create_connection(req, traces=None):
assert e.strerror == err.strerror


async def test_close_conn_on_error(create_session):
class UnexpectedException(BaseException):
pass

err = UnexpectedException("permission error")
req = mock.Mock()
req_factory = mock.Mock(return_value=req)
req.send = mock.Mock(side_effect=err)
session = create_session(request_class=req_factory)

connections = []
original_connect = session._connector.connect

async def connect(req, traces=None):
conn = await original_connect(req, traces=traces)
connections.append(conn)
return conn

async def create_connection(req, traces=None):
# return self.transport, self.protocol
conn = mock.Mock()
return conn

session._connector.connect = connect
session._connector._create_connection = create_connection
session._connector._release = mock.Mock()

with pytest.raises(UnexpectedException):
async with session.request('get', 'http://example.com') as resp:
await resp.text()

# normally called during garbage collection. triggers an exception
# if the connection wasn't already closed
for c in connections:
c.__del__()


async def test_cookie_jar_usage(loop, aiohttp_client):
req_url = None

Expand Down

0 comments on commit 4534021

Please sign in to comment.