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

Default WebSocket accept message headers to an empty list #1422

Merged
merged 5 commits into from Jan 22, 2022
Merged

Default WebSocket accept message headers to an empty list #1422

merged 5 commits into from Jan 22, 2022

Conversation

Klavionik
Copy link
Contributor

Recently, #1361 introduced a possibility to pass a set of extra headers into the accept method. If no headers are passed, the default value is None. This value then gets sent to the protocol server.

This causes Uvicorn to raise an exception: TypeError: NoneType object is not iterable when using websockets and TypeError: can only concatenate list (not "NoneType") to list when using wsproto.

This PR fixes this, setting the headers argument to an empty list if it is not passed into the method. This is recommended by the ASGI spec (https://asgi.readthedocs.io/en/latest/specs/www.html#accept-send-event).

@aminalaee
Copy link
Member

Thank you for this. I guess adding/editing the test would be valuable here so this is safe for future changes.
Maybe in the test_additional_headers test?

@Klavionik
Copy link
Contributor Author

I've added a test, but I'm really not sure how adequate it is to do it that way. :) Just let me know if it's not.

tests/test_websockets.py Outdated Show resolved Hide resolved
@Kludex
Copy link
Sponsor Member

Kludex commented Jan 21, 2022

My interpretation of the ASGI spec is that the server will interpret the absence of headers as an empty array, not the application. If my understanding is correct, I think we should send the message without headers in this case.

@aminalaee
Copy link
Member

Yes, I agree that's what it says. Wouldn't that be a breaking change? I mean that's like the case with code.

@Kludex
Copy link
Sponsor Member

Kludex commented Jan 21, 2022

No, because we didn't release it yet. This is a feature introduced a few days ago.

@aminalaee
Copy link
Member

aminalaee commented Jan 21, 2022

Oh that makes sense, I thought it was from the last release 😆

@Kludex
Copy link
Sponsor Member

Kludex commented Jan 21, 2022

Should we also change the TestClient.extra_headers default value as well? #1361 (comment)

@Klavionik
Copy link
Contributor Author

@Kludex I see your point, but I'm not really sure about it, to be honest.

My humble reflection on this topic:

why should an application care about how exactly the server will interpret the absence of headers - be it an empty list or anything else? Why should the spec tell us about that? I'm leaning towards thinking that this is an instruction for application developers so that server developers would know what to expect. And in accordance to that, right now Uvicorn expects headers to be something iterable.

Anyway, if so, shouldn't we also remove the subprotocol too, when it is None? It's a similar case, I guess.

@Kludex
Copy link
Sponsor Member

Kludex commented Jan 21, 2022

And in accordance to that, right now Uvicorn expects headers to be something iterable.

Uvicorn only expects the headers to be iterable if present.

Anyway, if so, shouldn't we also remove the subprotocol too, when it is None? It's a similar case, I guess.

But yeah, you have a point. Ok, I'm fine with this 👍

starlette/websockets.py Outdated Show resolved Hide resolved
@matiuszka
Copy link
Contributor

@Klavionik Thanks for fixing that. I completely forgot about that.

@aminalaee
Copy link
Member

aminalaee commented Jan 21, 2022

Anyway, if so, shouldn't we also remove the subprotocol too, when it is None? It's a similar case, I guess.

I think we should pick one approach and stick with it.
In some cases we do send the optional values even if they are None, like subprotocol and code.
In some other places like the reason we don't send the value if it's None (still under review).

@Kludex
Copy link
Sponsor Member

Kludex commented Jan 21, 2022

Anyway, if so, shouldn't we also remove the subprotocol too, when it is None? It's a similar case, I guess.

I think we should pick one approach and stick with it. In some cases we do send the optional values even if they are None, like subprotocol and code. In some other places like the reason we don't send the value if it's None (still under review).

We send None when it's allowed by the ASGI spec, but yeah. I agree. We should probably adapt the reason PR to stick with the others. 🤔

Klavionik and others added 4 commits January 21, 2022 18:14
@Klavionik
Copy link
Contributor Author

Klavionik commented Jan 21, 2022

I accepted all the suggestions (first time using it, hope I did it right).

@Klavionik Thanks for fixing that. I completely forgot about that.

Happy to help! I was actually tinkering with BlackSheep, trying to add support for WebSocket connections to it, when I discovered this. I was referencing Starlette and did the same thing with the accept method. Luckily I was testing it using the latest Uvicorn release - that's how I saw the server crashing on the accept call.

So maybe we should consider adding some kind of check to Uvicorn? Like assert isinstance(headers, typing.Iterable), "The 'headers' argument must be an iterable."? Just to make sure it will raise a more clear exception if someone tries to do the same thing.

tests/test_websockets.py Outdated Show resolved Hide resolved
@aminalaee aminalaee mentioned this pull request Jan 22, 2022
8 tasks
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
@Kludex
Copy link
Sponsor Member

Kludex commented Jan 22, 2022

Thanks @Klavionik ! 🎉

@Kludex Kludex merged commit 9d282a9 into encode:master Jan 22, 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

4 participants