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

WebSocket subprotocol support #64

Closed
cgmartin opened this Issue Jun 25, 2013 · 6 comments

Comments

Projects
None yet
3 participants
@cgmartin

cgmartin commented Jun 25, 2013

Is there a way currently to specify a WebSocket subprotocol during the connection handshake? I would like to define a list of subprotocols that can be sent back in a Sec-WebSocket-Protocol header:

http://tools.ietf.org/html/rfc6455#section-1.3

Additional header fields are used to select options in the WebSocket
Protocol.  Typical options available in this version are the
subprotocol selector (|Sec-WebSocket-Protocol|), list of extensions
support by the client (|Sec-WebSocket-Extensions|), |Origin| header
field, etc.  The |Sec-WebSocket-Protocol| request-header field can be
used to indicate what subprotocols (application-level protocols
layered over the WebSocket Protocol) are acceptable to the client.
The server selects one or none of the acceptable protocols and echoes
that value in its handshake to indicate that it has selected that
protocol.

Sec-WebSocket-Protocol: chat

Also to note that might be of interest: I've created a HTTP Kit websocket handler for the WAMP subprotocol at http://cljwamp.us and I've been really been enjoying using HTTP Kit - thanks for sharing it!

@shenfeng

This comment has been minimized.

Show comment
Hide comment
@shenfeng

shenfeng Jun 26, 2013

Member

Hi, currently, with-channel does not allow send the handshake aromatically, and does not allow customization.

If you want to, you can look at the implementation detail:
https://github.com/http-kit/http-kit/blob/master/src/org/httpkit/server.clj#L88

Here is an example

(defn handler [request]
  (let [^AsyncChannel channl (:async-channel request)]
    (if (:websocket? ~request)
      (if-let [key# (get-in ~request [:headers "sec-websocket-key"])]
        (do (.sendHandshake channl
                            {"Upgrade"    "websocket"
                             "Connection" "Upgrade"
                             "X-Your-other-header" "value"
                             "Sec-WebSocket-Accept" (accept key#)})
            ;; DO what is required, like save the channel
            ;; return the channel is required
            channl
            )))))

What do you think?
If there is a way to allow customize the handeshake, and does not complicate the API, I will be more than glad to add it.

Member

shenfeng commented Jun 26, 2013

Hi, currently, with-channel does not allow send the handshake aromatically, and does not allow customization.

If you want to, you can look at the implementation detail:
https://github.com/http-kit/http-kit/blob/master/src/org/httpkit/server.clj#L88

Here is an example

(defn handler [request]
  (let [^AsyncChannel channl (:async-channel request)]
    (if (:websocket? ~request)
      (if-let [key# (get-in ~request [:headers "sec-websocket-key"])]
        (do (.sendHandshake channl
                            {"Upgrade"    "websocket"
                             "Connection" "Upgrade"
                             "X-Your-other-header" "value"
                             "Sec-WebSocket-Accept" (accept key#)})
            ;; DO what is required, like save the channel
            ;; return the channel is required
            channl
            )))))

What do you think?
If there is a way to allow customize the handeshake, and does not complicate the API, I will be more than glad to add it.

@cgmartin

This comment has been minimized.

Show comment
Hide comment
@cgmartin

cgmartin Jun 26, 2013

Thanks for the overview!
My first thoughts were around making a custom with-channel macro in my application and merging a custom header map during .sendHandshake, ie:

(defmacro with-channel-and-headers [request ch-name handshake-headers & body]
;;...
  (do (.sendHandshake channl
        (merge handshake-headers
          {"Upgrade"    "websocket"
           "Connection" "Upgrade"
           "X-Your-other-header" "value"
           "Sec-WebSocket-Accept" (accept key#)}))
;;...

But now that I'm reading the WebSocket rfc6455 spec, there may be some deeper logic required during .sendHandshake:

If the response includes a |Sec-WebSocket-Protocol| header field
and this header field indicates the use of a subprotocol that was
not present in the client's handshake (the server has indicated a
subprotocol not requested by the client), the client MUST _Fail
the WebSocket Connection_.

This implies some comparison is done with the client's request Sec-WebSocket-Protocol header?

I'll do some more research into this protocol handshaking and look through the Java .sendHandshake code to orient myself. Thanks again!

cgmartin commented Jun 26, 2013

Thanks for the overview!
My first thoughts were around making a custom with-channel macro in my application and merging a custom header map during .sendHandshake, ie:

(defmacro with-channel-and-headers [request ch-name handshake-headers & body]
;;...
  (do (.sendHandshake channl
        (merge handshake-headers
          {"Upgrade"    "websocket"
           "Connection" "Upgrade"
           "X-Your-other-header" "value"
           "Sec-WebSocket-Accept" (accept key#)}))
;;...

But now that I'm reading the WebSocket rfc6455 spec, there may be some deeper logic required during .sendHandshake:

If the response includes a |Sec-WebSocket-Protocol| header field
and this header field indicates the use of a subprotocol that was
not present in the client's handshake (the server has indicated a
subprotocol not requested by the client), the client MUST _Fail
the WebSocket Connection_.

This implies some comparison is done with the client's request Sec-WebSocket-Protocol header?

I'll do some more research into this protocol handshaking and look through the Java .sendHandshake code to orient myself. Thanks again!

@cgmartin cgmartin closed this Jun 26, 2013

@cgmartin cgmartin reopened this Jun 26, 2013

@cgmartin

This comment has been minimized.

Show comment
Hide comment
@cgmartin

cgmartin Jun 26, 2013

I have a pretty good idea now how to do this with a custom with-channel macro. I'll get it working in my app first and then share it here. Going to close this one since I don't have any other questions. Thanks again for the pointers.

cgmartin commented Jun 26, 2013

I have a pretty good idea now how to do this with a custom with-channel macro. I'll get it working in my app first and then share it here. Going to close this one since I don't have any other questions. Thanks again for the pointers.

@cgmartin cgmartin closed this Jun 26, 2013

@cgmartin

This comment has been minimized.

Show comment
Hide comment
@cgmartin

cgmartin Jun 27, 2013

In case it's of any interest, here is what I did to validate the websocket subprotocol (and origin header) during the handshake:
https://gist.github.com/cgmartin/5880732

The current with-channel is great for most cases I think (and flexible to support the long-polling fallback). I'm not sure if you'd like this one in http-kit proper, but you're more than welcome to use it.

Cheers!

cgmartin commented Jun 27, 2013

In case it's of any interest, here is what I did to validate the websocket subprotocol (and origin header) during the handshake:
https://gist.github.com/cgmartin/5880732

The current with-channel is great for most cases I think (and flexible to support the long-polling fallback). I'm not sure if you'd like this one in http-kit proper, but you're more than welcome to use it.

Cheers!

@shenfeng

This comment has been minimized.

Show comment
Hide comment
@shenfeng

shenfeng Jun 27, 2013

Member

I add it to the documentation: http://http-kit.org/server.html#handshake

Member

shenfeng commented Jun 27, 2013

I add it to the documentation: http://http-kit.org/server.html#handshake

@brandonbloom

This comment has been minimized.

Show comment
Hide comment
@brandonbloom

brandonbloom Dec 21, 2016

I came across another use-case for customizing the handshake: Sending a "Set-Cookie" header. Would be nice to have some way to customize the handshake headers.

brandonbloom commented Dec 21, 2016

I came across another use-case for customizing the handshake: Sending a "Set-Cookie" header. Would be nice to have some way to customize the handshake headers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment