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 *)