immutant

http://immutant.org Note: dev discussion happens in #immutant on FreeNode IRC.
kardan 2016-03-11T06:39:37.000412Z

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 …)

2016-03-11T10:33:38.000415Z

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.

2016-03-11T10:34:44.000417Z

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

2016-03-11T10:58:07.000419Z

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

2016-03-11T11:46:31.000420Z

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

2016-03-11T14:18:19.000422Z

@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? https://jira.jboss.org/jira/secure/CreateIssue.jspa?issuetype=2&pid=12311821

2016-03-11T14:58:01.000424Z

@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?

2016-03-11T14:59:41.000425Z

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

2016-03-11T15:00:40.000426Z

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?

2016-03-11T15:09:05.000428Z

@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

2016-03-11T15:11:59.000429Z

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

2016-03-11T15:14:50.000430Z

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

2016-03-11T15:15:32.000431Z

so here's the http-kit version

2016-03-11T15:16:45.000433Z

(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
             {:body ~ch-name})
         {:status 400 :body "Bad Sec-WebSocket-Key header"})
       (do ~@body
           {:body ~ch-name}))))

2016-03-11T15:17:53.000435Z

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

2016-03-11T15:22:05.000436Z

@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?

2016-03-11T15:23:17.000437Z

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

2016-03-11T15:23:58.000438Z

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

2016-03-11T15:24:45.000439Z

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)

2016-03-11T15:25:45.000440Z

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

2016-03-11T15:26:10.000441Z

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

2016-03-11T15:26:16.000442Z

I'd have to investigate it

2016-03-11T15:27:50.000443Z

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

2016-03-11T15:33:44.000445Z

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?

2016-03-11T15:35:04.000447Z

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

2016-03-11T15:36:06.000448Z

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

2016-03-11T15:36:52.000449Z

two separate servers?

2016-03-11T15:36:55.000450Z

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

2016-03-11T15:37:08.000451Z

one server, with multiple handlers registered

2016-03-11T15:37:18.000452Z

okay..

2016-03-11T15:49:16.000454Z

@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

2016-03-11T15:50:56.000455Z

so you should be able to do `

2016-03-11T15:54:00.000456Z

sorry, working on an example

2016-03-11T15:55:08.000457Z

take your time Toby..you've been super helpful

2016-03-11T15:55:42.000458Z

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

2016-03-11T15:56:13.000459Z

and returning a 400 if it is not there?

2016-03-11T15:56:33.000460Z

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

2016-03-11T15:57:02.000462Z

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

2016-03-11T15:57:16.000464Z

ah

2016-03-11T15:57:26.000465Z

what does your custom code do, roughly?

2016-03-11T15:58:46.000466Z

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

2016-03-11T16:00:21.000468Z

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

2016-03-11T16:01:13.000469Z

yup

2016-03-11T16:02:27.000470Z

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

2016-03-11T16:05:07.000471Z

ah, ok

2016-03-11T16:05:15.000472Z

I may have something for you

2016-03-11T16:05:18.000473Z

one sec

2016-03-11T16:07:58.000474Z

is waiting with baited breath

2016-03-11T16:10:03.000475Z

the request map should have a :server-exchange in it. That is an HttpServerExchange object (http://undertow.io/javadoc/1.3.x/io/undertow/server/HttpServerExchange.html). 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

2016-03-11T16:11:37.000478Z

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

2016-03-11T16:15:04.000481Z

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

2016-03-11T16:15:22.000482Z

ok..so 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?

2016-03-11T16:15:35.000483Z

yeah, basically

2016-03-11T16:17:27.000484Z

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

2016-03-11T16:20:44.000485Z

@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
      )))

2016-03-11T16:22:33.000486Z

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

2016-03-11T16:22:49.000487Z

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?

2016-03-11T16:23:20.000489Z

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

2016-03-11T16:23:33.000490Z

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

2016-03-11T16:23:50.000491Z

nah, standalone

2016-03-11T16:23:57.000492Z

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

2016-03-11T16:24:39.000493Z

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

2016-03-11T16:24:58.000494Z

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

2016-03-11T16:25:16.000495Z

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

2016-03-11T16:25:56.000496Z

yes, please let me know how it goes

2016-03-11T16:26:00.000497Z

and it's my pleasure!