OCaml HTTP cookie handling library with support for Eio-based storage jars
1(** Cookie management library for OCaml
2
3 HTTP cookies are a mechanism that allows "server side connections to store
4 and retrieve information on the client side." Originally designed to enable
5 persistent client-side state for web applications, cookies are essential for
6 storing user preferences, session data, shopping cart contents, and
7 authentication tokens.
8
9 This library provides a complete cookie jar implementation following
10 established web standards while integrating Eio for efficient asynchronous
11 operations.
12
13 {2 Cookie Format and Structure}
14
15 Cookies are set via the Set-Cookie HTTP response header with the basic
16 format: [NAME=VALUE] with optional attributes including:
17 - [expires]: Optional cookie lifetime specification
18 - [domain]: Specifying valid domains using tail matching
19 - [path]: Defining URL subset for cookie validity
20 - [secure]: Transmission over secure channels only
21 - [httponly]: Not accessible to JavaScript
22 - [samesite]: Cross-site request behavior control
23
24 {2 Domain and Path Matching}
25
26 The library implements standard domain and path matching rules:
27 - Domain matching uses "tail matching" (e.g., "acme.com" matches
28 "anvil.acme.com")
29 - Path matching allows subset URL specification for fine-grained control
30 - More specific path mappings are sent first in Cookie headers *)
31
32module SameSite : sig
33 type t = [ `Strict | `Lax | `None ]
34 (** Cookie same-site policy for controlling cross-site request behavior.
35
36 - [`Strict]: Cookie only sent for same-site requests, providing maximum
37 protection
38 - [`Lax]: Cookie sent for same-site requests and top-level navigation
39 (default for modern browsers)
40 - [`None]: Cookie sent for all cross-site requests (requires [secure] flag) *)
41
42 val equal : t -> t -> bool
43 (** Equality function for same-site values *)
44
45 val pp : Format.formatter -> t -> unit
46 (** Pretty printer for same-site values *)
47end
48
49module Expiration : sig
50 type t = [ `Session | `DateTime of Ptime.t ]
51 (** Cookie expiration strategy.
52
53 - [`Session]: Session cookie that expires when browser session ends
54 - [`DateTime time]: Persistent cookie that expires at specific time *)
55
56 val equal : t -> t -> bool
57 (** Equality function for expiration values *)
58
59 val pp : Format.formatter -> t -> unit
60 (** Pretty printer for expiration values *)
61end
62
63type t
64(** HTTP Cookie representation with all standard attributes.
65
66 A cookie represents a name-value pair with associated metadata that controls
67 its scope, security, and lifetime. Cookies with the same [name], [domain],
68 and [path] will overwrite each other when added to a cookie jar. *)
69
70(** {1 Cookie Accessors} *)
71
72val domain : t -> string
73(** Get the domain of a cookie *)
74
75val path : t -> string
76(** Get the path of a cookie *)
77
78val name : t -> string
79(** Get the name of a cookie *)
80
81val value : t -> string
82(** Get the value of a cookie *)
83
84val value_trimmed : t -> string
85(** Get cookie value with surrounding double-quotes removed if they form a
86 matching pair.
87
88 Only removes quotes when both opening and closing quotes are present. The
89 raw value is always preserved in {!value}. This is useful for handling
90 quoted cookie values per RFC 6265.
91
92 Examples:
93 - ["value"] → ["value"]
94 - ["\"value\""] → ["value"]
95 - ["\"value"] → ["\"value"] (no matching pair)
96 - ["\"val\"\""] → ["val\""] (removes outer pair only) *)
97
98val secure : t -> bool
99(** Check if cookie is secure only *)
100
101val http_only : t -> bool
102(** Check if cookie is HTTP only *)
103
104val partitioned : t -> bool
105(** Check if cookie has the Partitioned attribute.
106
107 Partitioned cookies are part of CHIPS (Cookies Having Independent
108 Partitioned State) and are stored separately per top-level site, enabling
109 privacy-preserving third-party cookie functionality. Partitioned cookies
110 must always be Secure. *)
111
112val expires : t -> Expiration.t option
113(** Get the expiration attribute if set.
114
115 - [None]: No expiration specified (browser decides lifetime)
116 - [Some `Session]: Session cookie (expires when browser session ends)
117 - [Some (`DateTime t)]: Expires at specific time [t]
118
119 Both [max_age] and [expires] can be present simultaneously. This library
120 stores both independently. *)
121
122val max_age : t -> Ptime.Span.t option
123(** Get the max-age attribute if set.
124
125 Both [max_age] and [expires] can be present simultaneously. When both are
126 present in a Set-Cookie header, browsers prioritize [max_age] per RFC 6265.
127 This library stores both independently and serializes both when present. *)
128
129val same_site : t -> SameSite.t option
130(** Get the same-site policy of a cookie *)
131
132val creation_time : t -> Ptime.t
133(** Get the creation time of a cookie *)
134
135val last_access : t -> Ptime.t
136(** Get the last access time of a cookie *)
137
138val make :
139 domain:string ->
140 path:string ->
141 name:string ->
142 value:string ->
143 ?secure:bool ->
144 ?http_only:bool ->
145 ?expires:Expiration.t ->
146 ?max_age:Ptime.Span.t ->
147 ?same_site:SameSite.t ->
148 ?partitioned:bool ->
149 creation_time:Ptime.t ->
150 last_access:Ptime.t ->
151 unit ->
152 t
153(** Create a new cookie with the given attributes.
154
155 Note: If [partitioned] is [true], the cookie must also be [secure]. Invalid
156 combinations will result in validation errors. *)
157
158(** {1 Cookie Creation and Parsing} *)
159
160val of_set_cookie_header :
161 now:(unit -> Ptime.t) -> domain:string -> path:string -> string -> t option
162(** Parse Set-Cookie response header value into a cookie.
163
164 Set-Cookie headers are sent from server to client and contain the cookie
165 name, value, and all attributes.
166
167 Parses a Set-Cookie header value following RFC specifications:
168 - Basic format: [NAME=VALUE; attribute1; attribute2=value2]
169 - Supports all standard attributes: [expires], [max-age], [domain], [path],
170 [secure], [httponly], [samesite], [partitioned]
171 - Returns [None] if parsing fails or cookie validation fails
172 - The [domain] and [path] parameters provide the request context for default
173 values
174 - The [now] parameter is used for calculating expiry times from [max-age]
175 attributes and setting creation/access times
176
177 Cookie validation rules:
178 - [SameSite=None] requires the [Secure] flag to be set
179 - [Partitioned] requires the [Secure] flag to be set
180
181 Example:
182 [of_set_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com" ~path:"/" "session=abc123;
183 Secure; HttpOnly"] *)
184
185val of_cookie_header :
186 now:(unit -> Ptime.t) ->
187 domain:string ->
188 path:string ->
189 string ->
190 (t, string) result list
191(** Parse Cookie request header containing semicolon-separated name=value pairs.
192
193 Cookie headers are sent from client to server and contain only name=value
194 pairs without attributes: ["name1=value1; name2=value2; name3=value3"]
195
196 Creates cookies with:
197 - Provided [domain] and [path] from request context
198 - All security flags set to [false] (defaults)
199 - All optional attributes set to [None]
200 - [creation_time] and [last_access] set to current time from [now]
201
202 Returns a list of parse results, one per cookie. Parse errors for individual
203 cookies are returned as [Error msg] without failing the entire parse. Empty
204 values and excess whitespace are ignored.
205
206 Example:
207 [of_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com" ~path:"/"
208 "session=abc; theme=dark"] *)
209
210val make_cookie_header : t list -> string
211(** Create cookie header value from cookies.
212
213 Formats a list of cookies into a Cookie header value suitable for HTTP
214 requests.
215 - Format: [name1=value1; name2=value2; name3=value3]
216 - Only includes cookie names and values, not attributes
217 - Cookies should already be filtered for the target domain/path
218 - More specific path mappings should be ordered first in the input list
219
220 Example: [make_cookie_header cookies] might return
221 ["session=abc123; theme=dark"] *)
222
223val make_set_cookie_header : t -> string
224(** Create Set-Cookie header value from a cookie.
225
226 Formats a cookie into a Set-Cookie header value suitable for HTTP responses.
227 Includes all cookie attributes: Max-Age, Expires, Domain, Path, Secure,
228 HttpOnly, and SameSite. *)
229
230(** {1 Pretty Printing} *)
231
232val pp : Format.formatter -> t -> unit
233(** Pretty print a cookie *)