summaryrefslogtreecommitdiff
path: root/compojure-3.2/src/compojure/http/servlet.clj
diff options
context:
space:
mode:
Diffstat (limited to 'compojure-3.2/src/compojure/http/servlet.clj')
-rwxr-xr-xcompojure-3.2/src/compojure/http/servlet.clj129
1 files changed, 129 insertions, 0 deletions
diff --git a/compojure-3.2/src/compojure/http/servlet.clj b/compojure-3.2/src/compojure/http/servlet.clj
new file mode 100755
index 0000000..b6eef26
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/servlet.clj
@@ -0,0 +1,129 @@
+;; Copyright (c) James Reeves. All rights reserved.
+;; The use and distribution terms for this software are covered by the Eclipse
+;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
+;; can be found in the file epl-v10.html at the root of this distribution. By
+;; using this software in any fashion, you are agreeing to be bound by the
+;; terms of this license. You must not remove this notice, or any other, from
+;; this software.
+
+(ns compojure.http.servlet
+ "Functions for interfacing Compojure with the Java servlet standard."
+ (:use compojure.http.routes
+ compojure.http.request)
+ (:import [java.io File InputStream FileInputStream]
+ java.util.Map$Entry
+ [javax.servlet.http Cookie HttpServlet HttpServletRequest HttpServletResponse]
+ javax.servlet.ServletContext
+ org.apache.commons.io.IOUtils))
+
+;; Functions to pull information from the request object
+
+(defn- get-headers
+ "Creates a name/value map of all the request headers."
+ [#^HttpServletRequest request]
+ (reduce
+ (fn [headers name]
+ (assoc headers (.toLowerCase name) (.getHeader request name)))
+ {}
+ (enumeration-seq (.getHeaderNames request))))
+
+(defn- get-content-length
+ "Returns the content length, or nil if there is no content."
+ [#^HttpServletRequest request]
+ (let [length (.getContentLength request)]
+ (if (>= length 0)
+ length)))
+
+(defn create-request
+ "Create the request map from the HttpServletRequest object."
+ [#^HttpServletRequest request, #^HttpServlet servlet]
+ {:server-port (.getServerPort request)
+ :server-name (.getServerName request)
+ :remote-addr (.getRemoteAddr request)
+ :uri (.getRequestURI request)
+ :query-string (.getQueryString request)
+ :scheme (keyword (.getScheme request))
+ :request-method (keyword (.toLowerCase (.getMethod request)))
+ :headers (get-headers request)
+ :content-type (.getContentType request)
+ :content-length (get-content-length request)
+ :character-encoding (.getCharacterEncoding request)
+ :body (.getInputStream request)
+ ;; Custom non-Ring field:
+ :servlet-request request
+ :servlet-context (.getServletContext servlet)})
+
+;; Functions to set data in the response object
+
+(defn- set-headers
+ "Update a HttpServletResponse with a map of headers."
+ [#^HttpServletResponse response, headers]
+ (doseq [[key val-or-vals] headers]
+ (if (string? val-or-vals)
+ (.setHeader response key val-or-vals)
+ (doseq [val val-or-vals]
+ (.addHeader response key val))))
+ ; Some headers must be set through specific methods
+ (when-let [content-type (get headers "Content-Type")]
+ (.setContentType response content-type)))
+
+(defn- set-body
+ "Update a HttpServletResponse body with a String, ISeq, File or InputStream."
+ [#^HttpServletResponse response, body]
+ (cond
+ (string? body)
+ (with-open [writer (.getWriter response)]
+ (.println writer body))
+ (seq? body)
+ (with-open [writer (.getWriter response)]
+ (doseq [chunk body]
+ (.print writer (str chunk))
+ (.flush writer)))
+ (instance? InputStream body)
+ (with-open [out (.getOutputStream response)]
+ (IOUtils/copy body out)
+ (.close body)
+ (.flush out))
+ (instance? File body)
+ (with-open [stream (FileInputStream. body)]
+ (set-body response stream))))
+
+(defn update-servlet-response
+ "Update the HttpServletResponse using a response map."
+ [#^HttpServletResponse response, {:keys [status headers body]}]
+ (.setStatus response status)
+ (set-headers response headers)
+ (set-body response body))
+
+;; Functions that combine request and response handling
+
+(defn request-handler
+ "Handle incoming HTTP requests from a servlet."
+ [[servlet request response] routes]
+ (.setCharacterEncoding response "UTF-8")
+ (if-let [response-map (routes (create-request request servlet))]
+ (update-servlet-response response response-map)
+ (throw (NullPointerException.
+ "Handler returned nil (maybe no routes matched URI)"))))
+
+(definline servlet
+ "Create a servlet from a sequence of routes. Automatically updates if
+ the routes binding is redefined."
+ [routes]
+ `(proxy [HttpServlet] []
+ (~'service [request# response#]
+ (request-handler [~'this request# response#]
+ ~routes))))
+
+(defmacro defservice
+ "Defines a service method with an optional prefix suitable for being used by
+ genclass to compile a HttpServlet class.
+ e.g. (defservice my-routes)
+ (defservice \"my-prefix-\" my-routes)"
+ ([routes]
+ `(defservice "-" ~routes))
+ ([prefix routes]
+ `(defn ~(symbol (str prefix "service"))
+ [servlet# request# response#]
+ (request-handler [servlet# request# response#]
+ ~routes))))