open Common_ type t = (string * string) list let empty = [] let contains name headers = let name' = String.lowercase_ascii name in List.exists (fun (n, _) -> name' = n) headers let get_exn ?(f = fun x -> x) x h = let x' = String.lowercase_ascii x in List.assoc x' h |> f let get ?(f = fun x -> x) x h = try Some (get_exn ~f x h) with Not_found -> None let remove x h = let x' = String.lowercase_ascii x in List.filter (fun (k, _) -> k <> x') h let set x y h = let x' = String.lowercase_ascii x in (x', y) :: List.filter (fun (k, _) -> k <> x') h let pp out l = let pp_pair out (k, v) = Format.fprintf out "@[%s: %s@]" k v in Format.fprintf out "@[%a@]" (Format.pp_print_list pp_pair) l (* token = 1*tchar tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA ; any VCHAR, except delimiters Reference: https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 *) let is_tchar = function | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | '-' | '.' | '^' | '_' | '`' | '|' | '~' -> true | _ -> false let for_all pred s = try String.iter (fun c -> if not (pred c) then raise Exit) s; true with Exit -> false let parse_ ~(buf : Buf.t) (bs : IO.Input.t) : t = let rec loop acc = match IO.Input.read_line_using_opt ~buf bs with | None -> raise End_of_file | Some "\r" -> acc | Some line -> Log.debug (fun k -> k "parsed header line %S" line); let k, v = try let i = String.index line ':' in let k = String.sub line 0 i in if not (for_all is_tchar k) then invalid_arg (Printf.sprintf "Invalid header key: %S" k); let v = String.sub line (i + 1) (String.length line - i - 1) |> String.trim in k, v with _ -> bad_reqf 400 "invalid header line: %S" line in loop ((String.lowercase_ascii k, v) :: acc) in loop []