Cookies: HTTP State Management
This library provides utilities for handling cookies as specified in RFC 6265 [RFC6265].
(require net/cookies) | package: net-cookies-lib |
1 Cookies: Common Functionality
(require net/cookies/common) | package: net-cookies-lib |
procedure
(cookie-name? v) → boolean?
v : any/c
double quotes
whitespace characters
#\@ or #\?
parentheses, brackets, or curly braces
commas, colons, or semicolons
equals, greater-than, or less-than signs
slashes or backslashes
procedure
(cookie-value? v) → boolean?
v : any/c
control characters
whitespace characters
double-quotes, except at the beginning and end if the entire value is double-quoted
commas
semicolons
backslashes
procedure
v : any/c
procedure
(domain-value? v) → boolean?
v : any/c
2 Cookies and HTTP Servers
(require net/cookies/server) | package: net-cookies-lib |
a serializable cookie structure definition
functions to convert a cookie structure to a string, or a value for the HTTP “Set-Cookie” response header
functions that allow reading an HTTP “Cookie” header generated by a user agent
struct
(struct cookie ( name value expires max-age domain path secure? http-only? extension)) name : (and/c string? cookie-name?) value : (and/c string? cookie-value?) expires : (or/c date? #f) max-age : (or/c (and/c integer? positive?) #f) domain : (or/c domain-value? #f) path : (or/c path/extension-value? #f) secure? : boolean? http-only? : boolean? extension : (or/c path/extension-value? #f)
procedure
(make-cookie name value [ #:expires exp-date #:max-age max-age #:domain domain #:path path #:secure? secure? #:http-only? http-only? #:extension extension]) → cookie? name : cookie-name? value : cookie-value? exp-date : (or/c date? #f) = #f max-age : (or/c (and/c integer? positive?) #f) = #f domain : (or/c domain-value? #f) = #f path : (or/c path/extension-value? #f) = #f secure? : boolean? = #f http-only? : boolean? = #f extension : (or/c path/extension-value? #f) = #f
Both exp-date and max-age are for specifying a time at which the user agent should remove the cookie from its cookie store. exp-date is for specifying this expiration time as a date; max-age is for specifying it as a number of seconds in the future. If both exp-date and max-age are given, an RFC6265-compliant user agent will disregard the exp-date and use the max-age.
domain indicates that the recipient should send the cookie back to the server only if the hostname in the request URI is either domain itself, or a host within domain.
path indicates that the recipient should send the cookie back to the server only if path is a prefix of the request URI’s path.
secure, when #t, sets a flag telling the recipient that the cookie may only be sent if the request URI’s scheme specifies a “secure” protocol (presumably HTTPS).
http-only?, when #t, sets a flag telling the recipient that the cookie may be communicated only to a server and only via HTTP or HTTPS. This flag is important for security reasons: Browsers provide JavaScript access to cookies (for example, via document.cookie), and consequently, when cookies contain sensitive data such as user session info, malicious JavaScript can compromise that data. The HttpOnly cookie flag, set by this keyword argument, instructs the browser not to make this cookie available to JavaScript code. If a cookie is intended to be confidential, both http-only? and secure? should be #t, and all connections should use HTTPS. (Some older browsers do not support this flag; see the OWASP page on HttpOnly for more info.)
procedure
c : cookie?
> (cookie->set-cookie-header (make-cookie "rememberUser" "bob" #:path "/main")) #"rememberUser=bob; Path=/main"
procedure
(clear-cookie-header name [ #:domain domain #:path path]) → bytes? name : cookie-name? domain : (or/c domain-value? #f) = #f path : (or/c path/extension-value? #f) = #f
> (clear-cookie-header "rememberUser" #:path "/main") #"rememberUser=; Expires=Thu, 01 Jan 2015 00:00:00 GMT; Path=/main"
procedure
(cookie-header->alist header) → (listof (cons/c bytes? bytes?))
header : bytes? (cookie-header->alist header decode) → (listof (cons/c X X)) header : bytes? decode : (-> bytes? X)
If a key in the header has no value, then #"", or (decode #"") if decode is present, is used as the value.
> (cookie-header->alist #"SID=31d4d96e407aad42; lang=en-US") '((#"SID" . #"31d4d96e407aad42") (#"lang" . #"en-US"))
> (cookie-header->alist #"SID=31d4d96e407aad42; lang=en-US" bytes->string/utf-8) '(("SID" . "31d4d96e407aad42") ("lang" . "en-US"))
> (cookie-header->alist #"seenIntro=; logins=3" (compose (lambda (s) (or (string->number s) s)) bytes->string/utf-8)) '(("seenIntro" . "") ("logins" . 3))
procedure
(cookie->string c) → string?
c : cookie?
> (cookie->string (make-cookie "usesRacket" "true")) "usesRacket=true"
> (cookie->string (make-cookie "favColor" "teal" #:max-age 86400 #:domain "example.com" #:secure? #t)) "favColor=teal; Max-Age=86400; Domain=example.com; Secure"
3 Cookies and HTTP User Agents
(require net/cookies/user-agent) | package: net-cookies-lib |
extract-and-save-cookies!, for storing cookies
cookie-header, for retrieving them and generating a “Cookie:” header
struct
(struct ua-cookie ( name value domain path expiration-time creation-time access-time persistent? host-only? secure-only? http-only?)) name : cookie-name? value : cookie-value? domain : domain-value? path : path/extension-value? expiration-time : (and/c integer? positive?) creation-time : (and/c integer? positive?) access-time : (and/c integer? positive?) persistent? : boolean? host-only? : boolean? secure-only? : boolean? http-only? : boolean?
All times are represented as the number of seconds since midnight UTC, January 1, 1970, like the values produced by current-seconds.
It’s unlikely a client will need to construct a ua-cookie instance directly (except perhaps for testing); extract-cookies produces struct instances for all the cookies received in a server’s response.
procedure
(cookie-expired? cookie [current-time]) → boolean?
cookie : ua-cookie? current-time : integer? = (current-seconds)
3.1 Cookie jars: Client storage
procedure
(extract-and-save-cookies! headers url [ decode]) → void? headers : (or/c (listof (cons/c bytes? bytes?)) (listof bytes?)) url : url? decode : (-> bytes? string?) = bytes->string/utf-8
The given headers may be provided either as an alist mapping header names to header values, or as a raw list of bytes such as the second return value produced by http-conn-recv! in net/http-client. Here is an example of each:
> (require net/url)
> (define site-url (string->url "http://test.example.com/apps/main"))
> (extract-and-save-cookies! '((#"X-Test-Header" . #"isThisACookie=no") (#"Set-Cookie" . #"a=b; Max-Age=2000; Path=/") (#"Set-Cookie" . #"user=bob; Max-Age=86400; Path=/apps")) site-url) > (cookie-header site-url) #"user=bob; a=b"
> (extract-and-save-cookies! '(#"X-Ignore-This: thisIsStillNotACookie=yes" #"Set-Cookie: p=q; Max-Age=2000; Path=/" #"Set-Cookie: usersMom=alice; Max-Age=86400; Path=/apps") site-url) > (cookie-header site-url) #"usersMom=alice; user=bob; p=q; a=b"
procedure
(save-cookie! c [via-http?]) → void?
c : ua-cookie? via-http? : boolean? = #t
procedure
(cookie-header url [encode #:filter-with ok?]) → (or/c bytes? #f)
url : url? encode : (-> string? bytes?) = string->bytes/utf-8 ok? : (-> ua-cookie? boolean?) = (lambda (x) #t)
Cookies with the “Secure” flag will be included in this header iff (url-scheme url) is "https", unless you remove them manually using the ok? parameter.
> (cookie-header (string->url "http://test.example.com/home")) #"p=q; a=b"
|
method
(send a-cookie-jar save-cookie! c [ via-http?]) → void? c : ua-cookie? via-http? : boolean? = #t Saves c to the jar, and removes any expired cookies from the jar as well.via-http? should be #t if the cookie was received via an HTTP API; it is for properly ignoring the cookie if the cookie’s http-only? flag is set, or if the cookie is attempting to replace an “HTTP only” cookie already present in the jar.
method
(send a-cookie-jar save-cookies! cs [ via-http?]) → void? cs : (listof ua-cookie?) via-http? : boolean? = #t Saves each cookie in cs to the jar, and removes any expired cookies from the jar. See the note immediately above, for explanation of the via-http? flag.
method
(send a-cookie-jar cookies-matching url [ secure?]) → (listof ua-cookie?) url : url? secure? : boolean? = (equal? (url-scheme url) "https") Produces all cookies in the jar that should be sent in the “Cookie” header for a request made to url. secure? specifies whether the cookies will be sent via a secure protocol. (If not, cookies with the “Secure” flag set should not be returned by this method.)This method should produce its cookies in the order expected according to RFC6265:
Cookies with longer paths are listed before cookies with shorter paths.
Among cookies that have equal-length path fields, cookies with earlier creation-times are listed before cookies with later creation-times.
If there are multiple cookies in the jar with the same name and different domains or paths, the RFC does not specify which to send. The default list-cookie-jar% class’s implementation of this method produces all cookies that match the domain and path of the given URL, in the order specified above.
| ||
superclass: object% | ||
|
parameter
(current-cookie-jar jar) → void? jar : (is-a?/c cookie-jar<%>)
= (new list-cookie-jar%)
3.2 Reading the Set-Cookie header
procedure
(extract-cookies headers url [decode]) → (listof ua-cookie?)
headers :
(or/c (listof (cons/c bytes? bytes?)) (listof bytes?)) url : url? decode : (-> bytes? string?) = bytes->string/utf-8
The given headers may be provided either as an alist mapping header names to header values, or as a raw list of bytes such as the second return value produced by http-conn-recv! in net/http-client.
This function is suitable for use with the headers/raw field of a request structure (from web-server/http/request-structs), or with the output of (extract-all-fields h), where h is a byte string.
procedure
(parse-cookie set-cookie-bytes url [decode]) → (or/c ua-cookie? #f)
set-cookie-bytes : bytes? url : url? decode : (-> bytes? string?) = bytes->string/utf-8
The decode function is used to convert the cookie’s textual fields (name, value, domain, and path) to strings.
procedure
(default-path url) → string?
url : url?
value
max-cookie-seconds : (and/c integer? positive?)
value
min-cookie-seconds : (and/c integer? negative?)
procedure
(parse-date s) → (or/c string? #f)
s : string?
4 Acknowledgements
The server-side library is based on the original net/cookie library by Francisco Solsona <solsona@acm.org>. Many of the cookie-construction tests for this library are adapted from the net/cookie tests.
Roman Klochkov <kalimehtar@mail.ru> wrote the first client-side cookie library on which this user-agent library is based. In particular, this library relies on his code for parsing dates and other cookie components.
Bibliography
[RFC1034] | P. Mockapetris, “Domain Names - Concepts and Facilities,” RFC, 1987. http://tools.ietf.org/html/rfc1034.html | |
[RFC1123] | R. Braden (editor), “Requirements for Internet Hosts - Application and Support,” RFC, 1989. http://tools.ietf.org/html/rfc1123.html | |
[RFC6265] | A. Barth, “HTTP State Management Mechanism,” RFC, 2011. http://tools.ietf.org/html/rfc6265.html |