http-kit / http-kit

Simple, high-performance event-driven HTTP client+server for Clojure
https://www.taoensso.com/http-kit
Apache License 2.0
2.45k stars 340 forks source link

async-channel response breaks Compojure's Renderable mechanism #233

Closed dmichulke closed 9 years ago

dmichulke commented 9 years ago

When using compojure routes and returning an async-channel (i.e., the channel itself and not a response map with an async-channel body), compojure's Renderable tries to turn the channel into a response map and understably fails to do so.

I could just respond with an async channel body, however, that forces me to respond with a fixed HTTP status.

Is there any way to deal with this?

Thanks and best regards.

Exception: No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: org.httpkit.server.AsyncChannel

Compojure API Doc & source (click the link there) http://weavejester.github.io/compojure/compojure.response.html

dmichulke commented 9 years ago

On a second look, the Channel protocol defines the second argument of send! as:

Data form: {:headers :status :body } or just body._ (see https://github.com/http-kit/http-kit/blob/master/src/org/httpkit/server.clj#L54)

If I implement Renderable via

(extend-protocol compojure.response/Renderable
  AsyncChannel
  (render [async-channel _] async-channel))

then I get org.httpkit.server.AsyncChannel cannot be cast to java.util.Map at org.httpkit.server.HttpHandler.run(RingHandler.java:91). (see https://github.com/http-kit/http-kit/blob/master/src/java/org/httpkit/server/RingHandler.java#L91)

There I don't see any provision for a response map delivered via AsyncChannel, just for a body.

dmichulke commented 9 years ago

OK, it seems I understand now what's going on, with-channel actually wraps the async channel in a body that is later on ignored and just used to get the channel through compojure.

That's the reason why there is this //hijacked in the source code (link below) and also the reason why it didn't work for me - I should have used {:body {:status 400 :body ...}} https://github.com/http-kit/http-kit/blob/master/src/java/org/httpkit/server/RingHandler.java#L96

Neat hack!