webapp.sse-webapp.venice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of venice Show documentation
Show all versions of venice Show documentation
Venice, a sandboxed Lisp implemented in Java.
;; -----------------------------------------------------------------------------
;; Demo Server-Side-Events Service
;; -----------------------------------------------------------------------------
;;
(load-module :tomcat ['tomcat :as 'tc])
(load-module :ring)
(load-module :server-side-events ['server-side-events :as 'sse])
(import :java.io.IOException)
; ensure the Tomcat libs are on the classpath
(tc/check-required-libs)
;; -----------------------------------------------------------------------------
;; Event Producer
;; -----------------------------------------------------------------------------
(defn event-producer [queue stop last-event-id]
(let [start-id (if (and (some? last-event-id)
(match? last-event-id "[1-9][0-9]*"))
(long last-event-id)
1000)
id-counter (atom start-id)]
(println "Event producer running, starting at" start-id)
(thread #(try
(loop []
(when-not @stop
(let [id (str @id-counter)]
(swap! id-counter inc) ;; next id
(sleep 2000)
(when-not @stop
(println "Adding event " id)
(put! queue { :id id
:event "demo"
:data ["Counter ~{id}"] })
(recur)))))
(finally
(println "Event producer stopped."))))))
;; -----------------------------------------------------------------------------
;; Ring handler (publishes the events to the client)
;; -----------------------------------------------------------------------------
(defn events-demo [queue request]
;; If a connection drops, the client can send a "Last-Event-ID" HTTP header
;; with its last received id value to the server upon reconnecting, allowing
;; the server to determine where to resume the event stream.
(let [last-event-id (ring-util/get-request-header request "Last-Event-ID")
max-count (ring-util/get-request-long-parameter request "max-count" nil)
stop-events (atom false)
async_request (:async-request request) ;; HttpServletRequest
async_response (:async-response request) ;; HttpServletResponse
os (. async_response :getOutputStream)
pr (io/wrap-os-with-print-writer os :utf-8)]
(. async_response :setContentType "text/event-stream")
(. async_response :setCharacterEncoding "UTF-8")
(println "Last-Event-ID: " last-event-id)
(println "Max count: " max-count)
;; start the event producer
(event-producer queue stop-events last-event-id)
(try
(loop [counter 0]
(if (or (nil? max-count) (< counter max-count))
(let [event (take! queue)
sse-event (sse/render event)]
(println "Publishing event: " (:id event))
(print pr sse-event)
(flush pr)
(recur (inc counter)))
(println "Event max count of" max-count "reached")))
(catch [:cause-type :java.io.IOException] e
(println "Client closed connection." (ex-message e)))
(catch :Exception e
(println "Stopped. Error:" (ex-message e))))
;; stop the event producer
(reset! stop-events true)
(empty queue)
(println "Stopped serving events")
;; return nil to signal the :ring module to not send any further data
;; to the client!
nil))
;; -----------------------------------------------------------------------------
;; Middleware configuration
;; -----------------------------------------------------------------------------
(def sse-queue (queue))
(def routes [[:get "/events" (partial events-demo sse-queue)]])
(defn events-servlet []
(ring/create-servlet (-> (ring/match-routes routes) ; >--+
; |
(ring-mw/mw-dump-response) ; ^ |
(ring-mw/mw-dump-request) ; | |
(ring-mw/mw-request-counter) ; | |
(ring-mw/mw-add-session 3600) ; | |
(ring-mw/mw-print-uri) ; | |
(ring-mw/mw-debug :on)))) ; +--+
;; Tomcat server options
(def tomcat-opts { :await? false ;; do not block - return after start
:base-dir "."
:port 8080 })
;; start Tomcat (wires Tomcat with the ring servlets)
(let [server (tc/start [ [ (events-servlet)
{ :name "events-servlet"
:mapping "/*"
:async-support true } ] ]
tomcat-opts)]
(defn stop [] (tc/shutdown server)))
;; -----------------------------------------------------------------------------
(println "Tomcat started on port ~(:port tomcat-opts).")
(println "Stop it by calling: (stop)")
;; -----------------------------------------------------------------------------
;; Venice HTTP Client examples
;; -----------------------------------------------------------------------------
;; GET (demo)
(comment
;; run this Http client in another REPL. It kicks off server side streaming
;; of events and displays the received events. Stops the connection after
;; having received 10 events.
;; Connection close is initated by client
(do
(load-module :http-client-j8 ['http-client-j8 :as 'hc])
(let [response (hc/send :get
"http://localhost:8080/events"
:headers { "Accept" "text/event-stream"
"Cache-Control" "no-cache"
"Connection" "keep-alive" }
:conn-timeout 0
:read-timeout 0
:debug true)]
(println "Status:" (:http-status response))
(hc/process-server-side-events
response
(fn [type event event-count]
(case type
:opened (do (println "\nStreaming started")
:ok)
:data (do (println "Event: " (pr-str event))
;; only process 10 events
(if (< event-count 10) :ok :stop))
:closed (do (println "Streaming closed")
:ok))))))
)
;; GET (demo)
(comment
;; run this Http client in another REPL. It kicks off server side streaming
;; of events and displays the received events. The server will close the
;; connection after having sent 6 events due to the uri query parameter
;; "max-count=6". The client reacts on the closed connection by stopping
;; processing events.
;; Connection close is initated by server
(do
(load-module :http-client-j8 ['http-client-j8 :as 'hc])
(let [response (hc/send :get
"http://localhost:8080/events?max-count=6"
:headers { "Accept" "text/event-stream"
"Cache-Control" "no-cache"
"Connection" "keep-alive" }
:conn-timeout 0
:read-timeout 0
:debug true)]
(println "Status:" (:http-status response))
(hc/process-server-side-events
response
(fn [type event event-count]
(case type
:opened (do (println "\nStreaming started")
:ok)
:data (do (println "Event: " (pr-str event))
;; only process 10 events
(if (< event-count 10) :ok :stop))
:closed (do (println "Streaming closed")
:ok))))))
)
;; GET (demo)
(comment
;; run this Http client in another REPL. It kicks off server side streaming
;; of events at the "Last-Event-Id" 5000 and displays the received events.
;; Stops the connection after having received 10 events.
;; Connection close is initated by client
(do
(load-module :http-client-j8 ['http-client-j8 :as 'hc])
(let [response (hc/send :get
"http://localhost:8080/events"
:headers { "Accept" "text/event-stream"
"Cache-Control" "no-cache"
"Connection" "keep-alive"
"Last-Event-ID" "5000"}
:conn-timeout 0
:read-timeout 0
:debug true)]
(println "Status:" (:http-status response))
(hc/process-server-side-events
response
(fn [type event event-count]
(case type
:opened (do (println "\nStreaming started")
:ok)
:data (do (println "Event: " (pr-str event))
;; only process 10 events
(if (< event-count 10) :ok :stop))
:closed (do (println "Streaming closed")
:ok))))))
)