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

Conversation

pacoyang
Copy link
Contributor

@pacoyang pacoyang commented Jan 17, 2022

Ref Issues: #1199 #1226
Ref PRs: #1192 #1244 #1307

Description

As #869 (released in 0.14.0) is missing handle the scenario of b"", it is possible to make the HTTP connection not close correctly.
Here is the memory usage plot with the current main branch, running a single tcping process for 5 minutes.
p1

The memory usage is increasing, and the netstat shows many CLOSE_WAIT connections.

p2

I read the code in #869 , I have found two issues:

  1. As we handle the connection close or not in the protocol, I think we should pass the scenario of b"" to protocol.data_received too. It is up to the protocol to determine if the connection should be closed or not.
  2. Reading the data from _buffer seems not a good way. In some scenarios, the client will initially send empty TCP pings, then _buffer will receive b"", determine if the connections should be closed or not through the _buffer data is confusing (refs from the read function).

Here is current PR code running a single tcping process for 5 minutes in the same testing environment:
p3

Test codes:

import uvicorn

async def app(scope, receive, send):
    assert scope["type"] == "http"
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [
                [b"content-type", b"text/plain"],
            ],
        }
    )
    await send(
        {
            "type": "http.response.body",
            "body": b"Hello, world!",
        }
    )


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", reload=False, log_level=5)

Copy link
Sponsor Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @pacoyang!

I've confirmed that this PR solves our issues: memory leak and CLOSE_WAIT. The logic here also makes sense to me, and I've specially liked the usage of the MUST_CLOSE state. 👍

That being said... I still think uvicorn should revert #869 . It's been almost a year and no one took the initiative to continue the work into trio compatibility.

In any case, I'll wait 10 days. If no other member gets involved on this PR, I'll approve, merge and release.

Comment on lines +225 to +228
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()
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 👍

Comment on lines +139 to +140
if data == b"" and not self.transport.is_closing():
self.transport.close()
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 👍

uvicorn/_handlers/http.py Show resolved Hide resolved
@Kludex
Copy link
Sponsor Member

Kludex commented Jan 20, 2022

@tomchristie @abersheeran @euri10 @florimondmanca friendly ping if someone wants to check this as well

@pacoyang
Copy link
Contributor Author

pacoyang commented Jan 21, 2022

That being said... I still think uvicorn should revert #869 . It's been almost a year and no one took the initiative to continue the work into trio compatibility.

@Kludex Sure, I just want to solve this issue as soon as possible, any way is acceptable. I love uvicorn, but this serious problem has forced me to choose another ASGI server. It's been a while since the discovery.

Using a streams handler can do more things like move the keep-alive timeout task out of the protocol, so we don't need to write multiple times in the protocol implementation. The keep-alive timeout task trigger still has issue, when a TCP connection is established and the client does not send any bytes, the server does not close this connection when keep-alive timeout. It can be reproduced by telnet.

@bittermandel
Copy link

Just stumbled upon this when running on fly.io with TCP health checks. Luckily I could switch over to HTTP checks to get around this, but following this to be sure it's resolved.

Copy link
Sponsor Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @pacoyang !

@Kludex Kludex merged commit fc6e056 into encode:master Jan 28, 2022
@pacoyang pacoyang deleted the fix-ensure-http-conn-closed branch January 29, 2022 02:23
Kludex added a commit that referenced this pull request Feb 3, 2022
Kludex added a commit that referenced this pull request Feb 3, 2022
Kludex added a commit that referenced this pull request Feb 3, 2022
* Revert "fix: move all data handle to protocol & ensure connection is closed (#1332)"

This reverts commit fc6e056.

* Revert stream interface
Kludex added a commit to sephioh/uvicorn that referenced this pull request Oct 29, 2022
Kludex added a commit to sephioh/uvicorn that referenced this pull request Oct 29, 2022
* Revert "fix: move all data handle to protocol & ensure connection is closed (encode#1332)"

This reverts commit fc6e056.

* Revert stream interface
pattersam pushed a commit to pattersam/ytta-app that referenced this pull request Sep 9, 2023
this is required for it to stay working with the AWS ELB

encode/uvicorn#1199
encode/uvicorn#1332

but we're still waiting for `fastapi[all]` to use the newer uvicorn: tiangolo/fastapi#4680
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants