add clj-browserchannel-jetty-adapter

This commit is contained in:
Gijs Stuurman 2012-04-07 21:02:59 +02:00
parent 8fce5be473
commit 9142fd6486
4 changed files with 127 additions and 0 deletions

View file

@ -0,0 +1,10 @@
/target
/lib
/classes
/checkouts
pom.xml
*.jar
*.class
.lein-deps-sum
.lein-failures
.lein-plugins

View file

@ -0,0 +1,16 @@
# clj-browserchannel-jetty-adapter
Jetty async adapter for BrowserChannel
## About
Written by:
Gijs Stuurman / [@thegeez][twt] / [Blog][blog] / [GitHub][github]
[twt]: http://twitter.com/thegeez
[blog]: http://thegeez.github.com
[github]: https://github.com/thegeez
### License
Copyright (c) 2012 Gijs Stuurman and released under an MIT license.

View file

@ -0,0 +1,9 @@
(defproject net.thegeez/clj-browserchannel-jetty-adapter "0.0.1"
:description "Jetty async adapter for BrowserChannel"
:url ""
:dependencies [[org.clojure/clojure "1.3.0"]
[ring/ring-core "1.1.0-beta3"]
[ring/ring-servlet "1.1.0-beta3" :exclusions [javax.servlet/servlet-api]]
[org.eclipse.jetty/jetty-server "8.1.2.v20120308"];; includes ssl
[net.thegeez/clj-browserchannel-server "0.0.1"]
])

View file

@ -0,0 +1,92 @@
(ns net.thegeez.jetty-async-adapter
"BrowserChannel adapter for the Jetty webserver, with async HTTP."
(:import (org.eclipse.jetty.server.handler AbstractHandler)
(org.eclipse.jetty.server Server Request Response)
(org.eclipse.jetty.server.nio SelectChannelConnector)
(org.eclipse.jetty.continuation Continuation ContinuationSupport ContinuationListener)
(org.eclipse.jetty.io EofException)
(javax.servlet.http HttpServletRequest))
(:require [ring.util.servlet :as servlet]
[net.thegeez.async-adapter :as async-adapter]))
;; Based on ring-jetty-async-adapter by Mark McGranaghan
;; (https://github.com/mmcgrana/ring/tree/jetty-async)
;; This has failed write support
(deftype JettyAsyncResponse [continuation]
async-adapter/IAsyncAdapter
(head [this status headers]
(doto (.getServletResponse continuation)
(servlet/set-status status)
(servlet/set-headers (assoc headers
"Transfer-Encoding" "chunked"))
(.flushBuffer)))
(write-chunk [this data]
(doto (.getWriter (.getServletResponse continuation))
(.write data)
(.flush))
(when (.checkError (.getWriter (.getServletResponse continuation)))
(throw async-adapter/ConnectionClosedException)))
(close [this]
(.complete continuation)))
(defn- proxy-handler
"Returns an Jetty Handler implementation for the given Ring handler."
[handler options]
(proxy [AbstractHandler] []
(handle [target ^Request base-request ^HttpServletRequest request response]
(let [request-map (servlet/build-request-map request)
response-map (handler request-map)]
(condp = (:async response-map)
nil
(do
(servlet/update-servlet-response response response-map)
(.setHandled base-request true))
:http
(let [reactor (:reactor response-map)
;; continuation lives until written to!
continuation (.startAsync request)
emit (JettyAsyncResponse. continuation)]
(.addContinuationListener continuation
(proxy [ContinuationListener] []
(onComplete [c] nil)
(onTimeout [c]
(.complete c))))
;; 4 minutes is google default
(.setTimeout continuation (get options :response-timeout (* 4 60 1000)))
(reactor emit)
))))))
(defn- create-server
"Construct a Jetty Server instance."
[options]
(let [connector (doto (SelectChannelConnector.)
(.setPort (options :port 80))
(.setHost (options :host)))
server (doto (Server.)
(.addConnector connector)
(.setSendDateHeader true))]
server))
(defn ^Server run-jetty-async
"Serve the given handler according to the options.
Options:
:configurator - A function called with the Server instance.
:port
:host
:join? - Block the caller: defaults to true.
:response-timeout - Timeout after which the server will close the connection"
[handler options]
(let [^Server s (create-server (dissoc options :configurator))]
(when-let [configurator (:configurator options)]
(configurator s))
(doto s
(.setHandler (proxy-handler handler options))
(.start))
(when (:join? options true)
(.join s))
s))