Using http-kit long polling with core.async channels - http

I have some long running process that returns a core.async channel with the result on it when the process has finished.
Now I'd like to return that result using long-polling with HTTP-kit.
Unfortunately I'm a bit confused what the right way of doing so is.
Currently I have a handler (hooked up to a route) that initiates the processing call and sends the result when done:
(defn handler
[request]
(let [c (process request)] ;; long running process that returns a channel
(http/with-channel request channel
(http/send! channel {:status 200
:body (<!! (go (<! c)))))
(http/on-close channel (fn [_] (async/close! c))))))
It works, but I'm unsure if this is the right way.
EDIT since <!! is blocking I'm now trying a non-blocking variant in a go-loop
(defn handler
[request]
(let [c (process request)]
(http/with-channel request channel
(async/go-loop [v (<! c)]
(http/send! channel {:status 200
:body v}))
(http/on-close channel (fn [_] (async/close! c))))))

Why not send on the channel in the go block?
(http/with-channel request channel
(go (http/send! channel (<! c))))
<!! is blocking - so there is no real advantage in your code from just directly calling <!! c in the handler:
(defn handler
[request]
(let [c (process request)] ;; long running process that returns a channel
{:status 200
:body (<!! c)}))
Edit in response to question update: The updated code works - this is a fully functioning namespace which works for me:
(ns async-resp.core
(:require [org.httpkit.server :as http]
[clojure.core.async :as async :refer (<! >! go chan go-loop close! timeout)]))
(defn process
[_]
(let [ch (chan)]
(go
(<! (timeout 5000))
(>! ch "TEST"))
ch))
(defn test-handler
[request]
(let [c (process request)]
(http/with-channel request channel
(go-loop [v (<! c)]
(http/send! channel {:status 200
:body v}))
(http/on-close channel (fn [_] (close! c))))))
(defn run
[]
(http/run-server test-handler {}))
As of the current moment in time though, I had to manually add the tools.analyzer.jvm dependency to project.clj - as I get compilation failures using core.async as-is.
Check you're running the latest core.async and analyzer?

Related

Clojure Cross Origin Error - Totally Lost

I have the following simple server in Clojure using Compojure (which is some flavor of the ring pattern). Everything was working fine in development, and now that I'm in prod I can't get CORS to work for the life of me - I have a wrap-preflight function which seems to work fine, but I keep getting CORS errors in terminal and neither the post or get requests for my comment system work. I am totally lost and very frustrated, I've asked around and no one else seems to know.
Here is the main core.clj code - If anyone has any ideas please let me know. You can see the errors live at thedailyblech.com (not an advert, but maybe it will help debug).
Thank you!
(ns clojure-play.core
(:use org.httpkit.server
[compojure.core :refer :all]
[compojure.route :as route]
[clojure.data.json :as json]
[clojure.tools.logging :only [info]]
[clojure-play.routes :as routes]
[ring.middleware.json :only [wrap-json-body]]
[ring.middleware.cors :refer [wrap-cors]])
(:require [monger.core :as mg]
[monger.collection :as mc]
[clojure.edn :as edn]
[clojure.java.io :as io]
[compojure.handler :as handler])
(:import [org.bson.types ObjectId]
[com.mongodb DB WriteConcern])
(:gen-class))
(println "in the beginning was the command line...")
(defonce channels (atom #{}))
(defn connect! [channel]
(info "channel open")
(swap! channels conj channel))
(defn notify-clients [msg]
(doseq [channel #channels]
(send! channel msg)))
(defn disconnect! [channel status]
(info "channel closed:" status)
(swap! channels #(remove #{channel} %)))
(defn ws-handler [request]
(with-channel request channel
(connect! channel)
(on-close channel (partial disconnect! channel))
(on-receive channel #(notify-clients %))))
(defn my-routes [db]
(routes
(GET "/foo" [] "Hello Foo")
(GET "/bar" [] "Hello Bar")
(GET "/json_example/:name" [] routes/json_example)
(GET "/json_example" [] routes/json_example)
(POST "/email" [] routes/post_email)
(POST "/write_comment" [] (fn [req] (routes/write_comment req db)))
(POST "/update_comment" [] (fn [req] (routes/update_comment req db)))
(GET "/read_comments/:path" [path] (fn [req] (routes/read_comments req db path)))
(GET "/read_comments/:path1/:path2" [path1 path2] (fn [req] (routes/read_comments req db (str path1 "/" path2))))
(GET "/ws" [] ws-handler)))
(defn connectDB []
(defonce connection
(let
[uri "mongodb://somemlabthingy"
{:keys [conn db]} (mg/connect-via-uri uri)]
{:conn conn
:db db}))
{:db (:db connection)
:conn (:conn connection)})
(def cors-headers
"Generic CORS headers"
{"Access-Control-Allow-Origin" "*"
"Access-Control-Allow-Headers" "*"
"Access-Control-Allow-Methods" "GET POST OPTIONS DELETE PUT"})
(defn preflight?
"Returns true if the request is a preflight request"
[request]
(= (request :request-method) :options))
(defn -main
"this is main"
[& args]
(println "hello there main")
(def db (get (connectDB) :db))
(println (read-string (slurp (io/resource "environment/config.edn"))))
(defn wrap-preflight [handler]
(fn [request]
(do
(println "inside wrap-preflight")
(println "value of request")
(println request)
(println "value of handler")
(println handler)
(if (preflight? request)
{:status 200
:headers cors-headers
:body "preflight complete"}
(handler request)))))
(run-server
(wrap-preflight
(wrap-cors
(wrap-json-body
(my-routes db)
{:keywords? true :bigdecimals? true})
:access-control-allow-origin [#"http://www.thedailyblech.com"]
:access-control-allow-methods [:get :put :post :delete :options]
:access-control-allow-headers ["Origin" "X-Requested-With"
"Content-Type" "Accept"]))
{:port 4000}))
The CORS middleware handles the preflight stuff automatically -- you do not need separate middleware for it, nor do you need to produce your own headers etc.
You have it wrapping the routes which is correct -- so CORS-checking happens first, then routing. You should remove your custom preflight middleware and it should work at that point.
We use wrap-cors at work and the only complication we hit was in allowing enough headers (some inserted by production infrastructure, like load balancers). We ended up with this:
:access-control-allow-headers #{"accept"
"accept-encoding"
"accept-language"
"authorization"
"content-type"
"origin"}
For what it's worth, here's what we have for methods:
:access-control-allow-methods [:delete :get
:patch :post :put]
(you do not need :options in there)
After digging around for hours, I found this to be super helpful on an issues post on the ring-cors github, for which documentation was severely lacking.
Using the linked gist, I was able to get past the CORS issues:
; Copied from linked gist
(def cors-headers
"Generic CORS headers"
{"Access-Control-Allow-Origin" "*"
"Access-Control-Allow-Headers" "*"
"Access-Control-Allow-Methods" "GET"})
(defn preflight?
"Returns true if the request is a preflight request"
[request]
(= (request :request-method) :options))
(defn all-cors
"Allow requests from all origins - also check preflight"
[handler]
(fn [request]
(if (preflight? request)
{:status 200
:headers cors-headers
:body "preflight complete"}
(let [response (handler request)]
(update-in response [:headers]
merge cors-headers )))))
; my -main
(defn -main
"Main entry point"
[& args]
(let [port (Integer/parseInt (or (System/getenv "PORT") "8081"))]
(server/run-server
(all-cors
(wrap-defaults #'app-routes site-defaults))
{:port port})
(println "Running on" port)))
This finally allowed me to see the headers properly set in Chrome dev tools and also got rid of the warning on my React front-end.
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
Might be worth trying and adding an explicit
(OPTIONS "/*" req handle-preflight)
route to your Compojure routes - in my case that's what made it work.
(RING-MIDDLEWARE-CORS/wrap-cors
:access-control-allow-credentials "true"
:access-control-allow-origin [#".*"]
:access-control-allow-headers #{"accept"
"accept-encoding"
"accept-language"
"authorization"
"content-type"
"origin"}
:access-control-allow-methods [:get :put :post :delete :options])

How can I make an http call in clojure/ring?

My web client (written in cljs) connects to backend (written in clj) which needs to make a few third party API calls. It has to be done on the server and then the result should be transformed in a specific way and sent back to the client.
Here's my handler for one of the urls
(defn get-orders [req]
(let [{:keys [sig uri]} (api-signature :get-orders)]
(client/get uri
{:async? true}
(fn [response] {:body "something"})
(fn [exception] {:body "error"}))))
Instead of returning {:body "something"}, it is returning the following error:
No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: org.apache.http.impl.nio.client.FutureWrapper
What am I doing wrong?
When you specify {:async? true}, clj-http.client/get will return a future which is a FutureWrapper in the error message you got.
So if you don't need async, don't use it. This is an example of a synchronous ring handler which calls a third-party url and returns the response that got back.
(defn handler [request]
(response {:result (client/get "http://example.com")}))
If you really need async, use async version of ring handler.
(defn handler [request respond raise]
(client/get "http://example.com"
{:async? true}
(fn [response] (respond {:body "something"}))
(fn [exception] (raise {:body "error"}))))
Don't forget to config webserver adapter to use the async handler. For example, for Jetty, set :async? flag to true like so
(jetty/run-jetty app {:port 4000 :async? true :join? false})
If you want to concurrently call to multiple third-party urls and return once to web client, use promise to help
(defn handler [request]
(let [result1 (promise)
result2 (promise)]
(client/get "http://example.com/"
{:async? true}
(fn [response] (deliver result1 {:success true :body "something"}))
(fn [exception] (deliver result1 {:success false :body "error"})))
(client/get "http://example.com/"
{:async? true}
(fn [response] (deliver result2 {:success true :body "something"}))
(fn [exception] (deliver result2 {:success false :body "error"})))
(cond
(and (:success #result1)
(:success #result2))
(response {:result1 (:body #result1)
:result2 (:body #result2)})
(not (:success #result1))
(throw (ex-info "fail1" (:body #result1)))
(not (:success #result2))
(throw (ex-info "fail2" (:body #result2))))))

ClojureScript file preloader - function or pattern to emulate promise?

I'm trying to create a file preloader within ClojureScript. My idea was a pattern like this:
(def urls (atom[]))
(def loaded-resources (atom []))
(def all-resources (promise))
(defn loading-callback []
(if (= (count urls) (count loaded-resources))
(deliver all-resources loaded-resources)))
;; fill urls array
;; start ajax-loading with loading-callback on success
So my main function could go on until it would require the resources and then wait for them, which works well in Clojure.
Unfortunately, promises don't exist in ClojureScript, so how can I work around that issue? There's promesa bringing promises to CLJS based on core.async channels, but it only allows future-like promises that wait for a single function to execute which won't suffice my needs (at least in the way I've been thinking about it yesterday...).
Any suggestions to solve this issue? Maybe use a completely different pattern? I want to keep the code as simple as possible to convince people in my team to try out CLJ/S.
EDIT:
After Alan's second idea:
(def urls (atom[]))
(def loaded-resources (atom []))
(defn loading-callback [data]
(swap! loaded-resources conj data))
(defn load! [post-loading-fn]
(add-watch loaded-resources :watch-loading
(fn [_ _ _ cur]
(if (= (count cur) (count #urls)) (post-loading-fn))))
;; init ajax loading
)
(defn init []
;; fill urls array
(load! main))
(main []
(do-terrific-stuff #loaded-resources))
Meanwhile I had tried to use core.async
(def urls (atom []))
(def loaded-resources (atom []))
(def resource-chan (chan))
(defn loading-callback [data]
(go (>! resource-chan data)))
;; fill url array from main
(load! []
;; init ajax loading
(go-loop []
(when-not (= (count #loaded-resources) (count #urls))
(swap! loaded-resources conj (<! resource-chan))
(recur)))
Not sure which version is better.
I can think of 2 approaches.
Change all-resources to another atom, initialized at nil. Poll it 2x-5x/sec until it is not nil and has the "delivered" result.
Use add-watch to register a callback function to execute when the value is changed. This takes the place of blocking until the value is delivered. It is described here: http://clojuredocs.org/clojure.core/add-watch
They show a good example:
(def a (atom {}))
(add-watch a :watcher
(fn [key atom old-state new-state]
(prn "-- Atom Changed --")
(prn "key" key)
(prn "atom" atom)
(prn "old-state" old-state)
(prn "new-state" new-state)))
(reset! a {:foo "bar"})
;; "-- Atom Changed --"
;; "key" :watcher
;; "atom" #<Atom#4b020acf: {:foo "bar"}>
;; "old-state" {}
;; "new-state" {:foo "bar"}
;; {:foo "bar"}
Assuming your load resource function returns a channel (like cljs-http/get).
In clj, all you need to do is hold on to them to do a "wait-all".
(let [cs (doall (map load-resource urls)) ;; initiate the get
... ;; other initialisation
res (map <!! cs)] ;; wait-all for the resources
(do-other-things res))
In cljs, you can accumulate the responses before you continue:
(go
(let [res (atom [])]
(doseq [c cs]
(swap! res conj (<! c)))
(do-other-things #res)))
JavaScript is a single threaded environment so there is no blocking wait.
If you wish to request multiple resources and continue iff they have all been served, I do recommend using core.async and especially pipeline-async. It has a knob to finetune the parallelism of asynchronous requests. Here is idiomatic ClojureScript code to achieve what you want:
(ns example.core
(:require [cljs.core.async :refer [chan take! put! pipeline-async]
:as async]))
(defn load-resources [urls on-resources]
(let [urls-ch (chan (count urls))
resources-ch (chan)]
;; Create pipeline:
(pipeline-async 10 ;; have at most 10 requests in flight at
;; the same time, finetune as desired
resources-ch
(fn [url done-ch]
;; Pseudo code:
(request-resource
url
(fn [loaded-resource]
(put! done-ch loaded-resource))))
urls-ch)
;; Eagerly aggregate result until results-ch closes, then call back:
(take! (async/into [] resources-ch) on-resources)
;; Start the party by putting all urls onto urls-ch
;; and then close it:
(async/onto-chan urls-ch urls)))

TCP transport long bytes with Aleph

I am trying to build RCP server based on aleph. It passed on all the test, but when the byte array for send or receive become large, the bytes seems been corrupted.
For example. I tried to send a byte array which length is 2936, but I only got 1024 bytes at server
I followed the example and did my own modifications for nippy. The encoding and decoding will be done by handlers themselves.
(defn wrap-duplex-stream
[s]
(let [out (s/stream)]
(s/connect out s)
(s/splice out s)))
(defn client
[host port]
(d/chain (tcp/client {:host host, :port port})
#(wrap-duplex-stream %)))
(defn start-server
[handler port]
(tcp/start-server
(fn [s info]
(handler (wrap-duplex-stream s) info))
{:port port}))
I finally made my own codec for byte array.
It is simple, but to figure how to make use it with gloss and aleph is time consuming
(defn buffer->byte-array [buf-seq]
(byte-streams/to-byte-array buf-seq))
(defn bytes-codec []
(reify
Reader
(read-bytes [this buf-seq]
(let [buf-seq (dup-bytes buf-seq)
byte-arr (buffer->byte-array buf-seq)]
[true byte-arr nil]))
Writer
(sizeof [x]
nil)
(write-bytes [x y byte-arr]
[(ByteBuffer/wrap byte-arr)])))
(def protocol
(gloss/compile-frame
(gloss/finite-frame
:uint32
(bytes-codec))
#(nippy/freeze %)
#(nippy/thaw %)))
(defn wrap-duplex-stream
[s]
(let [out (s/stream)]
(s/connect
(s/map #(io/encode protocol %) out)
s)
(s/splice
out
(io/decode-stream s protocol))))
(defn client
[host port]
(d/chain (tcp/client {:host host, :port port})
#(wrap-duplex-stream %)))
(defn start-server
[handler port]
(tcp/start-server
(fn [s info]
(handler (wrap-duplex-stream s) info))
{:port port}))

Clojure - core.async interface for apache kafka

I am using clj-kafka, and I am trying to make a core.async interface to it in the REPL.
I am getting some messages, but my structure feels wrong : I either cannot stop receiving messages, or have to launch the go routine again to receive more messages.
Here is my attempt :
(defn consume [topic]
(let [consume-chan (chan)]
(with-resource [c (consumer config)]
shutdown
(go (doseq [m (messages c "test")]
(>! chan message) ;; should I check the return value?
)))
consume-chan)) ;; is it the right place to return a channel ?
(def consume-chan (consume "test"))
;;(close! consume-chan)
(go (>! consume-chan "hi")) ;; manual test, but I have some messages in Kafka already
(def cons-ch (go
(with-resource [c (consumer config)]
shutdown
(doseq [m (messages c "test")]
(>! consume-chan m))))) ;; should I check something here ?
;;(close! cons-ch)
(def go-ch
(go-loop []
(if-let [km (<! consume-chan)]
(do (println "Got a value in this loop:" km)
(recur))
(do (println "Stop recurring - channel closed")))))
;;(close! go-ch)
How do I consume a lazy-sequence of messages with a core.async interface ?
Here's what I would do:
>! and <! return nil if the channel is closed, so make sure that the loop exits when this happens - that way you can easily end the loop from the outside by closing the channel.
Use a try/catch to check for exceptions inside the go block, and make any exception the return value so that they don't get lost.
Check for exceptions on read values, to catch anything from inside the channel.
The go blocks return a channel, and the return value of the code inside the block (like the exceptions from above) will be put on the channel. Check these channels for exceptions, possibly to rethrow.
You can now write to a channel like this:
(defn write-seq-to-channel
[channel
values-seq]
(a/go
(try
(loop [values values-seq]
(when (seq values)
(when (a/>! channel (first values))
(recur (rest values)))))
(catch Throwable e
e))))
and you read like this:
(defn read-from-channel-and-print
[channel]
(a/go
(try
(loop []
(let [value (a/<! channel)]
(when value
(when (instance? Throwable value)
(throw value))
(println "Value read:" value)
(recur))))
(catch Throwable e
e))))
You will now have two channels, so use something like alts! or alts!! to check for your loops exiting. Close the channel when you are done.

Resources