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 Proxy Configuration
7
8 Per RFC 9110 Section 3.7 and Section 7.3.2:
9 A proxy is a message-forwarding agent chosen by the client,
10 usually configured via local rules.
11
12 {2 Usage}
13
14 Create a proxy configuration:
15 {[
16 let proxy = Proxy.http ~port:8080 "proxy.example.com"
17
18 (* With authentication *)
19 let proxy = Proxy.http
20 ~port:8080
21 ~auth:(Auth.basic ~username:"user" ~password:"pass")
22 "proxy.example.com"
23
24 (* With bypass list *)
25 let proxy = Proxy.http
26 ~no_proxy:["localhost"; "*.internal.example.com"]
27 "proxy.example.com"
28 ]}
29
30 Read from environment variables:
31 {[
32 match Proxy.from_env () with
33 | Some proxy -> (* use proxy *)
34 | None -> (* no proxy configured *)
35 ]} *)
36
37(** Log source for proxy operations *)
38val src : Logs.Src.t
39
40(** {1 Proxy Types} *)
41
42(** Proxy protocol type *)
43type proxy_type =
44 | HTTP (** HTTP proxy (CONNECT for HTTPS, absolute-URI for HTTP) *)
45 | SOCKS5 (** SOCKS5 proxy (RFC 1928) - future extension *)
46
47(** Proxy configuration *)
48type config = {
49 host : string; (** Proxy server hostname *)
50 port : int; (** Proxy server port (default: 8080) *)
51 proxy_type : proxy_type;
52 auth : Auth.t option; (** Proxy authentication (Proxy-Authorization) *)
53 no_proxy : string list; (** Hosts/patterns to bypass proxy *)
54}
55
56(** {1 Configuration Constructors} *)
57
58val http : ?port:int -> ?auth:Auth.t -> ?no_proxy:string list -> string -> config
59(** [http ?port ?auth ?no_proxy host] creates an HTTP proxy configuration.
60
61 @param port Proxy port (default: 8080)
62 @param auth Proxy authentication credentials
63 @param no_proxy List of hosts/patterns to bypass the proxy.
64 Supports wildcards like [*.example.com] to match [foo.example.com].
65 @param host Proxy server hostname *)
66
67val socks5 : ?port:int -> ?auth:Auth.t -> ?no_proxy:string list -> string -> config
68(** [socks5 ?port ?auth ?no_proxy host] creates a SOCKS5 proxy configuration.
69
70 {b Note:} SOCKS5 support is not yet implemented. This function creates
71 the configuration type for future use.
72
73 @param port Proxy port (default: 1080)
74 @param auth Proxy authentication credentials
75 @param no_proxy List of hosts/patterns to bypass the proxy
76 @param host Proxy server hostname *)
77
78(** {1 Configuration Utilities} *)
79
80val should_bypass : config -> string -> bool
81(** [should_bypass config url] returns [true] if [url] should bypass
82 the proxy based on the [no_proxy] list.
83
84 Matching rules:
85 - Exact hostname match (case-insensitive)
86 - Wildcard prefix match: [*.example.com] matches [foo.example.com]
87 - [localhost] and [127.0.0.1] match by default if in no_proxy list *)
88
89val host_port : config -> string * int
90(** [host_port config] returns the proxy host and port as a tuple. *)
91
92val validate_supported : config -> unit
93(** [validate_supported config] checks that the proxy type is currently supported.
94 @raise Error.Proxy_error if SOCKS5 is requested (not yet implemented) *)
95
96(** {1 Environment Variable Support} *)
97
98val from_env : unit -> config option
99(** [from_env ()] reads proxy configuration from environment variables.
100
101 Checks the following variables (in order of preference):
102 - [HTTP_PROXY] / [http_proxy]
103 - [HTTPS_PROXY] / [https_proxy]
104 - [ALL_PROXY] / [all_proxy]
105 - [NO_PROXY] / [no_proxy] (comma-separated list of bypass patterns)
106
107 Returns [None] if no proxy is configured.
108
109 URL format: [http://[user:pass@]host[:port]]
110
111 Example environment:
112 {[
113 HTTP_PROXY=http://user:pass@proxy.example.com:8080
114 NO_PROXY=localhost,*.internal.example.com,.example.org
115 ]} *)
116
117val from_env_for_url : string -> config option
118(** [from_env_for_url url] reads proxy configuration appropriate for [url].
119
120 - Uses [HTTPS_PROXY] for HTTPS URLs
121 - Uses [HTTP_PROXY] for HTTP URLs
122 - Falls back to [ALL_PROXY]
123 - Returns [None] if the URL matches [NO_PROXY] patterns *)
124
125(** {1 Pretty Printing} *)
126
127val pp_proxy_type : Format.formatter -> proxy_type -> unit
128(** Pretty printer for proxy type *)
129
130val pp_config : Format.formatter -> config -> unit
131(** Pretty printer for proxy configuration.
132 Note: Authentication credentials are redacted. *)