···
String.sub domain 1 (String.length domain - 1)
46
+
(** Remove duplicate cookies, keeping the last occurrence.
48
+
Used to deduplicate combined cookie lists where delta cookies should
49
+
take precedence over original cookies. *)
50
+
let dedup_by_identity cookies =
51
+
let rec aux acc = function
52
+
| [] -> List.rev acc
55
+
List.exists (fun c2 -> cookie_identity_matches c c2) rest
57
+
if has_duplicate then aux acc rest else aux (c :: acc) rest
(** Check if a string is an IP address (IPv4 or IPv6).
Per RFC 6265 Section 5.1.3, domain matching should only apply to hostnames,
not IP addresses. IP addresses require exact match only.
@see <https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3> RFC 6265 Section 5.1.3 - Domain Matching *)
52
-
let is_ip_address domain =
53
-
match Ipaddr.of_string domain with
67
+
let is_ip_address domain = Result.is_ok (Ipaddr.of_string domain)
(** Check if a cookie domain matches a request domain.
···
@see <https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3> RFC 6265 Section 5.1.3 - Domain Matching
@see <https://datatracker.ietf.org/doc/html/rfc6265#section-5.3> RFC 6265 Section 5.3 - Storage Model (host-only-flag) *)
let domain_matches ~host_only cookie_domain request_domain =
77
-
if is_ip_address request_domain then
78
-
(* IP addresses: exact match only per Section 5.1.3 *)
79
-
request_domain = cookie_domain
81
-
(* Hostnames: exact match or subdomain match (if not host_only) *)
82
-
request_domain = cookie_domain
84
-
&& String.ends_with ~suffix:("." ^ cookie_domain) request_domain)
89
+
request_domain = cookie_domain
90
+
|| (not (is_ip_address request_domain || host_only)
91
+
&& String.ends_with ~suffix:("." ^ cookie_domain) request_domain)
(** Check if a cookie path matches a request path.
···
(* Combine original and delta cookies, with delta taking precedence *)
let all_cookies = jar.original_cookies @ jar.delta_cookies in
351
-
(* Filter out duplicates, keeping the last occurrence (from delta) *)
352
-
let rec dedup acc = function
353
-
| [] -> List.rev acc
355
-
(* Keep this cookie only if no later cookie has the same identity *)
356
-
let has_duplicate =
357
-
List.exists (fun c2 -> cookie_identity_matches c c2) rest
359
-
if has_duplicate then dedup acc rest else dedup (c :: acc) rest
361
-
let unique_cookies = dedup [] all_cookies in
357
+
let unique_cookies = dedup_by_identity all_cookies in
(* Filter for applicable cookies, excluding removal cookies and expired cookies *)
···
Eio.Mutex.lock jar.mutex;
455
-
(* Combine and deduplicate cookies for count *)
let all_cookies = jar.original_cookies @ jar.delta_cookies in
457
-
let rec dedup acc = function
458
-
| [] -> List.rev acc
460
-
let has_duplicate =
461
-
List.exists (fun c2 -> cookie_identity_matches c c2) rest
463
-
if has_duplicate then dedup acc rest else dedup (c :: acc) rest
465
-
let unique = dedup [] all_cookies in
452
+
let unique = dedup_by_identity all_cookies in
let n = List.length unique in
Eio.Mutex.unlock jar.mutex;
let get_all_cookies jar =
Eio.Mutex.lock jar.mutex;
472
-
(* Combine and deduplicate, with delta taking precedence *)
let all_cookies = jar.original_cookies @ jar.delta_cookies in
474
-
let rec dedup acc = function
475
-
| [] -> List.rev acc
477
-
let has_duplicate =
478
-
List.exists (fun c2 -> cookie_identity_matches c c2) rest
480
-
if has_duplicate then dedup acc rest else dedup (c :: acc) rest
482
-
let unique = dedup [] all_cookies in
460
+
let unique = dedup_by_identity all_cookies in
Eio.Mutex.unlock jar.mutex;
···
(* Combine and deduplicate cookies *)
let all_cookies = jar.original_cookies @ jar.delta_cookies in
501
-
let rec dedup acc = function
502
-
| [] -> List.rev acc
504
-
let has_duplicate =
505
-
List.exists (fun c2 -> cookie_identity_matches c c2) rest
507
-
if has_duplicate then dedup acc rest else dedup (c :: acc) rest
509
-
let unique = dedup [] all_cookies in
479
+
let unique = dedup_by_identity all_cookies in
···
|> Option.value ~default:Ptime.epoch
555
-
let exp_int = try int_of_string expires with _ -> 0 in
556
-
if exp_int = 0 then None
558
-
match Ptime.of_float_s (float_of_int exp_int) with
559
-
| Some t -> Some (`DateTime t)
525
+
match int_of_string_opt expires with
526
+
| Some exp_int when exp_int <> 0 ->
527
+
Option.map (fun t -> `DateTime t)
528
+
(Ptime.of_float_s (float_of_int exp_int))
(* Mozilla format: include_subdomains=TRUE means host_only=false *)
let host_only = include_subdomains <> "TRUE" in