Skip to content

Commit

Permalink
Merge pull request #1361 from yunstanford/cancel-request-when-connect…
Browse files Browse the repository at this point in the history
…ion-lost

Cancel request when connection lost
  • Loading branch information
sjsadowski committed Oct 12, 2018
2 parents 176f8d1 + 3149d5a commit 4175924
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
4 changes: 4 additions & 0 deletions sanic/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ def connection_made(self, transport):

def connection_lost(self, exc):
self.connections.discard(self)
if self._request_handler_task:
self._request_handler_task.cancel()
if self._request_stream_task:
self._request_stream_task.cancel()
if self._request_timeout_handler:
self._request_timeout_handler.cancel()
if self._response_timeout_handler:
Expand Down
73 changes: 73 additions & 0 deletions tests/test_request_cancel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import pytest
import asyncio
import contextlib

from sanic.response import text, stream


async def test_request_cancel_when_connection_lost(loop, app, test_client):
app.still_serving_cancelled_request = False

@app.get('/')
async def handler(request):
await asyncio.sleep(1.0)
# at this point client is already disconnected
app.still_serving_cancelled_request = True
return text('OK')

test_cli = await test_client(app)

# schedule client call
task = loop.create_task(test_cli.get('/'))
loop.call_later(0.01, task)
await asyncio.sleep(0.5)

# cancelling request and closing connection after 0.5 sec
task.cancel()

with contextlib.suppress(asyncio.CancelledError):
await task

# Wait for server and check if it's still serving the cancelled request
await asyncio.sleep(1.0)

assert app.still_serving_cancelled_request is False


async def test_stream_request_cancel_when_connection_lost(loop, app, test_client):
app.still_serving_cancelled_request = False

@app.post('/post/<id>', stream=True)
async def post(request, id):
assert isinstance(request.stream, asyncio.Queue)

async def streaming(response):
while True:
body = await request.stream.get()
if body is None:
break
await response.write(body.decode('utf-8'))

await asyncio.sleep(1.0)
# at this point client is already disconnected
app.still_serving_cancelled_request = True

return stream(streaming)

test_cli = await test_client(app)

# schedule client call
task = loop.create_task(test_cli.post('/post/1'))
loop.call_later(0.01, task)
await asyncio.sleep(0.5)

# cancelling request and closing connection after 0.5 sec
task.cancel()

with contextlib.suppress(asyncio.CancelledError):
await task

# Wait for server and check if it's still serving the cancelled request
await asyncio.sleep(1.0)

assert app.still_serving_cancelled_request is False

0 comments on commit 4175924

Please sign in to comment.