cl-cookie
2024-10-12
HTTP cookie manager
CL-Cookie
HTTP cookie manager for Common Lisp.
Key features include:
- Create
cookie
struct instances withmake-cookie
and the following properties:name
,value
,path
,domain
,origin-host
,expires
,max-age
,same-site
,partitioned
,secure-p
,httponly-p
. Thecreation-timestamp
attribute is set automatically at creation time. - Compare cookies with
cookie=
andcookie-equal
. - Convert
Set-Cookie
header string intocookie
instance withparse-set-cookie-headers
. - Serialize
cookie
instances into header strings withwrite-cookie-header
andwrite-set-cookie-header
. - Create
cookie-jar
struct instances withmake-cookie-jar
which allows managing of multiple cookies. You can add multiple cookies to a cookie-jar withmerge-cookies
, or match cookies of certainhost
andpath
withcookie-jar-host-cookies
. - Sanity check for cookie attributes in
make-cookie
,parse-set-cookie-header
,write-set-cookie-header
. Can be turned off by setting*sanity-check*
orsanity-check
parameter (inmake-cookie
andparse-set-cookie-header
) tonil
.
Usage
make-cookie (&key name value path domain origin-host expires max-age same-site partitioned secure-p httponly-p)
(defparameter *cookie* (cl-cookie:make-cookie :name "SID" :value "31d4d96e407aad42" :origin-host "example.com" :path "/api/" :domain ".example.com" :partitioned t :httponly-p t :secure-p t :expires (encode-universal-time 6 22 19 25 1 2002 0) :max-age 3600 ;; max-age takes precedence over expires :same-site "Strict")) ;=> *COOKIE* *cookie* ;=> #S(COOKIE :NAME "SID" :VALUE "31d4d96e407aad42" :EXPIRES 3220975326 :PATH "/api/" ; :DOMAIN ".example.com" :SAME-SITE "Strict" :MAX-AGE 3600 :PARTITIONED T ; :SECURE-P T :HTTPONLY-P T :ORIGIN-HOST "example.com" :CREATION-TIMESTAMP 3921465733)
write-set-cookie-header (cookie &optional stream)
(cl-cookie:write-set-cookie-header *cookie*) ;=> "SID=31d4d96e407aad42; Expires=Fri, 25 Jan 2002 19:22:06 GMT; Max-age=3600; Path=/api/; Domain=.example.com; SameSite=Strict; Partitioned; Secure; HttpOnly"
Sanity Check
If one of the following conditions is true, an error is emitted:
- Is
name
not supplied? - If
secure
is not present:- is
samesite=none
? - is
partitioned
present?
- is
(make-cookie :value "asdg") ;; => Invalid Cookie values: No name supplied. ;; You should set at least a dummy name to avoid bugs. ;; [Condition of type INVALID-COOKIE]
(make-cookie :name "haalllo" :value "asdg" :same-site "None") ;; => Invalid Cookie values: Samesite=None cookies require Secure. ;; [Condition of type INVALID-COOKIE]
(make-cookie :name "haalllo" :partitioned t) ;; => Invalid Cookie values: Partitioned cookies require Secure. ;; [Condition of type INVALID-COOKIE]
If you want to deactivate sanity checks (e.g. for performance reasons)
you can either supply parameter sanity-check
with nil
, or set
*sanity-check*
to nil
.
(make-cookie :name "haalllo" :partitioned t :sanity-check nil) ;; => #S(COOKIE :NAME "haalllo" :PARTITIONED T :CREATION-TIMESTAMP 3928172572)
(let ((cl-cookie:*sanity-check*)) (make-cookie :name "haalllo" :value "asdg" :same-site "None")) ;; => #S(COOKIE :NAME "haalllo" :VALUE "asdg" :SAME-SITE "None" ;; :CREATION-TIMESTAMP 3928172645)
cookie struct accessor functions
(cl-cookie:cookie-name *cookie*) ;=> "SID" (cl-cookie:cookie-value *cookie*) ;=> "31d4d96e407aad42" (cl-cookie:cookie-expires *cookie*) ;=> 3220975326 (cl-cookie:cookie-path *cookie*) ;=> "/api/" (cl-cookie:cookie-origin-host *cookie*) ;=> "example.com" ;; host takes precedence over domain, if both are supplied and conflicting. (cl-cookie:cookie-domain *cookie*) ;=> ".example.com" ;; This cookie is accessible for example.com and all subdomains (e.g. docs.example.com) (cl-cookie:cookie-same-site *cookie*) ;=> "Strict" (cl-cookie:cookie-max-age *cookie*) ;=> 3600 (cl-cookie:cookie-partitioned *cookie*) ;=> T (cl-cookie:cookie-secure-p *cookie*) ;=> T (cl-cookie:cookie-httponly-p *cookie*) ;=> T
parse-set-cookie-header (set-cookie-string origin-host origin-path)
(cl-cookie:parse-set-cookie-header "my_cookie=my_value; Domain=.example.com; Expires=Sat, 31 Dec 2024 23:59:59 GMT; HttpOnly; Max-Age=7200; Secure; SameSite=None" "example.com" "/") ;=> #S(COOKIE :NAME "my_cookie" :VALUE "my_value" :EXPIRES 3944678399 :PATH "/" :DOMAIN ".example.com" ; :SAME-SITE "None" :MAX-AGE 7200 :PARTITIONED NIL :SECURE-P T ; :HTTPONLY-P T :ORIGIN-HOST "example.com" :CREATION-TIMESTAMP 3921467559)
make-cookie-jar (&key cookies)
(defparameter *cookie-jar* (cl-cookie:make-cookie-jar :cookies (list (cl-cookie:make-cookie :name "SID" :value "31d4d96e407aad42" :origin-host "example.com" :path "/api/" :domain ".example.com" :max-age 3600)))) ;=> *cookie-jar*
merge-cookies (cookie-jar cookies)
(cl-cookie:merge-cookies *cookie-jar* (list (cl-cookie:parse-set-cookie-header "SID=31d4d96e407aad42; Path=/; Domain=example.com; Partitioned" "www.example.org" "/"))) ;=> (#S(CL-COOKIE:COOKIE :NAME "SID" :VALUE "31d4d96e407aad42" :PATH "/api/" :DOMAIN ".example.com" ; :ORIGIN-HOST "example.com" :EXPIRES NIL :MAX-AGE 3600 :SAME-SITE NIL ; :PARTITIONED NIL :SECURE-P NIL :HTTPONLY-P NIL :CREATION-TIMESTAMP 3921572023) ; #S(CL-COOKIE:COOKIE :NAME "SID" :VALUE "31d4d96e407aad42" :PATH "/" :DOMAIN "example.com" ; :ORIGIN-HOST "www.example.org" :EXPIRES NIL :MAX-AGE NIL :SAME-SITE NIL ; :PARTITIONED T :SECURE-P NIL :HTTPONLY-P NIL :CREATION-TIMESTAMP 3921572029))
cookie-jar-host-cookies (cookie-jar host path &key securep)
(cl-cookie:cookie-jar-host-cookies *cookie-jar* "example.com" "/") ;=> (#S(CL-COOKIE:COOKIE :NAME "SID" :VALUE "31d4d96e407aad42" :PATH "/" :DOMAIN "example.com" ; :ORIGIN-HOST "www.example.org" :EXPIRES NIL :MAX-AGE NIL :SAME-SITE NIL ; :PARTITIONED NIL :SECURE-P NIL :HTTPONLY-P NIL :CREATION-TIMESTAMP 3921566005))
See also
Author
- Eitaro Fukamachi (e.arrows@gmail.com)
Copyright
Copyright (c) 2015 Eitaro Fukamachi (e.arrows@gmail.com)
License
Licensed under the BSD 2-Clause License.