|
|
@@ -1,7 +1,9 @@ |
|
|
(ns ring.middleware.file-info
|
|
|
"Augment Ring File responses."
|
|
|
(:use [clojure.contrib.def :only (defvar-)])
|
|
|
- (:import java.io.File))
|
|
|
+ (:import java.io.File)
|
|
|
+ (:import java.text.SimpleDateFormat)
|
|
|
+ (:import java.util.SimpleTimeZone))
|
|
|
|
|
|
(defvar- base-mime-types
|
|
|
{"ai" "application/postscript"
|
|
|
@@ -70,18 +72,47 @@ |
|
|
[#^File file mime-types]
|
|
|
(get mime-types (get-extension file) "application/octet-stream"))
|
|
|
|
|
|
+(defvar- http-date-formatter
|
|
|
+ ;"A SimpleDateFormat instance, set to format using RFC 822/1123"
|
|
|
+ (let [formatter (SimpleDateFormat. "EEE, dd MMM yyyy HH:mm:ss ZZZ")]
|
|
|
+ (do
|
|
|
+ ; We use GMT because it makes testing much easier
|
|
|
+ (.setTimeZone formatter (SimpleTimeZone. 0 "GMT"))
|
|
|
+ formatter)))
|
|
|
+
|
|
|
+(defn- http-date
|
|
|
+ "Takes a Date or Long, returns a String in HTTP Date (RFC 822/1123) format"
|
|
|
+ [date]
|
|
|
+ (.format http-date-formatter date))
|
|
|
+
|
|
|
(defn wrap-file-info
|
|
|
"Wrap an app such that responses with a file a body will have
|
|
|
- corresponding Content-Type and Content-Length headers added if they can be
|
|
|
- determined from the file.
|
|
|
+ corresponding Content-Type, Content-Length, and Last Modified headers added
|
|
|
+ if they can be determined from the file.
|
|
|
If two arguments are given, the second is taken to be a map of file extensions
|
|
|
- to content types that will supplement the default, built-in map."
|
|
|
+ to content types that will supplement the default, built-in map.
|
|
|
+ If the request specifies If-Modified-Since in its header, and it is a literal
|
|
|
+ match of the string returned as Last-Modified, a 304 with no body will be
|
|
|
+ sent instead."
|
|
|
[app & [custom-mime-types]]
|
|
|
(let [mime-types (merge base-mime-types custom-mime-types)]
|
|
|
(fn [req]
|
|
|
(let [{:keys [headers body] :as response} (app req)]
|
|
|
(if (instance? File body)
|
|
|
- (assoc response :headers
|
|
|
- (assoc headers "Content-Length" (str (.length #^File body))
|
|
|
- "Content-Type" (guess-mime-type body mime-types)))
|
|
|
+ (let [
|
|
|
+ file-size (str (.length #^File body))
|
|
|
+ content-type (guess-mime-type body mime-types)
|
|
|
+ server-lmodified (http-date (.lastModified body))
|
|
|
+ client-lmodified (get (:headers req) "if-modified-since")
|
|
|
+ ;it'd be nice to have a real date comparison at some point
|
|
|
+ not-modified (= client-lmodified server-lmodified)]
|
|
|
+ (if not-modified
|
|
|
+ (assoc response :status 304 :body "" :headers
|
|
|
+ (assoc headers "Content-Length" "0"
|
|
|
+ "Content-Type" content-type
|
|
|
+ "Last-Modified" server-lmodified))
|
|
|
+ (assoc response :headers
|
|
|
+ (assoc headers "Content-Length" file-size
|
|
|
+ "Content-Type" content-type
|
|
|
+ "Last-Modified" server-lmodified))))
|
|
|
response)))))
|
0 comments on commit
849c1e6