···11# This file is generated by dune, edit dune-project instead
22opam-version: "2.0"
33-synopsis: "Cookie parsing and management library using Eio"
33+synopsis: "Cookie parsing and management library"
44description:
55- "Cookeio provides cookie management functionality for OCaml applications, including parsing Set-Cookie headers, managing cookie jars, and supporting the Mozilla cookies.txt format for persistence."
55+ "Cookeio provides cookie parsing and serialization for OCaml applications. It handles parsing Set-Cookie and Cookie headers with full support for all cookie attributes."
66maintainer: ["Anil Madhavapeddy <anil@recoil.org>"]
77authors: ["Anil Madhavapeddy"]
88license: "ISC"
···1111depends: [
1212 "ocaml" {>= "5.2.0"}
1313 "dune" {>= "3.20"}
1414- "eio" {>= "1.0"}
1514 "logs" {>= "0.9.0"}
1615 "ptime" {>= "1.1.0"}
1716 "eio_main" {with-test}
+2-3
dune-project
···15151616(package
1717 (name cookeio)
1818- (synopsis "Cookie parsing and management library using Eio")
1919- (description "Cookeio provides cookie management functionality for OCaml applications, including parsing Set-Cookie headers, managing cookie jars, and supporting the Mozilla cookies.txt format for persistence.")
1818+ (synopsis "Cookie parsing and management library")
1919+ (description "Cookeio provides cookie parsing and serialization for OCaml applications. It handles parsing Set-Cookie and Cookie headers with full support for all cookie attributes.")
2020 (depends
2121 (ocaml (>= 5.2.0))
2222 dune
2323- (eio (>= 1.0))
2423 (logs (>= 0.9.0))
2524 (ptime (>= 1.1.0))
2625 (eio_main :with-test)
+12-18
lib/core/cookeio.ml
···220220 }
221221222222(** Parse a single attribute and update the accumulator in-place *)
223223-let parse_attribute clock attrs attr_name attr_value =
223223+let parse_attribute now attrs attr_name attr_value =
224224 let attr_lower = String.lowercase_ascii attr_name in
225225 match attr_lower with
226226 | "domain" -> attrs.domain <- Some (normalize_domain attr_value)
···244244 | Some seconds ->
245245 (* Handle negative values as 0 per RFC 6265 *)
246246 let seconds = max 0 seconds in
247247- let now = Eio.Time.now clock in
247247+ let current_time = now () in
248248 (* Store the max-age as a Ptime.Span *)
249249 attrs.max_age <- Some (Ptime.Span.of_int_s seconds);
250250 (* Also compute and store expires as DateTime *)
251251- let expires = Ptime.of_float_s (now +. float_of_int seconds) in
251251+ let expires = Ptime.add_span current_time (Ptime.Span.of_int_s seconds) in
252252 (match expires with
253253 | Some time -> attrs.expires <- Some (`DateTime time)
254254 | None -> ());
···323323324324(** {1 Cookie Parsing} *)
325325326326-let of_set_cookie_header ~clock ~domain:request_domain ~path:request_path
326326+let of_set_cookie_header ~now ~domain:request_domain ~path:request_path
327327 header_value =
328328 Log.debug (fun m -> m "Parsing Set-Cookie: %s" header_value);
329329···344344 |> String.trim
345345 in
346346347347- let now =
348348- Ptime.of_float_s (Eio.Time.now clock)
349349- |> Option.value ~default:Ptime.epoch
350350- in
347347+ let current_time = now () in
351348352349 (* Parse all attributes into mutable accumulator *)
353350 let accumulated_attrs = empty_attributes () in
···356353 match String.index_opt attr '=' with
357354 | None ->
358355 (* Attribute without value (e.g., Secure, HttpOnly) *)
359359- parse_attribute clock accumulated_attrs attr ""
356356+ parse_attribute now accumulated_attrs attr ""
360357 | Some eq ->
361358 let attr_name = String.sub attr 0 eq |> String.trim in
362359 let attr_value =
363360 String.sub attr (eq + 1) (String.length attr - eq - 1)
364361 |> String.trim
365362 in
366366- parse_attribute clock accumulated_attrs attr_name attr_value)
363363+ parse_attribute now accumulated_attrs attr_name attr_value)
367364 attrs;
368365369366 (* Validate attributes *)
···373370 else
374371 let cookie =
375372 build_cookie ~request_domain ~request_path ~name
376376- ~value:cookie_value accumulated_attrs ~now
373373+ ~value:cookie_value accumulated_attrs ~now:current_time
377374 in
378375 Log.debug (fun m -> m "Parsed cookie: %a" pp cookie);
379376 Some cookie)
380377381381-let of_cookie_header ~clock ~domain ~path header_value =
378378+let of_cookie_header ~now ~domain ~path header_value =
382379 Log.debug (fun m -> m "Parsing Cookie header: %s" header_value);
383380384381 (* Split on semicolons *)
···403400 (String.length name_value - eq_pos - 1)
404401 |> String.trim
405402 in
406406- let now =
407407- Ptime.of_float_s (Eio.Time.now clock)
408408- |> Option.value ~default:Ptime.epoch
409409- in
403403+ let current_time = now () in
410404 (* Create cookie with defaults from Cookie header context *)
411405 let cookie =
412406 make ~domain ~path ~name:cookie_name ~value:cookie_value
413413- ~secure:false ~http_only:false ~partitioned:false ~creation_time:now
414414- ~last_access:now ()
407407+ ~secure:false ~http_only:false ~partitioned:false ~creation_time:current_time
408408+ ~last_access:current_time ()
415409 in
416410 Ok cookie)
417411 parts
+7-7
lib/core/cookeio.mli
···158158(** {1 Cookie Creation and Parsing} *)
159159160160val of_set_cookie_header :
161161- clock:_ Eio.Time.clock -> domain:string -> path:string -> string -> t option
161161+ now:(unit -> Ptime.t) -> domain:string -> path:string -> string -> t option
162162(** Parse Set-Cookie response header value into a cookie.
163163164164 Set-Cookie headers are sent from server to client and contain the cookie
···171171 - Returns [None] if parsing fails or cookie validation fails
172172 - The [domain] and [path] parameters provide the request context for default
173173 values
174174- - The [clock] parameter is used for calculating expiry times from [max-age]
175175- attributes
174174+ - The [now] parameter is used for calculating expiry times from [max-age]
175175+ attributes and setting creation/access times
176176177177 Cookie validation rules:
178178 - [SameSite=None] requires the [Secure] flag to be set
179179 - [Partitioned] requires the [Secure] flag to be set
180180181181 Example:
182182- [of_set_cookie_header ~clock ~domain:"example.com" ~path:"/" "session=abc123;
182182+ [of_set_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com" ~path:"/" "session=abc123;
183183 Secure; HttpOnly"] *)
184184185185val of_cookie_header :
186186- clock:_ Eio.Time.clock ->
186186+ now:(unit -> Ptime.t) ->
187187 domain:string ->
188188 path:string ->
189189 string ->
···197197 - Provided [domain] and [path] from request context
198198 - All security flags set to [false] (defaults)
199199 - All optional attributes set to [None]
200200- - [creation_time] and [last_access] set to current time from [clock]
200200+ - [creation_time] and [last_access] set to current time from [now]
201201202202 Returns a list of parse results, one per cookie. Parse errors for individual
203203 cookies are returned as [Error msg] without failing the entire parse. Empty
204204 values and excess whitespace are ignored.
205205206206 Example:
207207- [of_cookie_header ~clock ~domain:"example.com" ~path:"/"
207207+ [of_cookie_header ~now:(fun () -> Ptime_clock.now ()) ~domain:"example.com" ~path:"/"
208208 "session=abc; theme=dark"] *)
209209210210val make_cookie_header : t list -> string