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