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

Handle malformed HTTP request with multiple Connection headers #2002

Closed
wants to merge 1 commit into from

Conversation

zioproto
Copy link

@zioproto zioproto commented Jun 9, 2023

This PR fixes a problem with Websockets when running Uvicorn on GitHub Codespaces

Detailed discussion:
https://github.com/orgs/community/discussions/57596

Summary

Please do not merge quickly, I am not sure if this is a Uvicorn bug or a problem with the GitHub codespaces reverse proxy sending a malformed HTTP requests.

According to RFC7230 there should not be duplicated Connection headers.

A sender MUST NOT generate multiple header fields with the same field
name in a message unless either the entire field value for that
header field is defined as a comma-separated list [i.e., #(values)]
or the header field is a well-known exception (as noted below).

I am running Uvicorn in GitHub Codespaces. I am forwarding a port with visibility public and port protocol HTTP.
My first Websocket connection works, but after a few seconds the Websocket endpoint starts returning HTTP 404.

I captured traffic at the Uvicorn server with tcpdump and I figured out that when I receive a HTTP 404 the HTTP request has a duplicated Connection header.

Screenshot 2023-06-09 at 08 46 22

This is a problem and looking at the Uvicorn implementation it will make the Websockets connection fail to upgrade randomly depending on which one of the duplicated Connection header is read first.

def _get_upgrade(self) -> Optional[bytes]:
connection = []
upgrade = None
for name, value in self.headers:
if name == b"connection":
connection = [token.lower().strip() for token in value.split(b",")]
if name == b"upgrade":
upgrade = value.lower()
if b"upgrade" in connection:
return upgrade
return None

I confirm that with the patch proposed in this PR fixes the problem completely because connection will be a list with the values ['upgrade', 'keep-alive']. However it could be that the request is just malformed and Uvicorn should not implement anything to cope with malformed HTTP requests.

Please review.

Checklist

  • I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.

@Kludex
Copy link
Member

Kludex commented Jun 9, 2023

image

@zioproto
Copy link
Author

zioproto commented Jun 9, 2023

@Kludex do you think I need to update the current tests in place at tests/protocols/test_websocket.py ?

Like implementing a test:
test_websocket_upgrade_with_multiple_connection_headers ?

Do you think that is a valid HTTP Request ?

@Kludex
Copy link
Member

Kludex commented Jun 9, 2023

According to RFC7230 there should not be duplicated Connection headers.

Then we shouldn't change uvicorn. 😅

@Kludex
Copy link
Member

Kludex commented Jun 9, 2023

Does the same happens with hypercorn?

@zioproto
Copy link
Author

zioproto commented Jun 9, 2023

Does the same happens with hypercorn?

I dont know, I never used it.

This is confirmed being GitHub Codespaces sending duplicated Connection: header when port forwarding.

I will change the PR to return a proper HTTP return code in case of multiple Connection headers.

Should I just return a HTTP 400 error?
I read the 4XX client errors but I can't find a more specific one.

@Kludex
Copy link
Member

Kludex commented Jun 9, 2023

I dont know, I never used it.

It would be interesting to check the behavior of another server implementation.

I will change the PR to return a proper HTTP return code in case of multiple Connection headers.

No need. I think the current behavior is fine.

@Kludex
Copy link
Member

Kludex commented Jun 9, 2023

Since GitHub is in fault, and you got a solution for the problem, I'll be closing the PR.

Thanks for the investigation. 🙏

@Kludex Kludex closed this Jun 9, 2023
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.

2 participants