--- ../drakma-1.1.0/cookies.lisp 2009-12-01 17:06:56.000000000 -0500 +++ cookies.lisp 2010-04-02 17:57:34.000000000 -0400 @@ -263,25 +263,48 @@ `Set-Cookie' header as found in HEADERS \(an alist as returned by HTTP-REQUEST). Collects only cookies which match the domain of the \(PURI) URI URI." - (loop with set-cookie-header = (header-value :set-cookie headers) - with parsed-cookies = (and set-cookie-header (parse-set-cookie set-cookie-header)) - for (name value parameters) in parsed-cookies - for expires = (parameter-value "expires" parameters) - for domain = (or (parameter-value "domain" parameters) (uri-host uri)) - when (and (valid-cookie-domain-p domain) - (cookie-domain-matches domain uri)) - collect (make-instance 'cookie - :name name - :value value - :path (or (parameter-value "path" parameters) - (uri-path uri) - "/") - :expires (and expires - (plusp (length expires)) - (parse-cookie-date expires)) - :domain domain - :securep (not (not (parameter-present-p "secure" parameters))) - :http-only-p (not (not (parameter-present-p "HttpOnly" parameters)))))) + (unique-cookies + (loop with set-cookie-header = (header-value :set-cookie headers) + with parsed-cookies = (and set-cookie-header (parse-set-cookie set-cookie-header)) + for (name value parameters) in parsed-cookies + for expires = (parameter-value "expires" parameters) + for domain = (or (parameter-value "domain" parameters) (uri-host uri)) + when (and (valid-cookie-domain-p domain) + (cookie-domain-matches domain uri)) + collect (make-instance 'cookie + :name name + :value value + :path (or (parameter-value "path" parameters) + (uri-path uri) + "/") + :expires (and expires + (plusp (length expires)) + (parse-cookie-date expires)) + :domain domain + :securep (not (not (parameter-present-p "secure" parameters))) + :http-only-p (not (not (parameter-present-p "HttpOnly" parameters))))))) + +(defun unique-cookies (cookies) + "Iterates through the given list of COOKIE objects, and returns a +new list of COOKIE objects, with any duplicate cookies removed, using COOKIE= +to determine duplicates. COOKIEs earlier in the list will be replaced by +those later in the list. + +This is meant to deal with duplicate `Set-Cookie` headers from misbehaving servers. +For example, if the HTTP response contains: + + Set-Cookie: JSESSIONID=foo; Path=/; Secure + Set-Cookie: JSESSIONID=bar; Path=/; Secure + +This function will only keep the last value for JSESSIONID Path=/; Secure + +The choice of which duplicate to use (first or last) is arbitrary, but last was +chosen here to match the behavior of Firefox 3.6. +" + (let (res (cookies (reverse cookies))) + (dolist (cookie cookies res) + (unless (find cookie res :test #'cookie=) + (push cookie res))))) (defun update-cookies (new-cookies cookie-jar) "Updates the cookies in COOKIE-JAR by replacing those which are