summaryrefslogtreecommitdiff
path: root/compojure-3.2/src/compojure/http
diff options
context:
space:
mode:
Diffstat (limited to 'compojure-3.2/src/compojure/http')
-rwxr-xr-xcompojure-3.2/src/compojure/http/helpers.clj76
-rwxr-xr-xcompojure-3.2/src/compojure/http/middleware.clj131
-rwxr-xr-xcompojure-3.2/src/compojure/http/multipart.clj80
-rwxr-xr-xcompojure-3.2/src/compojure/http/request.clj109
-rwxr-xr-xcompojure-3.2/src/compojure/http/response.clj106
-rwxr-xr-xcompojure-3.2/src/compojure/http/routes.clj243
-rwxr-xr-xcompojure-3.2/src/compojure/http/servlet.clj129
-rwxr-xr-xcompojure-3.2/src/compojure/http/session.clj243
8 files changed, 1117 insertions, 0 deletions
diff --git a/compojure-3.2/src/compojure/http/helpers.clj b/compojure-3.2/src/compojure/http/helpers.clj
new file mode 100755
index 0000000..f60eeb9
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/helpers.clj
@@ -0,0 +1,76 @@
+;; 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.helpers
+ "Helper functions for things like redirection, serving files, 404s, etc."
+ (:use compojure.encodings
+ compojure.str-utils
+ clojure.contrib.def
+ clojure.contrib.str-utils
+ clojure.contrib.duck-streams)
+ (:import java.io.File))
+
+(defn- encode-cookie
+ "Encode sequence of key/value pairs a cookie."
+ [name value attrs]
+ (str-join "; "
+ (cons (str (urlencode name) "=" (urlencode value))
+ (for [[key val] attrs] (str* key "=" val)))))
+
+(defn set-cookie
+ "Return a Set-Cookie header."
+ ([name value]
+ {:headers {"Set-Cookie" (encode-cookie name value nil)}})
+ ([name value & attrs]
+ {:headers {"Set-Cookie" (encode-cookie name value (partition 2 attrs))}}))
+
+(defn content-type
+ "Retuns a Content-Type header given a type string."
+ [type]
+ {:headers {"Content-Type" type}})
+
+(defn redirect-to
+ "A shortcut for a '302 Moved' HTTP redirect."
+ [location]
+ [302 {:headers {"Location" location}}])
+
+(defn page-not-found
+ "A shortcut to create a '404 Not Found' HTTP response."
+ ([]
+ (page-not-found "public/404.html"))
+ ([filename]
+ [404 (File. filename)]))
+
+(defn- find-index-file
+ "Search the directory for index.*"
+ [dir]
+ (first
+ (filter
+ #(.startsWith (.toLowerCase (.getName %)) "index.")
+ (.listFiles dir))))
+
+(defn safe-path?
+ "Is a filepath safe for a particular root?"
+ [root path]
+ (.startsWith (.getCanonicalPath (File. root path))
+ (.getCanonicalPath (File. root))))
+
+(defn serve-file
+ "Attempts to serve up a static file from a directory, which defaults to
+ './public'. Nil is returned if the file does not exist. If the file is a
+ directory, the function looks for a file in the directory called 'index.*'."
+ ([path]
+ (serve-file "public" path))
+ ([root path]
+ (let [filepath (File. root path)]
+ (if (safe-path? root path)
+ (cond
+ (.isFile filepath)
+ filepath
+ (.isDirectory filepath)
+ (find-index-file filepath))))))
diff --git a/compojure-3.2/src/compojure/http/middleware.clj b/compojure-3.2/src/compojure/http/middleware.clj
new file mode 100755
index 0000000..f9a2dab
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/middleware.clj
@@ -0,0 +1,131 @@
+;; 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.middleware
+ "Various middleware functions."
+ (:use compojure.http.routes
+ compojure.str-utils
+ clojure.contrib.def
+ clojure.contrib.str-utils))
+
+(defn header-option
+ "Converts a header option KeyValue into a string."
+ [[key val]]
+ (cond
+ (true? val) (str* key)
+ (false? val) nil
+ :otherwise (str* key "=" val)))
+
+(defn header-options
+ "Converts a map into an HTTP header options string."
+ [m delimiter]
+ (str-join delimiter
+ (remove nil? (map header-option m))))
+
+(defn with-headers
+ "Merges a map of header name and values into the response. Overwrites
+ existing headers."
+ [handler headers]
+ (fn [request]
+ (if-let [response (handler request)]
+ (assoc response :headers
+ (merge (:headers response) headers)))))
+
+(defn with-cache-control
+ "Middleware to set the Cache-Control http header. Map entries with boolean
+ values either write their key if true, or nothing if false.
+ Example:
+ {:max-age 3600 :public false :must-revalidate true}
+ => Cache-Control: max-age=3600, must-revalidate"
+ [handler header-map]
+ (with-headers handler
+ {"Cache-Control" (header-options header-map ", ")}))
+
+(defn with-uri-rewrite
+ "Rewrites a request uri with the result of calling f with the
+ request's original uri. If f returns nil the handler is not called."
+ [handler f]
+ (fn [request]
+ (let [uri (:uri request)
+ rewrite (f uri)]
+ (if rewrite
+ (handler (assoc request :uri rewrite))
+ nil))))
+
+(defn- remove-or-nil-context
+ "Removes a context string from the front of a uri. If it wasn't there,
+ returns nil."
+ [uri context]
+ (if (.startsWith uri context)
+ (if-not (= uri context)
+ (subs uri (count context))
+ "/")
+ nil))
+
+(defn with-context
+ "Removes the context string from the beginning of the request uri
+ such that route matching is done without it. If the context is not
+ present, the handler will not be called."
+ [handler context]
+ (with-uri-rewrite handler #(remove-or-nil-context % context)))
+
+(defn- uri-snip-slash
+ "Removes a trailing slash from all uris except \"/\"."
+ [uri]
+ (if (and (not (= "/" uri))
+ (.endsWith uri "/"))
+ (chop uri)
+ uri))
+
+(defn ignore-trailing-slash
+ "Makes routes match regardless of whether or not a uri ends in a slash."
+ [handler]
+ (with-uri-rewrite handler uri-snip-slash))
+
+(defvar default-mimetypes
+ {"css" "text/css"
+ "gif" "image/gif"
+ "gz" "application/gzip"
+ "htm" "text/html"
+ "html" "text/html"
+ "jpg" "image/jpeg"
+ "js" "text/javascript"
+ "pdf" "application/pdf"
+ "png" "image/png"
+ "swf" "application/x-shockwave-flash"
+ "txt" "text/plain"
+ "xml" "text/xml"
+ "zip" "application/zip"}
+ "Default mimetype map used by with-mimetypes.")
+
+(defn- extension
+ "Returns the text after the last . of a String or nil."
+ [s]
+ (second (re-find #"\.(\w*$)" s)))
+
+(defn- request-mimetype
+ "Derives the mimetype from a request. See with-mimetypes for options."
+ [request options]
+ (let [default (or (:default options) "text/html")]
+ (if-let [ext (extension (:uri request))]
+ (let [mimetypes (or (:mimetypes options) default-mimetypes)]
+ (get mimetypes ext default))
+ default)))
+
+(defn with-mimetypes
+ "Middleware to add the proper Content-Type header based on the uri of
+ the request. options is a map containing a :mimetype map of extension
+ to type and a :default mime type. If :mimetype is not provided, a default
+ map with common mime types will be used. If :default is not provided,
+ \"text/html\" is used."
+ ([handler]
+ (with-mimetypes handler {}))
+ ([handler options]
+ (fn [request]
+ (let [mimetype (request-mimetype request options)]
+ ((with-headers handler {"Content-Type" mimetype}) request)))))
diff --git a/compojure-3.2/src/compojure/http/multipart.clj b/compojure-3.2/src/compojure/http/multipart.clj
new file mode 100755
index 0000000..afd6737
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/multipart.clj
@@ -0,0 +1,80 @@
+;; 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.multipart
+ "Add multipart form handling to Compojure. Relies on the Apache Commons
+ FileUpload library."
+ (:use clojure.contrib.def
+ compojure.map-utils)
+ (:import [org.apache.commons.fileupload FileUpload RequestContext]
+ [org.apache.commons.fileupload.disk DiskFileItemFactory DiskFileItem]))
+
+(defn multipart-form?
+ "Does a request have a multipart form?"
+ [request]
+ (if-let [content-type (:content-type request)]
+ (.startsWith content-type "multipart/form-data")))
+
+(defvar- file-upload
+ (FileUpload.
+ (doto (DiskFileItemFactory.)
+ (.setSizeThreshold -1)
+ (.setFileCleaningTracker nil)))
+ "Uploader class to save multipart form values to temporary files.")
+
+(defn- request-context
+ "Create a RequestContext object from a request map."
+ [request]
+ (proxy [RequestContext] []
+ (getContentType [] (:content-type request))
+ (getContentLength [] (:content-length request))
+ (getCharacterEncoding [] (:character-encoding request))
+ (getInputStream [] (:body request))))
+
+(defn- file-map
+ "Create a file map from a DiskFileItem."
+ [#^DiskFileItem item]
+ {:disk-file-item item
+ :filename (.getName item)
+ :size (.getSize item)
+ :content-type (.getContentType item)
+ :tempfile (.getStoreLocation item)})
+
+(defn parse-multipart-params
+ "Parse a map of multipart parameters from the request."
+ [request]
+ (reduce
+ (fn [param-map, #^DiskFileItem item]
+ (assoc-vec param-map
+ (keyword (.getFieldName item))
+ (if (.isFormField item)
+ (if (zero? (.getSize item))
+ ""
+ (.getString item))
+ (file-map item))))
+ {}
+ (.parseRequest
+ file-upload
+ (request-context request))))
+
+(defn get-multipart-params
+ "Retrieve multipart params from the request."
+ [request]
+ (if (multipart-form? request)
+ (parse-multipart-params request)
+ {}))
+
+(defn with-multipart
+ "Decorate a Ring handler with multipart parameters."
+ [handler]
+ (fn [request]
+ (let [params (get-multipart-params request)
+ request (-> request
+ (assoc :multipart-params params)
+ (assoc :params (merge (request :params) params)))]
+ (handler request))))
diff --git a/compojure-3.2/src/compojure/http/request.clj b/compojure-3.2/src/compojure/http/request.clj
new file mode 100755
index 0000000..8c09616
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/request.clj
@@ -0,0 +1,109 @@
+;; 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.request
+ "Functions for pulling useful data out of a HTTP request map."
+ (:use compojure.control
+ compojure.encodings
+ compojure.map-utils
+ compojure.str-utils
+ clojure.contrib.duck-streams
+ clojure.contrib.str-utils)
+ (:import java.net.URLDecoder
+ java.io.InputStreamReader))
+
+(defn- parse-params
+ "Parse parameters from a string into a map."
+ [param-string separator]
+ (reduce
+ (fn [param-map s]
+ (if-let [[_ key val] (re-matches #"([^=]+)=(.*)" s)]
+ (assoc-vec param-map
+ (keyword (urldecode key))
+ (urldecode (or val "")))
+ param-map))
+ {}
+ (remove blank?
+ (re-split separator param-string))))
+
+(defn parse-query-params
+ "Parse parameters from the query string."
+ [request]
+ (if-let [query (request :query-string)]
+ (parse-params query #"&")))
+
+(defn get-character-encoding
+ "Get the character encoding, or use the default from duck-streams."
+ [request]
+ (or (request :character-encoding) *default-encoding*))
+
+(defn- slurp-body
+ "Slurp the request body into a string."
+ [request]
+ (let [encoding (get-character-encoding request)]
+ (if-let [body (request :body)]
+ (slurp* (InputStreamReader. body encoding)))))
+
+(defn urlencoded-form?
+ "Does a request have a urlencoded form?"
+ [request]
+ (if-let [type (:content-type request)]
+ (.startsWith type "application/x-www-form-urlencoded")))
+
+(defn parse-form-params
+ "Parse urlencoded form parameters from the request body."
+ [request]
+ (if (urlencoded-form? request)
+ (if-let [body (slurp-body request)]
+ (parse-params body #"&"))))
+
+(defn- get-merged-params
+ "Get a map of all the parameters merged together."
+ [request]
+ (merge (:query-params request)
+ (:form-params request)
+ (:params request)))
+
+(defn- assoc-func
+ "Associate the result of a (func request) with a key on the request map."
+ [request key func]
+ (if (contains? request key)
+ request
+ (assoc request key (or (func request) {}))))
+
+(defn assoc-params
+ "Associate urlencoded parameters with a request. The following keys are added
+ to the request map: :query-params, :form-params and :params."
+ [request]
+ (-> request
+ (assoc-func :query-params parse-query-params)
+ (assoc-func :form-params parse-form-params)
+ (assoc-func :params get-merged-params)))
+
+(defn with-request-params
+ "Decorator that adds urlencoded parameters to the request map."
+ [handler]
+ (fn [request]
+ (handler (assoc-params request))))
+
+(defn parse-cookies
+ "Pull out a map of cookies from a request map."
+ [request]
+ (if-let [cookies (get-in request [:headers "cookie"])]
+ (parse-params cookies #";\s*")))
+
+(defn assoc-cookies
+ "Associate cookies with a request map."
+ [request]
+ (assoc-func request :cookies parse-cookies))
+
+(defn with-cookies
+ "Decorator that adds cookies to a request map."
+ [handler]
+ (fn [request]
+ (handler (assoc-cookies request))))
diff --git a/compojure-3.2/src/compojure/http/response.clj b/compojure-3.2/src/compojure/http/response.clj
new file mode 100755
index 0000000..269e71d
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/response.clj
@@ -0,0 +1,106 @@
+;; 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.response
+ "Parse a Compojure route return value into a HTTP response map."
+ (:use clojure.contrib.def
+ compojure.http.helpers)
+ (:import clojure.lang.Fn
+ clojure.lang.IPersistentVector
+ java.util.Map
+ clojure.lang.ISeq
+ java.io.File
+ java.io.InputStream
+ java.net.URL
+ clojure.lang.Keyword))
+
+(defmulti update-response
+ "Update a response with an object. The type of object determines how the
+ response is updated."
+ (fn [request reponse update]
+ (class update)))
+
+(defmethod update-response Integer
+ [request response status]
+ (assoc response :status status))
+
+(defmethod update-response String
+ [request response body]
+ (let [headers (merge (:headers (content-type "text/html")) (:headers response))
+ response (assoc response :headers headers)]
+ (if (string? (:body response))
+ (merge-with str response {:body body})
+ (assoc response :body body))))
+
+(defmethod update-response ISeq
+ [request response sequence]
+ (assoc response :body sequence))
+
+(defmethod update-response File
+ [request response file]
+ (assoc response :body file))
+
+(defmethod update-response InputStream
+ [request response stream]
+ (assoc response :body stream))
+
+(defmethod update-response URL
+ [request response url]
+ (assoc response :body (.openStream url)))
+
+(defmethod update-response IPersistentVector
+ [request response updates]
+ (reduce (partial update-response request) response updates))
+
+(defmethod update-response Keyword
+ [request response kw]
+ (if (not= kw :next)
+ (update-response request response (str kw))))
+
+(defmethod update-response Fn
+ [request response func]
+ (update-response request response (func request)))
+
+(defmethod update-response nil
+ [request response _]
+ response)
+
+(defn- merge-map
+ "Merges an inner map in 'from' into 'to'"
+ [to key from]
+ (merge-with merge to (select-keys from [key])))
+
+(defn- merge-bodies
+ "Merge the bodies in 'from' into 'to'."
+ [to from]
+ (let [from (select-keys from [:body])]
+ (if (and (-> to :body string?) (-> from :body string?))
+ (merge-with str to from)
+ (merge to from))))
+
+(defn- merge-rest
+ "Merge everything but the headers, session and body."
+ [to from]
+ (merge to (dissoc from :headers :session :body)))
+
+(defmethod update-response Map
+ [request response update-map]
+ (-> response
+ (merge-map :headers update-map)
+ (merge-map :session update-map)
+ (merge-bodies update-map)
+ (merge-rest update-map)))
+
+(defvar default-response
+ {:status 200, :headers {}}
+ "Default HTTP response map.")
+
+(defn create-response
+ "Create a new response map from an update object, x."
+ [request x]
+ (update-response request default-response x))
diff --git a/compojure-3.2/src/compojure/http/routes.clj b/compojure-3.2/src/compojure/http/routes.clj
new file mode 100755
index 0000000..d722a57
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/routes.clj
@@ -0,0 +1,243 @@
+;; 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.routes
+ "Macros and functions for compiling routes in the form (method path & body)
+ into stand-alone functions that return the return value of the body, or the
+ keyword :next if they don't match."
+ (:use compojure.http.request
+ compojure.http.response
+ compojure.http.session
+ compojure.str-utils
+ compojure.map-utils
+ compojure.control
+ compojure.encodings)
+ (:import java.util.regex.Pattern
+ java.util.Map))
+
+;; Functions for lexing a string
+
+(defn- lex-1
+ "Lex one symbol from a string, and return the symbol and trailing source."
+ [src clauses]
+ (some
+ (fn [[re action]]
+ (let [matcher (re-matcher re src)]
+ (if (.lookingAt matcher)
+ [(if (fn? action) (action matcher) action)
+ (.substring src (.end matcher))])))
+ (partition 2 clauses)))
+
+(defn- lex
+ "Lex a string into tokens by matching against regexs and evaluating
+ the matching associated function."
+ [src & clauses]
+ (loop [results []
+ src src
+ clauses clauses]
+ (if-let [[result src] (lex-1 src clauses)]
+ (let [results (conj results result)]
+ (if (= src "")
+ results
+ (recur results src clauses))))))
+
+;; Functions for matching URIs using a syntax borrowed from Ruby frameworks
+;; like Sinatra and Rails.
+
+(defstruct uri-matcher
+ :regex
+ :keywords)
+
+(defn compile-uri-matcher
+ "Compile a path string using the routes syntax into a uri-matcher struct."
+ [path]
+ (let [splat #"\*"
+ word #":([A-Za-z][\w-]*)"
+ literal #"(:[^A-Za-z*]|[^:*])+"]
+ (struct uri-matcher
+ (re-pattern
+ (apply str
+ (lex path
+ splat "(.*?)"
+ word "([^/.,;?]+)"
+ literal #(re-escape (.group %)))))
+ (vec
+ (remove nil?
+ (lex path
+ splat :*
+ word #(keyword (.group % 1))
+ literal nil))))))
+
+;; Don't compile paths more than once.
+(decorate-with memoize compile-uri-matcher)
+
+(defmulti compile-matcher
+ "Compile a string or regex into a form suitable for buing passed to the
+ match-uri function."
+ class)
+
+(defmethod compile-matcher String
+ [path]
+ (compile-uri-matcher path))
+
+(defmethod compile-matcher Pattern
+ [re]
+ re)
+
+(defn- assoc-keywords-with-groups
+ "Create a hash-map from a series of regex match groups and a collection of
+ keywords."
+ [groups keywords]
+ (reduce
+ (fn [m [k v]] (assoc-vec m k v))
+ {}
+ (map vector keywords groups)))
+
+(defmulti match-uri
+ "Match a URL against a compiled URI-matcher or a regular expression. Returns
+ the matched URI keywords as a map, or the matched regex groups as a vector."
+ (fn [matcher uri] (class matcher)))
+
+(defmethod match-uri Map
+ [uri-matcher uri]
+ (let [matcher (re-matcher (uri-matcher :regex) (or uri "/"))]
+ (if (.matches matcher)
+ (assoc-keywords-with-groups
+ (map urldecode (re-groups* matcher))
+ (uri-matcher :keywords)))))
+
+(defmethod match-uri Pattern
+ [uri-pattern uri]
+ (let [matches (re-matches uri-pattern (or uri "/"))]
+ (if matches
+ (if (vector? matches)
+ (vec (map urldecode (rest matches)))
+ []))))
+
+(defn match-method
+ "True if this request matches the supplied method."
+ [method request]
+ (let [request-method (request :request-method)
+ form-method (-> request :form-params :_method)]
+ (or (nil? method)
+ (if (and form-method (= request-method :post))
+ (= (upcase-name method) form-method)
+ (= method request-method)))))
+
+(defn request-url
+ "Return the complete URL for the request."
+ [request]
+ (str
+ (name (:scheme request))
+ "://"
+ (get-in request [:headers "host"])
+ (:uri request)))
+
+(defn absolute-url?
+ "True if the string is an absolute URL."
+ [s]
+ (re-find #"^[a-z+.-]+://" s))
+
+(defn get-matcher-uri
+ "Get the appropriate request URI for the given path pattern."
+ [path request]
+ (if (and (string? path) (absolute-url? path))
+ (request-url request)
+ (:uri request)))
+
+(defmacro request-matcher
+ "Compiles a function to match a HTTP request against the supplied method
+ and path template. Returns a map of the route parameters if the is a match,
+ nil otherwise. Precompiles the route when supplied with a literal string."
+ [method path]
+ (let [matcher (if (or (string? path) (instance? Pattern path))
+ (compile-matcher path)
+ `(compile-matcher ~path))]
+ `(fn [request#]
+ (and
+ (match-method ~method request#)
+ (match-uri ~matcher (get-matcher-uri ~path request#))))))
+
+;; Functions and macros for generating routing functions. A routing function
+;; returns :next if it doesn't match, and any other value if it does.
+
+(defmacro with-request-bindings
+ "Add shortcut bindings for the keys in a request map."
+ [request & body]
+ `(let [~'request ~request
+ ~'params (:params ~'request)
+ ~'cookies (:cookies ~'request)
+ ~'session (:session ~'request)
+ ~'flash (:flash ~'request)]
+ ~@body))
+
+(defn assoc-route-params
+ "Associate route parameters with the request map."
+ [request params]
+ (-> request
+ (assoc :route-params params)
+ (assoc :params (merge (:params request)
+ (if (map? params) params)))))
+
+(defn compile-route
+ "Compile a route in the form (method path & body) into a function."
+ [method path body]
+ `(let [matcher# (request-matcher ~method ~path)]
+ (fn [request#]
+ (if-let [route-params# (matcher# request#)]
+ (let [request# (assoc-route-params request# route-params#)]
+ (create-response request#
+ (with-request-bindings request# ~@body)))))))
+
+(defn routes*
+ "Create a Ring handler by combining several handlers into one."
+ [& handlers]
+ (fn [request]
+ (some #(% request) handlers)))
+
+(defn routes
+ "Create a Ring handler by combining several routes into one. Adds parameters
+ and cookies to the request."
+ [& handlers]
+ (-> (apply routes* handlers)
+ with-request-params
+ with-cookies))
+
+;; Macros for easily creating a compiled routing table
+
+(defmacro defroutes
+ "Define a Ring handler function from a sequence of routes. Takes an optional
+ doc-string."
+ [name doc? & routes]
+ (let [[name & routes] (apply-doc name doc? routes)]
+ `(def ~name
+ (routes ~@routes))))
+
+(defmacro GET "Generate a GET route."
+ [path & body]
+ (compile-route :get path body))
+
+(defmacro POST "Generate a POST route."
+ [path & body]
+ (compile-route :post path body))
+
+(defmacro PUT "Generate a PUT route."
+ [path & body]
+ (compile-route :put path body))
+
+(defmacro DELETE "Generate a DELETE route."
+ [path & body]
+ (compile-route :delete path body))
+
+(defmacro HEAD "Generate a HEAD route."
+ [path & body]
+ (compile-route :head path body))
+
+(defmacro ANY "Generate a route that matches any method."
+ [path & body]
+ (compile-route nil path body))
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))))
diff --git a/compojure-3.2/src/compojure/http/session.clj b/compojure-3.2/src/compojure/http/session.clj
new file mode 100755
index 0000000..d176dec
--- /dev/null
+++ b/compojure-3.2/src/compojure/http/session.clj
@@ -0,0 +1,243 @@
+;; 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.session
+ "Functions for creating and updating HTTP sessions."
+ (:use compojure.str-utils
+ compojure.http.helpers
+ compojure.http.request
+ compojure.http.response
+ compojure.encodings
+ compojure.crypto
+ clojure.contrib.except))
+
+;; Override these mulitmethods to create your own session storage.
+;; Uses the Compojure repository pattern.
+
+(defmulti create-session
+ "Create a new session map. Should not attempt to save the session."
+ (fn [repository] (:type repository)))
+
+(defmulti read-session
+ "Read in the session using the supplied data. Usually the data is a key used
+ to find the session in a store."
+ (fn [repository data] (:type repository)))
+
+(defmulti write-session
+ "Write a new or existing session to the session store."
+ (fn [repository session] (:type repository)))
+
+(defmulti destroy-session
+ "Remove the session from the session store."
+ (fn [repository session] (:type repository)))
+
+(defmulti session-cookie
+ "Return the session data to be stored in the cookie. This is usually the
+ session ID."
+ (fn [repository new? session] (:type repository)))
+
+;; Default implementations of create-session and set-session-cookie
+
+(defmethod create-session :default
+ [repository]
+ {:id (gen-uuid)})
+
+(defmethod session-cookie :default
+ [repository new? session]
+ (if new?
+ (session :id)))
+
+;; In memory sessions
+
+(def memory-sessions (ref {}))
+
+(defmethod read-session :memory
+ [repository id]
+ (@memory-sessions id))
+
+(defmethod write-session :memory
+ [repository session]
+ (dosync
+ (alter memory-sessions
+ assoc (session :id) session)))
+
+(defmethod destroy-session :memory
+ [repository session]
+ (dosync
+ (alter memory-sessions
+ dissoc (session :id))))
+
+;; Cookie sessions
+
+(def default-session-key
+ (delay (gen-secret-key {:key-size 128})))
+
+(defn- get-session-key
+ "Get the session key from the repository or use the default key."
+ [repository]
+ (force (repository :session-key default-session-key)))
+
+(defmethod create-session :cookie
+ [repository]
+ {})
+
+(defmethod session-cookie :cookie
+ [repository new? session]
+ (let [session-key (get-session-key repository)
+ cookie-data (seal session-key session)]
+ (if (> (count cookie-data) 4000)
+ (throwf "Session data exceeds 4K")
+ cookie-data)))
+
+(defmethod read-session :cookie
+ [repository data]
+ (unseal (get-session-key repository) data))
+
+(defmethod write-session :cookie
+ [repository session])
+
+(defmethod destroy-session :cookie
+ [repository session])
+
+;; Session middleware
+
+(defn timestamp-after
+ "Return the current time plus seconds as milliseconds."
+ [seconds]
+ (+ (* seconds 1000) (System/currentTimeMillis)))
+
+(defn assoc-expiry
+ "Associate an :expires-at key with the session if the session repository
+ contains the :expires key."
+ [repository session]
+ (if-let [expires (:expires repository)]
+ (assoc session :expires-at (timestamp-after expires))
+ session))
+
+(defn session-expired?
+ "True if this session's timestamp is in the past."
+ [session]
+ (if-let [expires-at (:expires-at session)]
+ (< expires-at (System/currentTimeMillis))))
+
+(defn- get-session
+ "Retrieve the session using the 'session' cookie in the request."
+ [repository request]
+ (if-let [session-data (-> request :cookies :compojure-session)]
+ (read-session repository session-data)))
+
+(defn- assoc-new-session
+ "Associate a new session with a request."
+ [repository request]
+ (assoc request
+ :session (assoc-expiry repository (create-session repository))
+ :new-session? true))
+
+(defn assoc-session
+ "Associate the session with the request."
+ [request repository]
+ (if-let [session (get-session repository request)]
+ (if (session-expired? session)
+ (do
+ (destroy-session repository session)
+ (assoc-new-session repository request))
+ (assoc request :session
+ (assoc-expiry repository session)))
+ (assoc-new-session repository request)))
+
+(defn assoc-flash
+ "Associate the session flash with the request and remove it from the
+ session."
+ [request]
+ (let [session (:session request)]
+ (-> request
+ (assoc :flash (session :flash {}))
+ (assoc :session (dissoc session :flash)))))
+
+(defn set-session-cookie
+ "Set the session cookie on the response if required."
+ [repository request response session]
+ (let [new? (:new-session? request)
+ cookie (session-cookie repository new? session)
+ update (set-cookie :compojure-session cookie
+ :path (repository :path "/"))]
+ (if cookie
+ (update-response request response update)
+ response)))
+
+(defn save-handler-session
+ "Save the session for a handler if required."
+ [repository request response session]
+ (when (and (contains? response :session)
+ (nil? (response :session)))
+ (destroy-session repository session))
+ (when (or (:session response)
+ (:new-session? request)
+ (not-empty (:flash request))
+ (contains? repository :expires))
+ (write-session repository session)))
+
+(defn- keyword->repository
+ "If the argument is a keyword, expand it into a repository map."
+ [repository]
+ (if (keyword? repository)
+ {:type repository}
+ repository))
+
+(defn with-session
+ "Wrap a handler in a session of the specified type. Session type defaults to
+ :memory if not supplied."
+ ([handler]
+ (with-session handler :memory))
+ ([handler repository]
+ (fn [request]
+ (let [repo (keyword->repository repository)
+ request (-> request (assoc-cookies)
+ (assoc-session repo)
+ (assoc-flash))
+ response (handler request)
+ session (if (contains? response :session)
+ (:session response)
+ (:session request))]
+ (when response
+ (save-handler-session repo request response session)
+ (set-session-cookie repo request response session))))))
+
+;; Useful functions for modifying the session
+
+(defn set-session
+ "Return a response map with the session set."
+ [session]
+ {:session session})
+
+(defn clear-session
+ "Set the session to nil."
+ []
+ (set-session nil))
+
+(defn alter-session
+ "Use a function to alter the session."
+ [func & args]
+ (fn [request]
+ (set-session
+ (apply func (request :session) args))))
+
+(defn session-assoc
+ "Associate key value pairs with the session."
+ [& keyvals]
+ (apply alter-session assoc keyvals))
+
+(defn session-dissoc
+ "Dissociate keys from the session."
+ [& keys]
+ (apply alter-session dissoc keys))
+
+(defn flash-assoc
+ "Associate key value pairs with the session flash."
+ [& keyvals]
+ (alter-session merge {:flash (apply hash-map keyvals)}))