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? https://jira.jboss.org/jira/secure/CreateIssue.jspa?issuetype=2&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
{: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
okay..
@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 Toby..you'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)
ah
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?
yup
and of course there's an extra if
before calling the sendHandShake to check for the subprotocol in the request map
ah, ok
I may have something for you
one sec
is waiting with baited breath
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
the relevant java is here: https://github.com/projectodd/wunderboss/blob/master/web-undertow/src/main/java/org/projectodd/wunderboss/web/undertow/async/websocket/UndertowWebsocket.java#L99
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
we build the checker here: https://github.com/immutant/immutant/blob/master/web/src/immutant/web/internal/undertow.clj#L221
I know that's confusing, working on an example ring-handler now
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?
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!