-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add customizable WebSocket subprotocol selection #3597
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
Conversation
axum/src/extract/ws.rs
Outdated
| /// - No allocation is performed unless the predicate constructs a dynamic | ||
| /// `HeaderValue`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this here? I think we generally shouldn't make any promises about allocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 251 to 258 in 07f8312
| // FIXME: This will often allocate a new `String` and so is less efficient than it | |
| // could be. But that can't be fixed without breaking changes to the public API. | |
| .map(Into::into) | |
| .find(|protocol| { | |
| req_protocols | |
| .split(',') | |
| .any(|req_protocol| req_protocol.trim() == protocol) | |
| }) |
Actually, I tried to respond to the
FIXME in protocols(). Yet indeed we can just remove these two lines.
|
Extra reviews appreciated, because I don't know much about ws, especially this subprotocol stuff. cc @SabrinaJewson who has reviewed a lot of WS + SSE stuff before. Also @Turbo87 maybe? |
|
Hmmm. I can’t help but wonder if we should just offer an explicit iterator over the request’s protocols together with a setter for the chosen protocol header, in case for example the token authentication logic is async or fallible? e.g. pub fn requested_protocols(&self) -> impl Iterator<Item = &str> {
self.sec_websocket_protocol
.as_ref()
.and_then(|p| p.to_str().ok())
.into_iter()
.flat_map(|s| s.split(','))
.map(|s| s.trim())
}
pub fn set_protocol(&mut self, protocol: HeaderValue) {
self.protocol = Some(protocol);
} |
Thanks for your time. Indeed this would be much easier to understand.
Should I close this PR and open a new one, or just force-push on this PR? |
|
Force-push is fine. |
I have force-pushed the code, with updated doc comments. And the use example in this PR has been correspondingly updated. Could you please have a review of that? |
|
@SabrinaJewson Could you please have a quick review of this? Following your insightful opinion, I added a getter and setter (as you wrote in the comment) in the current PR. These two methods are really useful for a project of mine, |
|
It looks good to me. Minor comment: I know I suggested the name originally, but since the getter is called |
98ddc9a to
d92a7d9
Compare
Happy new year! I have changed the name for the setter in the latest commit. |
This adds a getter for the Sec-WebSocket-Protocol requested by the client and a setter for the chosen subprotocol, which enables more flexible subprotocol selection logic, such as token-encoded subprotocols or runtime-info-based selection. Token-encoded subprotocols are a common practice for authentication in WebSocket. Although this is not a standard usage, it is used in practice, for example by Kubernetes: kubernetes/kubernetes@714f97d
|
@SabrinaJewson Could you please tell me what further modifications are needed for this PR to be merged? |
|
Sorry – to be clear I’m not a maintainer, so you’ll have to page @jplatte. |
| pub fn requested_protocols(&self) -> impl Iterator<Item = &str> { | ||
| self.sec_websocket_protocol | ||
| .as_ref() | ||
| .and_then(|p| p.to_str().ok()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of just returning an empty iterator when the header value is non-utf8, but I guess it can be fixed later (seems extremely unlikely to happen in practice unless the client is actively testing edge case behavior).
Motivation
Token-encoded subprotocol is a common practice for authentication in websocket, and though not a standard usage, it is even used by kubernetes,
However, current implementation of axum does not support read such ws subprotocol.
Solution
This adds a getter for the Sec-WebSocket-Protocol requested by the
client and a setter for the chosen subprotocol.
This enables more flexible selection logic (e.g. Token-encoded subprotocols or runtime selection) and avoids unnecessary allocations present in the existing protocols() helper.
And, in the use case of k8s, where the client init the ws connection as:
example usage could be: