Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ensure http connection is closed #1332

Merged
merged 1 commit into from Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 8 additions & 15 deletions uvicorn/_handlers/http.py
Expand Up @@ -7,6 +7,9 @@
from uvicorn.server import ServerState


MAX_RECV = 2 ** 16
abersheeran marked this conversation as resolved.
Show resolved Hide resolved


async def handle_http(
reader: asyncio.StreamReader,
writer: asyncio.StreamWriter,
Expand All @@ -33,13 +36,13 @@ async def handle_http(
# Use a future to coordinate between the protocol and this handler task.
# https://docs.python.org/3/library/asyncio-protocol.html#connecting-existing-sockets
loop = asyncio.get_event_loop()
connection_lost = loop.create_future()
reader_read = asyncio.create_task(reader.read(MAX_RECV))

# Switch the protocol from the stream reader to our own HTTP protocol class.
protocol = config.http_protocol_class( # type: ignore[call-arg, operator]
config=config,
server_state=server_state,
on_connection_lost=lambda: connection_lost.set_result(True),
on_connection_lost=reader_read.cancel,
)
transport = writer.transport
transport.set_protocol(protocol)
Expand All @@ -56,7 +59,7 @@ async def handle_http(

@task.add_done_callback
def retrieve_exception(task: asyncio.Task) -> None:
exc = task.exception()
exc = task.exception() if not task.cancelled() else None

if exc is None:
return
Kludex marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -74,15 +77,5 @@ def retrieve_exception(task: asyncio.Task) -> None:

# Kick off the HTTP protocol.
protocol.connection_made(transport)

# Pass any data already in the read buffer.
# The assumption here is that we haven't read any data off the stream reader
# yet: all data that the client might have already sent since the connection has
# been established is in the `_buffer`.
data = reader._buffer # type: ignore
if data:
protocol.data_received(data)

# Let the transport run in the background. When closed, this future will complete
# and we'll exit here.
await connection_lost
data = await reader_read
protocol.data_received(data)
4 changes: 4 additions & 0 deletions uvicorn/protocols/http/h11_impl.py
Expand Up @@ -222,6 +222,10 @@ def handle_events(self):
continue
self.cycle.more_body = False
self.cycle.message_event.set()
elif event_type is h11.ConnectionClosed:
break
if self.conn.our_state is h11.MUST_CLOSE and not self.transport.is_closing():
self.transport.close()
Comment on lines +225 to +228
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine 👍


def handle_upgrade(self, event):
upgrade_value = None
Expand Down
2 changes: 2 additions & 0 deletions uvicorn/protocols/http/httptools_impl.py
Expand Up @@ -136,6 +136,8 @@ def data_received(self, data):
self.transport.close()
except httptools.HttpParserUpgrade:
self.handle_upgrade()
if data == b"" and not self.transport.is_closing():
self.transport.close()
Comment on lines +139 to +140
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine 👍


def handle_upgrade(self):
upgrade_value = None
Expand Down