Skip to content

Add support for Sec-WebSocket-Protocol to HTTP::WebSocketHandler#16574

Merged
straight-shoota merged 4 commits intocrystal-lang:masterfrom
antondalgren:sec-websocket-protocol
Feb 25, 2026
Merged

Add support for Sec-WebSocket-Protocol to HTTP::WebSocketHandler#16574
straight-shoota merged 4 commits intocrystal-lang:masterfrom
antondalgren:sec-websocket-protocol

Conversation

@antondalgren
Copy link
Contributor

Closes #15547

@straight-shoota straight-shoota changed the title Adds Sec-WebSocket-Protocol support to HTTP::WebSocketHandler Add support for Sec-WebSocket-Protocol to HTTP::WebSocketHandler Jan 21, 2026
Comment on lines 69 to 78
requested_protocols = request.headers["Sec-WebSocket-Protocol"]?
return nil unless requested_protocols
if supported_protocols = @protocols
requested_protocols.split(",").each do |protocol|
protocol = protocol.strip
if supported_protocols.includes? protocol
return protocol
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: prefer early returns.

Suggested change
requested_protocols = request.headers["Sec-WebSocket-Protocol"]?
return nil unless requested_protocols
if supported_protocols = @protocols
requested_protocols.split(",").each do |protocol|
protocol = protocol.strip
if supported_protocols.includes? protocol
return protocol
end
end
end
return unless requested_protocols = request.headers["Sec-WebSocket-Protocol"]?
return unless supported_protocols = @protocols
requested_protocols.split(',').each do |protocol|
protocol = protocol.strip
return protocol if protocol.in?(supported_protocols)
end

@spuun
Copy link
Contributor

spuun commented Jan 22, 2026

I'd like to see the possibility to have regex in the supported protocol list. Sometimes you'd like to "catch all", like ^mqtt.

I have a feeling that even though we have the IANA WebSocket Subprotocol Name Registry not all implementations will follow this strictly. E.g. a client will send mqtt for mqtt 5, another will send mqtt for mqtt 3.1.1 and a third will send mqtt5 for mqtt 5`.

If I can configure the handler to accept any matching ^mqtt and the response header will be set to the request header, it's up the sub-protocol to handle the version check.

@straight-shoota
Copy link
Member

@spuun I'm not sure about this... It seems like a relatively simple addition. But then it also adds a bit of complexity and I find it hard to assess. Maybe we could defer this to a follow-up discussion?

Websocket subprotocol matching is intentionally very simple. Adding regex to that makes it carry quite a bit more weight and seems overpowered. It may also open opportunities for DoS.

Potential alternatives would be to allow specifying a custom handler for subprotocol matching. That would give users full flexibility to implement matching however they want. It might be even more overpowered, though.
Instead of full regex support, it seems your described use case could be met with prefix matching. Maybe we could only implement that instead of full regex support to keep things simpler?

@ysbaddaden
Copy link
Collaborator

ysbaddaden commented Jan 22, 2026

A customizable subprotocol handler proc actually sounds great 🤔
But let's see that in a follow up.

antondalgren added a commit to antondalgren/crystal that referenced this pull request Jan 23, 2026
I decided to go with #protocol and protocols instead of subprotocols as
was selected in crystal-lang#16574. The decision was based on the JavaScript WebSocket API
which uses #protocol to read the negotiated subprotocol, if any.

Part of crystal-lang#15574
spuun added a commit to cloudamqp/lavinmq that referenced this pull request Jan 23, 2026
…er (#1637)

This fixes some parts that #1621 missed.

The specs added in #1621 did only verify that the websocket handler
picked the right client handler based on `Sec-WebSocket-Protocol`. What
they didn't verify was if `Sec-WebSocket-Protocol` was sent back to the
client.

Because of how Crystal's `HTTP::WebSocketHandler` and
`HTTP::Server:Response#upgrade` works I just copied
`HTTP::WebSocketHandler` to make my own fixed version.
crystal-lang/crystal#16574 would fix this in the
stdlib.
viktorerlingsson pushed a commit to cloudamqp/lavinmq that referenced this pull request Jan 28, 2026
…er (#1637)

This fixes some parts that #1621 missed.

The specs added in #1621 did only verify that the websocket handler
picked the right client handler based on `Sec-WebSocket-Protocol`. What
they didn't verify was if `Sec-WebSocket-Protocol` was sent back to the
client.

Because of how Crystal's `HTTP::WebSocketHandler` and
`HTTP::Server:Response#upgrade` works I just copied
`HTTP::WebSocketHandler` to make my own fixed version.
crystal-lang/crystal#16574 would fix this in the
stdlib.
kickster97 pushed a commit to cloudamqp/lavinmq that referenced this pull request Feb 6, 2026
…er (#1637)

This fixes some parts that #1621 missed.

The specs added in #1621 did only verify that the websocket handler
picked the right client handler based on `Sec-WebSocket-Protocol`. What
they didn't verify was if `Sec-WebSocket-Protocol` was sent back to the
client.

Because of how Crystal's `HTTP::WebSocketHandler` and
`HTTP::Server:Response#upgrade` works I just copied
`HTTP::WebSocketHandler` to make my own fixed version.
crystal-lang/crystal#16574 would fix this in the
stdlib.
@ysbaddaden ysbaddaden added this to the 1.20.0 milestone Feb 19, 2026
Comment on lines +73 to +75
if supported_protocols.includes? protocol
return protocol
end
Copy link
Contributor

@Sija Sija Feb 19, 2026

Choose a reason for hiding this comment

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

Suggestion: simplification

Suggested change
if supported_protocols.includes? protocol
return protocol
end
return protocol if protocol.in?(supported_protocols)

@straight-shoota straight-shoota merged commit 5aeb807 into crystal-lang:master Feb 25, 2026
43 of 44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP::WebSocketHandler does not support/manages WebSocket sub-protocols

6 participants