Permalink
Browse files

Fixed cookies to work with non-standard cookie parsing in browsers

  • Loading branch information...
1 parent 70d71e6 commit ef91c8fa47d4ab20affb8270a0d7a6a8a343725f @weavejester weavejester committed May 16, 2010
View
32 ring-core/src/ring/middleware/cookies.clj
@@ -1,7 +1,8 @@
(ns ring.middleware.cookies
"Cookie manipulation."
- (:use [clojure.contrib.def :only (defvar-)])
- (:require [clojure.contrib.java-utils :as ju]))
+ (:use [clojure.contrib.def :only (defvar-)]
+ [clojure.contrib.java-utils :only (as-str)]
+ ring.util.codec))
(defvar- re-token #"[!#$%&'*\-+.0-9A-Z\^_`a-z\|~]+"
"HTTP token: 1*<any CHAR except CTLs or tspecials>. See RFC2068")
@@ -38,9 +39,10 @@
"Turn quoted strings into normal Clojure strings using read-string."
[cookies]
(for [[name value] cookies]
- (if (.startsWith #^String value "\"")
- [name (read-string value)]
- [name value])))
+ (let [value (url-decode value)]
+ (if (.startsWith #^String value "\"")
+ [name (read-string value)]
+ [name value]))))
(defn- get-cookie
"Get a single cookie from a sequence of cookie-values"
@@ -72,29 +74,35 @@
(dissoc "$Version"))
{}))
-(defn- write-attr
- "Turn a name-value pair into a cookie attr string."
+(defn- write-value
+ "Write the main cookie value."
[name value]
- (str (ju/as-str name) "=" (pr-str value)))
+ (str (as-str name) "=" (url-encode value)))
+
+(defn- valid-attr?
+ "Is the attribute valid?"
+ [[_ value]]
+ (not (.contains (str value) ";")))
(defn- write-attr-map
"Write a map of cookie attributes to a string."
[attrs]
+ {:pre [(every? valid-attr? attrs)]}
(for [[key value] attrs]
(let [name (set-cookie-attrs key)]
(cond
- (true? value) (str ";" name)
+ (true? value) (str ";" (as-str name))
(false? value) ""
- :else (str ";" (write-attr name value))))))
+ :else (str ";" (as-str name) "=" value)))))
(defn- write-cookies
"Turn a map of cookies into a seq of strings for a Set-Cookie header."
[cookies]
(for [[name value] cookies]
(if (map? value)
- (apply str (write-attr name (:value value))
+ (apply str (write-value name (:value value))
(write-attr-map (dissoc value :value)))
- (write-attr name value))))
+ (write-value name value))))
(defn- set-cookies
"Add a Set-Cookie header to a response if there is a :cookies key."
View
19 ring-core/test/ring/middleware/cookies_test.clj
@@ -34,29 +34,40 @@
(deftest wrap-cookies-set-basic-cookie
(let [handler (constantly {:cookies {"a" "b"}})
resp ((wrap-cookies handler) {})]
- (is (= {"Set-Cookie" (list "a=\"b\"")}
+ (is (= {"Set-Cookie" (list "a=b")}
(:headers resp)))))
(deftest wrap-cookies-set-multiple-cookies
(let [handler (constantly {:cookies {"a" "b", "c" "d"}})
resp ((wrap-cookies handler) {})]
- (is (= {"Set-Cookie" (list "a=\"b\"" "c=\"d\"")}
+ (is (= {"Set-Cookie" (list "a=b" "c=d")}
(:headers resp)))))
(deftest wrap-cookies-set-keyword-cookie
(let [handler (constantly {:cookies {:a "b"}})
resp ((wrap-cookies handler) {})]
- (is (= {"Set-Cookie" (list "a=\"b\"")}
+ (is (= {"Set-Cookie" (list "a=b")}
(:headers resp)))))
(deftest wrap-cookies-set-extra-attrs
(let [cookies {"a" {:value "b", :path "/", :secure true}}
handler (constantly {:cookies cookies})
resp ((wrap-cookies handler) {})]
- (is (= {"Set-Cookie" (list "a=\"b\";Path=\"/\";Secure")}
+ (is (= {"Set-Cookie" (list "a=b;Path=/;Secure")}
(:headers resp)))))
(deftest wrap-cookies-always-assocs-map
(let [req {:headers {}}
resp ((wrap-cookies :cookies) req)]
(is (= {} resp))))
+
+(deftest wrap-cookies-read-urlencoded
+ (let [req {:headers {"cookie" "a=hello+world"}}
+ resp ((wrap-cookies :cookies) req)]
+ (is (= {"a" {:value "hello world"}} resp))))
+
+(deftest wrap-cookies-set-urlencoded-cookie
+ (let [handler (constantly {:cookies {"a" "hello world"}})
+ resp ((wrap-cookies handler) {})]
+ (is (= {"Set-Cookie" (list "a=hello+world")}
+ (:headers resp)))))
View
8 ring-core/test/ring/middleware/session_test.clj
@@ -43,7 +43,7 @@
handler (wrap-session handler {:store store})
response (handler {:cookies {}})]
(is (= (get-in response [:headers "Set-Cookie"])
- ["ring-session=\"foo:bar\""]))))
+ ["ring-session=foo%3Abar"]))))
(deftest session-delete-outputs-cookie
(let [store {:read (constantly {:foo "bar"})
@@ -52,7 +52,7 @@
handler (wrap-session handler {:store store})
response (handler {:cookies {"ring-session" {:value "foo:bar"}}})]
(is (= (get-in response [:headers "Set-Cookie"])
- ["ring-session=\"deleted\""]))))
+ ["ring-session=deleted"]))))
(deftest session-cookie-has-attributes
(let [store {:read (constantly {})
@@ -61,7 +61,7 @@
handler (wrap-session handler {:store store :cookie-attrs {:max-age 5}})
response (handler {:cookies {}})]
(is (= (get-in response [:headers "Set-Cookie"])
- ["ring-session=\"foo:bar\";Max-Age=5"]))))
+ ["ring-session=foo%3Abar;Max-Age=5"]))))
(deftest session-does-not-clobber-response-cookies
(let [store {:read (constantly {})
@@ -71,4 +71,4 @@
handler (wrap-session handler {:store store :cookie-attrs {:max-age 5}})
response (handler {:cookies {}})]
(is (= (get-in response [:headers "Set-Cookie"])
- ["ring-session=\"foo:bar\";Max-Age=5" "cookie2=\"value2\""]))))
+ ["ring-session=foo%3Abar;Max-Age=5" "cookie2=value2"]))))

0 comments on commit ef91c8f

Please sign in to comment.