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]
41 flag) *)
42
43 val equal : t -> t -> bool
44 (** Equality function for same-site values *)
45
46 val pp : Format.formatter -> t -> unit
47 (** Pretty printer for same-site values *)
48end
49
50module Expiration : sig
51 type t = [ `Session | `DateTime of Ptime.t ]
52 (** Cookie expiration strategy.
53
54 - [`Session]: Session cookie that expires when browser session ends
55 - [`DateTime time]: Persistent cookie that expires at specific time *)
56
57 val equal : t -> t -> bool
58 (** Equality function for expiration values *)
59
60 val pp : Format.formatter -> t -> unit
61 (** Pretty printer for expiration values *)
62end
63
64type t
65(** HTTP Cookie representation with all standard attributes.
66
67 A cookie represents a name-value pair with associated metadata that controls
68 its scope, security, and lifetime. Cookies with the same [name], [domain],
69 and [path] will overwrite each other when added to a cookie jar. *)
70
71(** {1 Cookie Accessors} *)
72
73val domain : t -> string
74(** Get the domain of a cookie *)
75
76val path : t -> string
77(** Get the path of a cookie *)
78
79val name : t -> string
80(** Get the name of a cookie *)
81
82val value : t -> string
83(** Get the value of a cookie *)
84
85val value_trimmed : t -> string
86(** Get cookie value with surrounding double-quotes removed if they form a
87 matching pair.
88
89 Only removes quotes when both opening and closing quotes are present. The
90 raw value is always preserved in {!value}. This is useful for handling
91 quoted cookie values per RFC 6265.
92
93 Examples:
94 - ["value"] → ["value"]
95 - ["\"value\""] → ["value"]
96 - ["\"value"] → ["\"value"] (no matching pair)
97 - ["\"val\"\""] → ["val\""] (removes outer pair only) *)
98
99val secure : t -> bool
100(** Check if cookie is secure only *)
101
102val http_only : t -> bool
103(** Check if cookie is HTTP only *)
104
105val partitioned : t -> bool
106(** Check if cookie has the Partitioned attribute.
107
108 Partitioned cookies are part of CHIPS (Cookies Having Independent
109 Partitioned State) and are stored separately per top-level site, enabling
110 privacy-preserving third-party cookie functionality. Partitioned cookies
111 must always be Secure. *)
112
113val expires : t -> Expiration.t option
114(** Get the expiration attribute if set.
115
116 - [None]: No expiration specified (browser decides lifetime)
117 - [Some `Session]: Session cookie (expires when browser session ends)
118 - [Some (`DateTime t)]: Expires at specific time [t]
119
120 Both [max_age] and [expires] can be present simultaneously. This library
121 stores both independently. *)
122
123val max_age : t -> Ptime.Span.t option
124(** Get the max-age attribute if set.
125
126 Both [max_age] and [expires] can be present simultaneously. When both are
127 present in a Set-Cookie header, browsers prioritize [max_age] per RFC 6265.
128 This library stores both independently and serializes both when present. *)
129
130val same_site : t -> SameSite.t option
131(** Get the same-site policy of a cookie *)
132
133val creation_time : t -> Ptime.t
134(** Get the creation time of a cookie *)
135
136val last_access : t -> Ptime.t
137(** Get the last access time of a cookie *)
138
139val make :
140 domain:string ->
141 path:string ->
142 name:string ->
143 value:string ->
144 ?secure:bool ->
145 ?http_only:bool ->
146 ?expires:Expiration.t ->
147 ?max_age:Ptime.Span.t ->
148 ?same_site:SameSite.t ->
149 ?partitioned:bool ->
150 creation_time:Ptime.t ->
151 last_access:Ptime.t ->
152 unit ->
153 t
154(** Create a new cookie with the given attributes.
155
156 Note: If [partitioned] is [true], the cookie must also be [secure]. Invalid
157 combinations will result in validation errors. *)
158
159(** {1 Cookie Creation and Parsing} *)
160
161val of_set_cookie_header :
162 now:(unit -> Ptime.t) -> domain:string -> path:string -> string -> t option
163(** Parse Set-Cookie response header value into a cookie.
164
165 Set-Cookie headers are sent from server to client and contain the cookie
166 name, value, and all attributes.
167
168 Parses a Set-Cookie header value following RFC specifications:
169 - Basic format: [NAME=VALUE; attribute1; attribute2=value2]
170 - Supports all standard attributes: [expires], [max-age], [domain], [path],
171 [secure], [httponly], [samesite], [partitioned]
172 - Returns [None] if parsing fails or cookie validation fails
173 - The [domain] and [path] parameters provide the request context for default
174 values
175 - The [now] parameter is used for calculating expiry times from [max-age]
176 attributes and setting creation/access times
177
178 Cookie validation rules:
179 - [SameSite=None] requires the [Secure] flag to be set
180 - [Partitioned] requires the [Secure] flag to be set
181
182 Example:
183 [of_set_cookie_header ~now:(fun () -> Ptime_clock.now ())
184 ~domain:"example.com" ~path:"/" "session=abc123; Secure; HttpOnly"] *)
185
186val of_cookie_header :
187 now:(unit -> Ptime.t) ->
188 domain:string ->
189 path:string ->
190 string ->
191 (t, string) result list
192(** Parse Cookie request header containing semicolon-separated name=value pairs.
193
194 Cookie headers are sent from client to server and contain only name=value
195 pairs without attributes: ["name1=value1; name2=value2; name3=value3"]
196
197 Creates cookies with:
198 - Provided [domain] and [path] from request context
199 - All security flags set to [false] (defaults)
200 - All optional attributes set to [None]
201 - [creation_time] and [last_access] set to current time from [now]
202
203 Returns a list of parse results, one per cookie. Parse errors for individual
204 cookies are returned as [Error msg] without failing the entire parse. Empty
205 values and excess whitespace are ignored.
206
207 Example:
208 [of_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com"
209 ~path:"/" "session=abc; theme=dark"] *)
210
211val make_cookie_header : t list -> string
212(** Create cookie header value from cookies.
213
214 Formats a list of cookies into a Cookie header value suitable for HTTP
215 requests.
216 - Format: [name1=value1; name2=value2; name3=value3]
217 - Only includes cookie names and values, not attributes
218 - Cookies should already be filtered for the target domain/path
219 - More specific path mappings should be ordered first in the input list
220
221 Example: [make_cookie_header cookies] might return
222 ["session=abc123; theme=dark"] *)
223
224val make_set_cookie_header : t -> string
225(** Create Set-Cookie header value from a cookie.
226
227 Formats a cookie into a Set-Cookie header value suitable for HTTP responses.
228 Includes all cookie attributes: Max-Age, Expires, Domain, Path, Secure,
229 HttpOnly, and SameSite. *)
230
231(** {1 Pretty Printing} *)
232
233val pp : Format.formatter -> t -> unit
234(** Pretty print a cookie *)