Permalink
Fetching contributors…
Cannot retrieve contributors at this time
148 lines (130 sloc) 4.15 KB
(ns ring.util.response
"Generate and augment Ring responses."
(:import java.io.File)
(:require [clojure.java.io :as io]
[clojure.string :as str]))
(defn redirect
"Returns a Ring response for an HTTP 302 redirect."
[url]
{:status 302
:headers {"Location" url}
:body ""})
(defn redirect-after-post
"Returns a Ring response for an HTTP 303 redirect."
[url]
{:status 303
:headers {"Location" url}
:body ""})
(defn not-found
"Returns a 404 'not found' response."
[body]
{:status 404
:headers {}
:body body})
(defn response
"Returns a skeletal Ring response with the given body, status of 200, and no
headers."
[body]
{:status 200
:headers {}
:body body})
(defn- safe-path?
"Is a filepath safe for a particular root?"
[^String root ^String path]
(.startsWith (.getCanonicalPath (File. root path))
(.getCanonicalPath (File. root))))
(defn- find-index-file
"Search the directory for an index file."
[^File dir]
(first
(filter
#(.startsWith (.toLowerCase (.getName ^File %)) "index.")
(.listFiles dir))))
(defn- get-file
"Safely retrieve the correct file. See file-response for an
explanation of options."
[^String path opts]
(if-let [^File file (if-let [^String root (:root opts)]
(and (safe-path? root path) (File. root path))
(File. path))]
(cond
(.isDirectory file)
(and (:index-files? opts true) (find-index-file file))
(.exists file)
file)))
(defn file-response
"Returns a Ring response to serve a static file, or nil if an appropriate
file does not exist.
Options:
:root - take the filepath relative to this root path
:index-files? - look for index.* files in directories, defaults to true"
[filepath & [opts]]
(if-let [file (get-file filepath opts)]
(response file)))
;; In Clojure versions 1.2.0, 1.2.1 and 1.3.0, the as-file function
;; in clojure.java.io does not correctly decode special characters in
;; URLs (e.g. '%20' should be turned into ' ').
;;
;; See: http://dev.clojure.org/jira/browse/CLJ-885
;;
;; As a work-around, we'll backport the fix from the Clojure master
;; branch into url-as-file.
(defn- url-as-file [u]
(io/as-file
(str/replace
(.replace (.getFile u) \/ File/separatorChar)
#"%.."
(fn [escape]
(-> escape
(.substring 1 3)
(Integer/parseInt 16)
(char)
(str))))))
(defn resource-response
"Returns a Ring response to serve a packaged resource, or nil if the
resource does not exist.
Options:
:root - take the resource relative to this root"
[path & [opts]]
(let [path (-> (str (:root opts "") "/" path)
(.replace "//" "/")
(.replaceAll "^/" ""))]
(if-let [resource (io/resource path)]
(if (= "file" (.getProtocol resource))
(let [file (url-as-file resource)]
(if-not (.isDirectory file)
(response file)))
(response (io/input-stream resource))))))
(defn status
"Returns an updated Ring response with the given status."
[resp status]
(assoc resp :status status))
(defn header
"Returns an updated Ring response with the specified header added."
[resp name value]
(assoc-in resp [:headers name] (str value)))
(defn content-type
"Returns an updated Ring response with the a Content-Type header corresponding
to the given content-type."
[resp content-type]
(header resp "Content-Type" content-type))
(defn charset
"Returns an updated Ring response with the supplied charset added to the
Content-Type header."
[resp charset]
(update-in resp [:headers "Content-Type"]
(fn [content-type]
(-> (or content-type "text/plain")
(str/replace #";\s*charset=[^;]*" "")
(str "; charset=" charset)))))
(defn set-cookie
"Sets a cookie on the response. Requires the handler to be wrapped in the
wrap-cookies middleware."
[resp name value & [opts]]
(assoc-in resp [:cookies name] (merge {:value value} opts)))
(defn response?
"True if the supplied value is a valid response map."
[resp]
(and (map? resp)
(integer? (:status resp))
(map? (:headers resp))))