Fork me on GitHub

I tried to hook up SSL with Immutant web but Chrome complains with “ERR_SSL_VERSION_OR_CIPHER_MISMATCH “. I can’t find a way to define suites. Am I overlooking something? (not done anything with ssl before …)


hey everyone, I am trying to use the immutant/web component 2.1.3. When I start up a repl and require the immutant.web ns, i get this error -- Exception namespace 'potemkin.utils' not found clojure.core/load-lib (core.clj:5380) anyone seen this? I've added org.immutant/web "2.1.3" in my project deps. also tried adding all of immutant to the deps but same error.


i know potemkin is a dep for immutant, but I shouldnt be needing to add it manually right? I ran lein deps dint help


found the reason - potemkin.utils compile was failing as it needed the vector symbol from the clj-tuple lib, and for some reason my project was pulling in a lower version of clj-tuple that dint have it. Fixed it by explictly adding the latest clj-tuple as a dep


does someone how i could check for a subprotocol before accepting a client websocket connection? (with immutant)


@moizsj: we don't expose the subprotocol currently, but I think we could make it part of the request map for upgrades - would you mind filing an issue for that?;pid=12311821


@tcrawley: thanks for the answer. as a workaround, I plan to write a custom version of the as-channel method that will inspect the headers from the request map to check the subprotocol. it'll pretty much be a copy except the header check. do you have any better ideas for a workaround?


@tcrawley: our front end currently sticks the subprotocol in the headers


do you need a custom as-channel if you are inspecting headers? can't you just look at the headers in the request map, and decide to call as-channel or not based on those?


@tcrawley: what I need is a way to get in the middle of the handshake between the server and client. I need to check the header for subprotocol, then add the Sec-WebSocket-Protocol header to the response (and some other custom headers). And if it goes through, only then would i want the :on-open to be called


we are using http-kit at the moment and we had to write a similar custom with-subproto-channel macro (similar but not same) as the with-channel macro of http-kit. And now trying to port to immutant/web


ah, that's tougher - in http-kit, you have more access to the upgrade process. In Immutant, that's hidden - we had to do that to be able to support WildFly. If you plan to just use Immutant outside of the container, you could probably do it with a custom Undertow HttpHandler


so here's the http-kit version


(defmacro with-channel
[request ch-name & body]
  `(let [~ch-name (:async-channel ~request)]
     (if (:websocket? ~request)
       (if-let [key# (get-in ~request [:headers "sec-websocket-key"])]
         (do (.sendHandshake ~(with-meta ch-name {:tag `AsyncChannel})
                             {"Upgrade"    "websocket"
                              "Connection" "Upgrade"
                              "Sec-WebSocket-Accept" (accept key#)})
             {:body ~ch-name})
         {:status 400 :body "Bad Sec-WebSocket-Key header"})
       (do ~@body
           {:body ~ch-name}))))


the custom part (which I cant post here) is checking headers for subprot, then augmenting the headers map handed out to the sendHandShake


@tcrawley: would you kindly elaborate a bit on your suggestion? what do you mean by Immutant without a container? And when you say using an Undertow Handler, is that something that'll need to written in Java? And i assume wont be Ring compatible?


Immutant supports two modes of deployment: an uberjar (just like you do with http-kit), and as a special war file to a WildFly or EAP application server


in the first case, we interact directly with the Undertow web server. in the container case, we have to interact with the servlet api, so have different implementations for websockets


because of that, your ring handler isn't called during the ws handshake, but after it is completed (undertow would allow us to do it during the handshake, but the servlet api does not)


so you would have to write a custom HttpHandler that handled the upgrade process. you could write it in clojure using proxy or reify


but it may be difficult to have that work with our async api


I'd have to investigate it


the servlet websocket api is terrible, and I hate that it puts this limitation on us


so suppose I wrote the HttpHandler and the aync api dint work , could i still build some Compojure routes, pass the handler (of the routes) to the run, and one of those routes explicitly dealt with upgrades ("/xyz/websock")? would that work?


in that ws-handler, I'd need to callback into equivalents of :on-open, :on-message, :on-close


you couldn't nest the HttpHandler route under compojure, but could do (run compojure-app :path "/") (run http-handler :path "/websock")


two separate servers?


let me take a look at the code to see if it would be possible to insert a handler in the chain that would allow you to use one run call and still use async


one server, with multiple handlers registered


@moizsj: actually, hmm. outside of the container, we do call your ring handler before calling the upgrade handler, and only call the upgrade handler if your ring-handler returns the result of as-channel


so you should be able to do `


sorry, working on an example


take your time've been super helpful


in your above code, are you ensuring the sec-websocket-key is set before letting the upgrade continue?


and returning a 400 if it is not there?


or is there some back and forth between the client and server during the handshake there?


that part is the same as the above http-kit version (nothing custom)


what does your custom code do, roughly?


adds an extra Sec-WebSocket-Protocol header to the above map passed to sendHandShake, and a bunch of extra headers


and .sendHandshake then sends those headers back to the client as part of the upgrade negotiation?


and of course there's an extra if before calling the sendHandShake to check for the subprotocol in the request map


I may have something for you


is waiting with baited breath


the request map should have a :server-exchange in it. That is an HttpServerExchange object ( From it, you can get the request headers (`.getRequestHeaders`). You can then mutate the headers, then call as-channel and return. That exchange is then passed to the upgrade handler, with the headers set. If the upgrade succeeds, your :on-open will get called


checker is what calls your ring handler, and looks at the response to see if it has a channel body, and then calls the upgrade handler (`wsHandler` there) if it does


I know that's confusing, working on an example ring-handler now

moizsj16:03:22 I simply inspect the exchange (after pulling it out from the request map), look for the subprot header, add my custom headers, and then return as-channel from the ring handler?


yeah, basically


ok cool, I think i got the picture. the example would surely help.


@moizsj: something like (untested):

(import 'io.undertow.util.HttpString)

(defn handler [req]
  (let [exchange-headers (-> :server-exchange .getRequestHeaders)]
    (when (check-subprotocol (:headers req))
      (.put exchange-headers (HttpString. "header-name") "header-value"))
    (as-channel req
      :on-open (fn [ch])
      ;; etc


and you can use immutant.web.internal.headers/set-header instead of calling .put directly on exchange-headers to avoid that HttpString nonsense


got it. and you reckon this coupling to the exchange object is not risky? could it be removed/renamed since its part of the public contract?


(set-header exchange-headers "foo" "bar")


it's safe, as long as you never plan to use this inside a WildFly container


nah, standalone


it's part of the undertow public api, so would only break if immutant moved away from undertow, which won't happen


I'm not 100% this will work, but I am fairly confident


and if it doesn't work, I think I can make it work with minor changes to immutant itself


super. thanks for taking so much time to help. I'll come back next week and let you know how this pans out.


yes, please let me know how it goes


and it's my pleasure!