Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add clj-time Intervals/DateTime for :max-age and :expires (fixes #55)

- Add a test for :max-age and :expires to (wrap-cookies ...). There were
  no real tests to exercise the base input cases (int, string) which the
  comment block states. This test just makes sure it fulfils that
  contract.
- clj-time is now a project dependency
- :max-age accepts an Interval as input. Updated (write-attr-map ...) to
  accept an Interval (from JodaTime) as well as an int. The interface
  from clj-time is used.
- :expires accepts a DateTime object. It converts the DateTime object in
  the equivalent RFC822 which the cookie spec requires.
- Added pre-conditions for :max-age and :expires to make sure that they
  only accept Interval and DateTime, respectively.
  • Loading branch information...
commit 75c90aa29787c4b60e2bf331158303df30fd8af8 1 parent 80ddf5c
@KushalP KushalP authored
View
3  ring-core/project.clj
@@ -5,6 +5,7 @@
[commons-codec "1.6"]
[commons-io "2.1"]
[commons-fileupload "1.2.1"]
- [javax.servlet/servlet-api "2.5"]]
+ [javax.servlet/servlet-api "2.5"]
+ [clj-time "0.3.7"]]
:profiles
{:dev {:dependencies [[org.clojure/clojure-contrib "1.2.0"]]}})
View
19 ring-core/src/ring/middleware/cookies.clj
@@ -1,6 +1,9 @@
(ns ring.middleware.cookies
"Cookie manipulation."
- (:require [ring.util.codec :as codec]))
+ (:require [ring.util.codec :as codec])
+ (:use [clj-time.core :only (in-secs)]
+ [clj-time.format :only (formatters unparse)])
+ (:import (org.joda.time Interval DateTime)))
(def ^{:private true
:doc "HTTP token: 1*<any CHAR except CTLs or tspecials>. See RFC2068"}
@@ -91,7 +94,11 @@
"Is the attribute valid?"
[[key value]]
(and (contains? set-cookie-attrs key)
- (not (.contains (str value) ";"))))
+ (not (.contains (str value) ";"))
+ (case key
+ :max-age (or (instance? Interval value) (integer? value))
+ :expires (or (instance? DateTime value) (string? value))
+ true)))
(defn- write-attr-map
"Write a map of cookie attributes to a string."
@@ -100,9 +107,11 @@
(for [[key value] attrs]
(let [attr-name (name (set-cookie-attrs key))]
(cond
- (true? value) (str ";" attr-name)
- (false? value) ""
- :else (str ";" attr-name "=" value)))))
+ (instance? Interval value) (str ";" attr-name "=" (in-secs value))
+ (instance? DateTime value) (str ";" attr-name "=" (unparse (formatters :rfc822) value))
+ (true? value) (str ";" attr-name)
+ (false? value) ""
+ :else (str ";" attr-name "=" value)))))
(defn- write-cookies
"Turn a map of cookies into a seq of strings for a Set-Cookie header."
View
57 ring-core/test/ring/middleware/test/cookies.clj
@@ -1,6 +1,7 @@
(ns ring.middleware.test.cookies
(:use clojure.test
- ring.middleware.cookies))
+ ring.middleware.cookies
+ [clj-time.core :only (interval date-time)]))
(deftest wrap-cookies-basic-cookie
(let [req {:headers {"cookie" "a=b"}}
@@ -88,3 +89,57 @@
(let [response {:cookies {"a" {:value "foo" :invalid true}}}
handler (wrap-cookies (constantly response))]
(is (thrown? AssertionError (handler {})))))
+
+(deftest wrap-cookies-accepts-max-age
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :max-age 123}}
+ handler (constantly {:cookies cookies})
+ resp ((wrap-cookies handler) {})]
+ (is (= {"Set-Cookie" (list "a=b;Path=/;Secure;HttpOnly;Max-Age=123")}
+ (:headers resp)))))
+
+(deftest wrap-cookies-accepts-expires
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :expires "123"}}
+ handler (constantly {:cookies cookies})
+ resp ((wrap-cookies handler) {})]
+ (is (= {"Set-Cookie" (list "a=b;Path=/;Secure;HttpOnly;Expires=123")}
+ (:headers resp)))))
+
+(deftest wrap-cookies-accepts-max-age-from-clj-time
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :max-age (interval (date-time 2012)
+ (date-time 2015))}}
+ handler (constantly {:cookies cookies})
+ resp ((wrap-cookies handler) {})
+ max-age 94694400]
+ (is (= {"Set-Cookie" (list (str "a=b;Path=/;Secure;HttpOnly;Max-Age=" max-age))}
+ (:headers resp)))))
+
+(deftest wrap-cookies-accepts-expires-from-clj-time
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :expires (date-time 2015 12 31)}}
+ handler (constantly {:cookies cookies})
+ resp ((wrap-cookies handler) {})
+ expires "Thu, 31 Dec 2015 00:00:00 +0000"]
+ (is (= {"Set-Cookie" (list (str "a=b;Path=/;Secure;HttpOnly;Expires=" expires))}
+ (:headers resp)))))
+
+(deftest wrap-cookies-throws-exception-when-not-using-intervals-correctly
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :expires (interval (date-time 2012)
+ (date-time 2015))}}
+ handler (constantly {:cookies cookies})]
+ (is (thrown? AssertionError ((wrap-cookies handler) {})))))
+
+(deftest wrap-cookies-throws-exception-when-not-using-datetime-correctly
+ (let [cookies {"a" {:value "b", :path "/",
+ :secure true, :http-only true,
+ :max-age (date-time 2015 12 31)}}
+ handler (constantly {:cookies cookies})]
+ (is (thrown? AssertionError ((wrap-cookies handler) {})))))
Please sign in to comment.
Something went wrong with that request. Please try again.