forked from
anil.recoil.org/ocaml-requests
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 CONNECT Tunneling for HTTPS via Proxy
7
8 Per RFC 9110 Section 9.3.6: The CONNECT method requests that the recipient
9 establish a tunnel to the destination origin server and, if successful,
10 thereafter restrict its behavior to blind forwarding of packets in both
11 directions.
12
13 {2 Usage}
14
15 Establish an HTTPS tunnel through an HTTP proxy:
16 {[
17 let tunnel_flow = Proxy_tunnel.connect
18 ~sw ~net
19 ~proxy:(Proxy.http "proxy.example.com")
20 ~target_host:"api.example.com"
21 ~target_port:443
22 ()
23 in
24 (* Now wrap tunnel_flow with TLS and send HTTPS requests *)
25 ]} *)
26
27val src : Logs.Src.t
28(** Log source for tunnel operations. *)
29
30(** {1 Tunnel Establishment} *)
31
32val connect :
33 sw:Eio.Switch.t ->
34 net:_ Eio.Net.t ->
35 proxy:Proxy.config ->
36 target_host:string ->
37 target_port:int ->
38 unit ->
39 [ `Close | `Flow | `R | `Shutdown | `W ] Eio.Resource.t
40(** [connect ~sw ~net ~proxy ~target_host ~target_port ()] establishes an HTTP
41 tunnel through [proxy] to [target_host:target_port].
42
43 This performs the following steps per RFC 9110 Section 9.3.6: 1. Opens a TCP
44 connection to the proxy server 2. Sends a CONNECT request with the target
45 host:port 3. Includes Proxy-Authorization header if proxy has auth
46 configured 4. Waits for a 2xx response from the proxy 5. Returns the raw
47 connection for the caller to wrap with TLS
48
49 @param sw Eio switch for resource management
50 @param net Eio network capability
51 @param proxy Proxy configuration including host, port, and optional auth
52 @param target_host Destination server hostname
53 @param target_port Destination server port (typically 443 for HTTPS)
54 @raise Error.Proxy_error if the CONNECT request fails
55 @raise Error.Tcp_connect_failed if cannot connect to proxy. *)
56
57val connect_with_tls :
58 sw:Eio.Switch.t ->
59 net:_ Eio.Net.t ->
60 clock:_ Eio.Time.clock ->
61 proxy:Proxy.config ->
62 target_host:string ->
63 target_port:int ->
64 ?tls_config:Tls.Config.client ->
65 unit ->
66 Eio.Flow.two_way_ty Eio.Resource.t
67(** [connect_with_tls ~sw ~net ~clock ~proxy ~target_host ~target_port
68 ?tls_config ()] establishes an HTTPS tunnel and performs TLS handshake.
69
70 This is a convenience function that combines {!connect} with TLS wrapping:
71 1. Establishes the tunnel via {!connect} 2. Performs TLS handshake with the
72 target host through the tunnel 3. Returns the TLS-wrapped connection ready
73 for HTTPS requests
74
75 @param sw Eio switch for resource management
76 @param net Eio network capability
77 @param clock Eio clock for TLS operations
78 @param proxy Proxy configuration
79 @param target_host Destination server hostname (used for SNI)
80 @param target_port Destination server port
81 @param tls_config
82 Optional custom TLS configuration. If not provided, uses default
83 configuration from system CA certificates.
84 @raise Error.Proxy_error if tunnel establishment fails
85 @raise Error.Tls_handshake_failed if TLS handshake fails. *)
86
87(** {1 Low-level Functions} *)
88
89val write_connect_request :
90 Eio.Buf_write.t ->
91 proxy:Proxy.config ->
92 target_host:string ->
93 target_port:int ->
94 unit
95(** [write_connect_request w ~proxy ~target_host ~target_port] writes a CONNECT
96 request to the buffer.
97
98 Format per RFC 9110 Section 9.3.6:
99 {v
100 CONNECT host:port HTTP/1.1
101 Host: host:port
102 Proxy-Authorization: Basic ... (if auth configured)
103 v}
104
105 This is exposed for testing and custom tunnel implementations. *)
106
107val parse_connect_response :
108 Eio.Buf_read.t -> proxy:Proxy.config -> target:string -> unit
109(** [parse_connect_response r ~proxy ~target] reads and validates the CONNECT
110 response from the proxy.
111
112 @raise Error.Proxy_error if the response status is not 2xx. *)