A batteries included HTTP/1.1 client in OCaml
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** HTTP Link header parsing per RFC 8288
7
8 This module parses Link headers commonly used for:
9 - API pagination (rel="next", "prev", "first", "last")
10 - Resource discovery
11 - Relationship navigation
12
13 Per Recommendation #19: Parse Link headers for pagination support.
14
15 {2 Example: Following Pagination}
16 {[
17 let rec fetch_all_pages session url acc =
18 let response = Requests.get session url in
19 let data = Response.body response |> Eio.Flow.read_all in
20 let acc = data :: acc in
21 match Link.next_url (Response.headers response) with
22 | Some next -> fetch_all_pages session next acc
23 | None -> List.rev acc
24 ]}
25
26 {2 Example: Getting All Pagination URLs}
27 {[
28 let response = Requests.get session "https://api.example.com/items" in
29 let (first, prev, next, last) = Link.pagination (Response.headers response) in
30 match next with
31 | Some url -> Printf.printf "Next page: %s\n" url
32 | None -> print_endline "No more pages"
33 ]}
34*)
35
36(** A parsed Link header entry *)
37type t
38
39(** {1 Constructors} *)
40
41val make :
42 uri:string ->
43 ?rel:string ->
44 ?title:string ->
45 ?media_type:string ->
46 ?hreflang:string ->
47 ?params:(string * string) list ->
48 unit -> t
49(** Create a link value *)
50
51(** {1 Accessors} *)
52
53val uri : t -> string
54(** The target URI *)
55
56val rel : t -> string option
57(** The relation type (e.g., "next", "prev", "last", "self") *)
58
59val title : t -> string option
60(** Human-readable title *)
61
62val media_type : t -> string option
63(** Media type hint (from "type" parameter) *)
64
65val hreflang : t -> string option
66(** Language hint *)
67
68val params : t -> (string * string) list
69(** Additional parameters not covered by standard accessors *)
70
71(** {1 Parsing} *)
72
73val parse : string -> t list
74(** Parse a Link header value into a list of links.
75 Handles multiple comma-separated links. *)
76
77val from_headers : Headers.t -> t list
78(** Extract and parse Link header from response headers.
79 Returns empty list if no Link header present. *)
80
81(** {1 Finding Links} *)
82
83val find_rel : string -> t list -> t option
84(** Find the first link with a specific relation type *)
85
86val filter_rel : string -> t list -> t list
87(** Find all links with a specific relation type *)
88
89(** {1 Pagination Helpers} *)
90
91val pagination : Headers.t -> string option * string option * string option * string option
92(** [pagination headers] extracts pagination links.
93 Returns [(first, prev, next, last)] where each is optional.
94
95 Looks for links with rel="first", rel="prev", rel="next", rel="last". *)
96
97val has_next : Headers.t -> bool
98(** Check if there are more pages (next link exists) *)
99
100val next_url : Headers.t -> string option
101(** Get the next page URL if available *)
102
103val prev_url : Headers.t -> string option
104(** Get the previous page URL if available *)
105
106(** {1 Formatting} *)
107
108val pp : Format.formatter -> t -> unit
109(** Pretty-print a link in Link header format *)
110
111val to_string : t -> string
112(** Convert link to string representation *)
113
114(** {1 Logging} *)
115
116val src : Logs.Src.t
117(** Log source for link parsing operations *)