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

Support the WebSocket Denial Response ASGI extension #1916

Merged
merged 23 commits into from
Dec 17, 2023

Conversation

kristjanvalur
Copy link
Contributor

@kristjanvalur kristjanvalur commented Mar 29, 2023

uvicorn now can return a response from websocket apps that choose to reject a connection with a custom response.
See https://github.com/encode/starlette/pull/2041/files for related work.

This is a re-submission of pr #1907

@Kludex
Copy link
Member

Kludex commented Mar 29, 2023

still something broken with the GHA. I'll leave this until we sort out what's going on.

It looks like it's related to this PR. I just cancelled the two previous jobs.

Comment on lines 1128 to 1172
if ws_protocol_cls == WSProtocol:
# wsproto automatically makes the message chunked
assert response.headers["transfer-encoding"] == "chunked"
else:
# websockets automatically adds a content-length
assert response.headers["content-length"] == "8"
Copy link
Member

Choose a reason for hiding this comment

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

It makes sense for WebSockets to add the content-length, since we can't have a chunked one there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well, this is only about what happens if the app doesn't provide a content-length and only provides the raw body data. the defaults for these two protocols are different. Technically, you can provide chunked data over the websockets: The app, provides the "chunked" header, and then provides the chunks, with the appropriate chunk headers (as an application should do if it provides that header (I think). I think that nowadays it is rare for an application to specify content length or transfer encoding...

@Kludex
Copy link
Member

Kludex commented Mar 29, 2023

Interesting... I've just found this:

The protocol server must not start sending the response to the client until it has received at least one Response Body event.

Ref.: https://asgi.readthedocs.io/en/latest/specs/www.html#response-start-send-event

Meaning that we shouldn't be sending the 404 if nobody is provided... But that also means that we have an issue with the HTTP implementations 🤔

@kristjanvalur
Copy link
Contributor Author

Meaning that we shouldn't be sending the 404 if nobody is provided... But that also means that we have an issue with the HTTP implementations 🤔

Yes, h11 definitely starts sending the header before receiving the first body.

This can be fixed, I can fix both h11 and websockets to wait for the first body... I can do it for websockets in this PR and then a followup one for h11_impl.py

@kristjanvalur
Copy link
Contributor Author

It looks like it's related to this PR. I just cancelled the two previous jobs.

Well, I haven't touched any of the .github/ code, and this is what the actions log says:
GitHub Actions has encountered an internal error when running your job.
The actions don't even start. I have never seen this before.

@kristjanvalur kristjanvalur marked this pull request as ready for review March 30, 2023 23:04
@kristjanvalur
Copy link
Contributor Author

seems things are running smoothly again. there was hanging test due to an introduced bug, but that should not have caused these weird symptoms. Now fixed.

@paulo-raca
Copy link

Awesome, thanks!

@kristjanvalur
Copy link
Contributor Author

I guess there are some changes upstream. Do you want me to update this PR?

@Kludex
Copy link
Member

Kludex commented Dec 2, 2023

Yes. I'll get to this tomorrow.

@Kludex Kludex changed the title Support the websocket.http.response ASGI extension Support the WebSocket Denial Response ASGI extension Dec 17, 2023
Copy link
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.

@kristjanvalur Please check my changes, and if we are fine, let's merge this.

Comment on lines 325 to 319
# Create the event here but do not send it, the ASGI spec
# suggest that we wait for the body event before sending.
# https://asgi.readthedocs.io/en/latest/specs/www.html#response-start-send-event
self.reject_event = event
Copy link
Member

Choose a reason for hiding this comment

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

We already discussed this: django/asgiref#387

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know. I disagree, I think the recommendation is a good one because it enlarges the window during which the server can still send a 500 error in case the application messes up. It is not a requirement but (IMHO) good practice in a library, and that is the reason I wrote the code that way.
But it is your call of course, happy to get this merged either way.

Copy link
Member

Choose a reason for hiding this comment

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

I understand, and it makes sense... But I'll rather not have a mismatch in how the HTTP implementations behave, and the WebSockets.

uvicorn/protocols/websockets/wsproto_impl.py Outdated Show resolved Hide resolved
@@ -199,7 +199,7 @@ class WebSocketResponseStartEvent(TypedDict):
class WebSocketResponseBodyEvent(TypedDict):
type: Literal["websocket.http.response.body"]
body: bytes
more_body: bool
more_body: NotRequired[bool]
Copy link
Member

Choose a reason for hiding this comment

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

This is more correct.

Copy link
Contributor Author

@kristjanvalur kristjanvalur Dec 17, 2023

Choose a reason for hiding this comment

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

If you say so :), We didn't have those fancy type decorations in the old days.

@Kludex Kludex merged commit 6568184 into encode:master Dec 17, 2023
15 checks passed
@kristjanvalur kristjanvalur deleted the kristjan/ext branch December 18, 2023 09:14
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.

3 participants