websockets icon indicating copy to clipboard operation
websockets copied to clipboard

Server header gets added by default - No provision to disable.

Open iudeen opened this issue 3 years ago • 2 comments

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?

iudeen avatar Jul 15 '22 06:07 iudeen

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.

Kludex avatar Jul 15 '22 08:07 Kludex

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.

aaugustin avatar Jul 15 '22 08:07 aaugustin

I didn't ask, but is this feature wanted? If so, can I help?

Kludex avatar Aug 12 '22 16:08 Kludex

Yes, there should be a way to disable the User-Agent header -- probably with yet another keyword argument for connect, serve, WebSocketClientProtocol, and WebSocketServerProtocol.

aaugustin avatar Aug 15 '22 09:08 aaugustin

Thanks for the instructions @aaugustin 🙏

@iudeen do you wish to work on this, or should I?

Kludex avatar Aug 15 '22 09:08 Kludex

You can work on this :)!

I can help, if needed!

iudeen avatar Aug 15 '22 09:08 iudeen

Do you want me to? @Kludex

iudeen avatar Aug 15 '22 10:08 iudeen

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_headers is 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 = False and if 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_protocol argument
  • 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?

aaugustin avatar Aug 16 '22 06:08 aaugustin

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!

iudeen avatar Aug 16 '22 06:08 iudeen

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.

aaugustin avatar Aug 16 '22 06:08 aaugustin

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.

iudeen avatar Aug 16 '22 06:08 iudeen

Cool - go as far as you're interested and let me know when I can finalize and merge :-)

aaugustin avatar Aug 16 '22 07:08 aaugustin

@aaugustin added tests. I hope I did it the right way.

iudeen avatar Aug 16 '22 07:08 iudeen

Still needs a test for the non-101 response path + docs.

aaugustin avatar Aug 16 '22 11:08 aaugustin

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.

iudeen avatar Aug 16 '22 11:08 iudeen

Thanks @iudeen and @aaugustin 🙏

Kludex avatar Aug 17 '22 08:08 Kludex

Thanks @aaugustin and @Kludex for all the help!

iudeen avatar Aug 17 '22 08:08 iudeen