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

Correctly handle Upgrade-requests for HTTP/2 by ignoring them #1642

Closed
wants to merge 7 commits into from

Conversation

hajs
Copy link

@hajs hajs commented Sep 11, 2022

(Fix for #1501)

MDN (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade) says:

The server can choose to ignore the request, for any reason, in which case it should just respond as though the Upgrade header had not been sent (for example, with a 200 OK).

The previous implementation could only upgrade requests for WebSockets and generated errors for "Upgrade: h2c".

Example for uvicorn example:app:

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',

With this patch the server will send the correct result:

$ curl -v --http2 localhost:8000
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQCAAAAAAIAAAAA
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Sun, 11 Sep 2022 21:14:15 GMT
< server: uvicorn
< content-type: text/plain
< transfer-encoding: chunked
<
* Connection #0 to host localhost left intact

Previous behaviour:

$ curl -v --http2 localhost:8000
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQCAAAAAAIAAAAA
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< content-type: text/plain; charset=utf-8
< connection: close
< Transfer-Encoding: chunked
<
* Closing connection 0
Unsupported upgrade request.

@Kludex Kludex added this to the Version 0.19.0 milestone Sep 12, 2022
@Kludex Kludex mentioned this pull request Sep 12, 2022
5 tasks
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.

If we look for should_upgrade on the httptools implementation, why is the same logic not needed on on_body and on_message_complete?

Comment on lines 249 to 257
upgrade_http2 = False
for name, value in self.headers:
if name == b"upgrade":
upgrade_value = value.lower()
if upgrade_value == b"h2c":
upgrade_http2 = True
break
if not upgrade_http2:
return
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

We can add a self.is_http2, and set as True on the on_header method.

Copy link
Author

Choose a reason for hiding this comment

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

Oh indeed, the code looks like, we should also replace the self.parser.should_upgrade() in on_body and on_message_complete with (self.parser.should_upgrade() and not self.is_http2).
Sorry, I had overlooked that because no unit test failed for this edge case.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

We can probably benefit from more tests that consider those conditionals 🙌

@Kludex Kludex added the waiting author Waiting for author's reply label Sep 17, 2022
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.

The drop on coverage threshold shouldn't be needed if we add the necessary tests here. Would you like to work on those, @hajs ?

@Kludex Kludex removed the waiting author Waiting for author's reply label Sep 20, 2022
@hajs
Copy link
Author

hajs commented Sep 20, 2022

Yeah this is normally not a good idea. The coverage declined only after re-formatting this commit 0cffedc into multiple lines.

I am not sure how to test for different request-response-cycles. Do you have any idea?

@Kludex
Copy link
Sponsor Member

Kludex commented Sep 29, 2022

Thanks for the PR @hajs! 🙏

I'll be closing this, and continue on #1661, as it's the changes are a superset of these here.

@Kludex Kludex closed this Sep 29, 2022
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

2 participants