Hi mond,

I've been using HTTPkit with Compojure and core.async (all fronted by Nginx 
so the entire stack is async FWIW).

To glue core.async with HTTPkit when handling inbound requests, I have a 
utility function

(defn handle-async! [handler req]
  (http-server/with-channel req channel
                            (take! (handler req) #(http-server/send! 
channel %))))

and I make use of that utility function when defining routes

(defroutes all-routes
           (GET "/covers/v1/:cover" [] (partial http/handle-async! 
fetch-cover!))
           (context "/books/v1" []
                    (GET "/search" [] (partial http/handle-async! 
search-handler!))
                    (GET "/:isbn" [] (partial http/handle-async! 
get-book-by-isbn!))))

The handler functions fetch-cover!, search-handler!, get-book-by-isbn!, all 
return channels (they either call go for stuff that needs to be async, or 
to-chan on collections read directly from memory.

- Mike 

On Saturday, September 27, 2014 5:01:36 AM UTC-4, mond wrote:
>
> Hi James,
>
> Er, nice tip on the routes - that was another thing that I didn't expect 
> to work but was happy when it did ;-) I will of course adapt it to your 
> recommendation.
>
> Speaking to the main point, no I don't want to put a channel on to the 
> response so that's a mistake that I see and would like to avoid. I would 
> like to understand where I have gone wrong.
>
> Maybe I need to use HTTPkit instead?
>
> Thanks
>
> Ray
>
>
>
> On Saturday, 27 September 2014 02:08:50 UTC+2, James Reeves wrote:
>>
>> Hi Ray,
>>
>> I don't entirely understand why you expected this to work. Channels 
>> aren't a valid Ring response body. The error message is essentially telling 
>> you that Compojure has no way of turning the channel object you've returned 
>> into a valid response.
>>
>> The other problem you have is that the Ring Jetty adapter doesn't have 
>> any support for asynchronous operations.
>>
>> Another small point. You're using "*" as an argument name, but this isn't 
>> really recommended. This only works by coincidence, and it may be removed 
>> in future versions. Instead use something like:
>>
>> (GET ["/:brand/:country/:resource" :resource #".*"] [brand country 
>> resource] ...)
>>
>> - James
>>
>> On 27 September 2014 00:14, mond <r...@mcdermott.be> wrote:
>>
>>> My first core.async program ... all works outside of the web app but 
>>> barfs once I put the functions inside a web container. I hope somebody in 
>>> the group can point to my obvious mistake...
>>>
>>> The idea is that the function 'respond-within-sla' will give back a 
>>> result or a come back later message after N milliseconds. It is passed a 
>>> number of ms, three functions and the arguments for the final function ... 
>>> which is the one that should operate within the SLA.
>>>
>>> (defn respond-within-sla [expected-result-milliseconds respond-ok 
>>> respond-later data-fetcher & args]
>>>   (let [data-channel (timeout expected-result-milliseconds)]
>>>     (go (if-let [data (<!! data-channel)]
>>>           ((async/close! data-channel)
>>>            (respond-ok data))
>>>           (respond-later)))
>>>     (go
>>>       (>! data-channel (apply data-fetcher args)))))
>>>
>>> To keep the volume of code to parse to a minimum I have made a few toy 
>>> functions that demonstrate the failure...
>>>
>>> ; test funcs
>>>
>>> (defn ok [data]
>>>   (prn (str "send HTTP 200 ... got data " data)))
>>>
>>> (defn later []
>>>   (prn (str "send HTTP 202 ... request received, come back later")))
>>>
>>> (defn fetcher [arg1 arg2 arg3]
>>>   (prn (str "fetching data with args " arg1 " " arg2 " " arg3))
>>>   "response-data")
>>>
>>> (defn failer [& args]
>>>   (Thread/sleep 1000)
>>>   (str "never gets here " args))
>>>
>>> ; test funcs
>>>
>>> (defn ok [data]
>>>   (prn (str "send HTTP 200 ... got data " data)))
>>>
>>> (defn later []
>>>   (prn (str "send HTTP 202 ... request received, come back later")))
>>>
>>> (defn fetcher [arg1 arg2 arg3]
>>>   (prn (str "fetching data with args " arg1 " " arg2 " " arg3))
>>>   "response-data")
>>>
>>> (defn failer [& args]
>>>   (Thread/sleep 1000)
>>>   (str "never gets here " args))
>>>
>>> (defn generate-response [brand country resource]
>>>   (let [sla (or (env :sla-milliseconds) 100)]
>>>     (respond-within-sla sla ok later fetcher brand country resource)))
>>>
>>> (defn generate-fail [brand country resource]
>>>   (let [sla (or (env :sla-milliseconds) 100)]
>>>     (respond-within-sla sla ok later failer brand country resource)))
>>>
>>> From within the REPL it all works fine...
>>>
>>> (generate-response "A" "B" "C")
>>> => #<ManyToManyChannel 
>>> clojure.core.async.impl.channels.ManyToManyChannel@4b7ae3f7>
>>> "fetching data with args A B C"
>>> "send HTTP 200 ... got data response-data"
>>> (generate-fail "A" "B" "C")
>>> => #<ManyToManyChannel 
>>> clojure.core.async.impl.channels.ManyToManyChannel@4eb8b5a9>
>>> "send HTTP 202 ... request received, come back later"
>>>
>>> Here is the compojure route..
>>>
>>> (defroutes app
>>>            (GET "/:brand/:country/*" [brand country *]
>>>                 (generate-response brand country *))
>>>
>>>            (ANY "*" []
>>>                 (route/not-found "You must use a REST style to specify 
>>> brand and country keys in the URL")))
>>>
>>> If I now start it up 'lein run' and try to exercise the functions from 
>>> the web server...
>>>
>>> $ curl -I http://localhost:5000/A/B/D.jpg
>>> HTTP/1.1 500 Server Error
>>> Date: Fri, 26 Sep 2014 23:02:03 GMT
>>> Content-Length: 0
>>> Connection: close
>>> Server: Jetty(7.6.8.v20121106)
>>>
>>> And on the server I see this:
>>>
>>> $ lein run
>>> Compiling redirector.web
>>> 2014-09-27 01:01:48.426:INFO:oejs.Server:jetty-7.6.8.v20121106
>>> 2014-09-27 01:01:48.458:INFO:oejs.AbstractConnector:Started 
>>> SelectChannelConnector@0.0.0.0:5000
>>> 2014-09-27 01:02:03.535:WARN:oejs.AbstractHttpConnection:/A/B/D.jpg
>>> java.lang.IllegalArgumentException: No implementation of method: :render 
>>> of protocol: #'compojure.response/Renderable found for class: 
>>> clojure.core.async.impl.channels.ManyToManyChannel
>>> at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:544)
>>> at compojure.response$fn__213$G__208__220.invoke(response.clj:9)
>>> at compojure.core$make_route$fn__332.invoke(core.clj:100)
>>> at compojure.core$if_route$fn__320.invoke(core.clj:46)
>>> at compojure.core$if_method$fn__313.invoke(core.clj:33)
>>> at compojure.core$routing$fn__338.invoke(core.clj:113)
>>> at clojure.core$some.invoke(core.clj:2515)
>>> at compojure.core$routing.doInvoke(core.clj:113)
>>> at clojure.lang.RestFn.applyTo(RestFn.java:139)
>>> at clojure.core$apply.invoke(core.clj:626)
>>> at compojure.core$routes$fn__342.invoke(core.clj:118)
>>> at clojure.lang.Var.invoke(Var.java:379)
>>> at 
>>> ring.middleware.keyword_params$wrap_keyword_params$fn__534.invoke(keyword_params.clj:35)
>>> at 
>>> ring.middleware.nested_params$wrap_nested_params$fn__576.invoke(nested_params.clj:84)
>>> at ring.middleware.params$wrap_params$fn__507.invoke(params.clj:64)
>>> at 
>>> ring.middleware.multipart_params$wrap_multipart_params$fn__612.invoke(multipart_params.clj:118)
>>> at ring.middleware.flash$wrap_flash$fn__1286.invoke(flash.clj:35)
>>> at ring.middleware.session$wrap_session$fn__1273.invoke(session.clj:98)
>>> at ring.adapter.jetty$proxy_handler$fn__1426.invoke(jetty.clj:18)
>>> at 
>>> ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown
>>>  
>>> Source)
>>> at 
>>> org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
>>> at org.eclipse.jetty.server.Server.handle(Server.java:363)
>>> at 
>>> org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:483)
>>> at 
>>> org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:920)
>>> at 
>>> org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:982)
>>> at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635)
>>> "fetching data with args A B D.jpg"
>>> at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
>>> at 
>>> org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
>>> at 
>>> org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:628)
>>> at 
>>> org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
>>> at 
>>> org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
>>> at 
>>> org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
>>> at java.lang.Thread.run(Thread.java:744)
>>> "send HTTP 200 ... got data response-data"
>>>
>>> FYI I have tested the functions prior to adding the core.async code and 
>>> they all work fine from within Jetty and on Heroku.
>>>
>>> It seems like somehow I am getting the wrong thing put into my response.
>>>
>>> I am guessing that I have configured something wrong so here is my 
>>> project.clj
>>>
>>> (defproject redirector "1.0.0-SNAPSHOT"
>>>   :description "Clojure HTTP redirector"
>>>   :url "http://redirector.herokuapp.com";
>>>   :license {:name "Eclipse Public License v1.0"
>>>             :url "http://www.eclipse.org/legal/epl-v10.html"}
>>>   :dependencies [[org.clojure/clojure "1.6.0"]
>>>                  [compojure "1.1.9"]
>>>                  [ring/ring-jetty-adapter "1.2.2"]
>>>                  [com.novemberain/monger "2.0.0"]
>>>                  [com.taoensso/carmine "2.7.0"]
>>>                  [environ "0.5.0"]
>>>                  [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
>>>   :min-lein-version "2.0.0"
>>>   :plugins [[environ/environ.lein "0.2.1"]]
>>>   :hooks [environ.leiningen.hooks]
>>>   :main "redirector.web"
>>>   :aot :all
>>>   :uberjar-name "redirector-standalone.jar"
>>>   :profiles {:production {:env {:production true}}})
>>>
>>> Any ideas, help would be greatly appreciated.
>>>
>>> Thanks
>>>
>>> Ray
>>>
>>>
>>>  -- 
>>> You received this message because you are subscribed to the Google
>>> Groups "Clojure" group.
>>> To post to this group, send email to clo...@googlegroups.com
>>> Note that posts from new members are moderated - please be patient with 
>>> your first post.
>>> To unsubscribe from this group, send email to
>>> clojure+u...@googlegroups.com
>>> For more options, visit this group at
>>> http://groups.google.com/group/clojure?hl=en
>>> --- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "Clojure" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to clojure+u...@googlegroups.com.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to