websockets
websockets copied to clipboard
Server header gets added by default - No provision to disable.
Websocket Library adds default header a Server: Python/3.10 websockets/10.3 when no server header is passed.
There is no API as such to not add this header. Is it possible to add one?
Just to give more context...
Uvicorn by default adds the "Server: uvicorn" headers. We also have a flag --no-server-headers which disables those headers.
websockets adds their own server headers, which is fine for Uvicorn. But there's no way to not add those headers using websockets.
Indeed there's no convenient way to disable it right now.
The best workaround that I can offer right now is:
import websockets.http
websockets.http.USER_AGENT = "<whatever you want>"
This won't remove the header entirely but at least it will hide that you're using websockets.
I didn't ask, but is this feature wanted? If so, can I help?
Yes, there should be a way to disable the User-Agent header -- probably with yet another keyword argument for connect, serve, WebSocketClientProtocol, and WebSocketServerProtocol.
Thanks for the instructions @aaugustin 🙏
@iudeen do you wish to work on this, or should I?
You can work on this :)!
I can help, if needed!
Do you want me to? @Kludex
Thanks for submitting the pull request! This gave me a solid basis for thinking about the best API here. (I care a lot about API design in websockets, sorry.)
Let's say that your proposal is option 1 and look at possible alternatives for the serve() API.
Option 2 (rejected)
I considered overloading the extra_headers argument i.e. tell users who want to remove the Server header to set extra_headers={"Server": None}.
Pros:
- avoids adding a keyword argument
- at least one user who faced this issue before tried this intuitively
Cons:
- requires changing the type of headers values to
Optional[str], which I really don't want to do extra_headersis about adding headers; using it to remove headers would be confusing
Option 3 (good)
Then I considered:
server_header: Optional[str] = USER_AGENT
The semantic would be "set it to None to disable the Server header entirely". This one is pretty straightforward and has two benefits over Option 1:
- it provides an obvious way to change the header, rather than just remove it
- it avoids double negations like
no_server_header: bool = Falseandif not self.no_server_header
Same with user_agent_header on the client side for the User-Agent header.
Option 4 and 4bis (possible)
We could document other locations for the server_header value:
- a class attribute on the protocol class; then you'd have to subclass the protocol and pass it with the
create_protocolargument - a module-level value in the library; then you could monkey-patch it to change it
And we'd just change the code to:
if USER_AGENT is not None:
headers["Server"] = USER_AGENT
to support removing it by setting the value to None.
After reviewing all these options, it seems to me that option 3 provides the best trade-off of convenience, simplicity, and supported use cases. What do you think?
Thanks for multiple options! I like Option 3 as well, for the same set of reasons you have mentioned. It's more intuitive to the users, and much cleaner!
Let's do that, then.
If you want to update your PR and maybe take a stab at tests and docs, that's useful. Else, I can take it from there. Let me know!
Quick tip for tests — tests for the legacy implementation are a nightmare :-/ test_protocol_custom_request_user_agent and test_protocol_custom_response_user_agent are good starting points for adding tests.
I have pushed few commits incorporating your suggestions, please check if its okay.
I can try to look into tests later today, I will let you know if I am able to do.
Cool - go as far as you're interested and let me know when I can finalize and merge :-)
@aaugustin added tests. I hope I did it the right way.
Still needs a test for the non-101 response path + docs.
I will try to add docs later. I was not able to add non-101 tests for legacy. (would be great if you could guide me there).
I added for standard implementations.
Thanks @iudeen and @aaugustin 🙏
Thanks @aaugustin and @Kludex for all the help!