A batteries included HTTP/1.1 client in OCaml
at main 116 lines 4.1 kB view raw
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: 9 The CONNECT method requests that the recipient establish a tunnel 10 to the destination origin server and, if successful, thereafter restrict 11 its behavior to blind forwarding of packets in both 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 27(** Log source for tunnel operations *) 28val src : Logs.Src.t 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 41 an HTTP tunnel through [proxy] to [target_host:target_port]. 42 43 This performs the following steps per RFC 9110 Section 9.3.6: 44 1. Opens a TCP connection to the proxy server 45 2. Sends a CONNECT request with the target host:port 46 3. Includes Proxy-Authorization header if proxy has auth configured 47 4. Waits for a 2xx response from the proxy 48 5. Returns the raw connection for the caller to wrap with TLS 49 50 @param sw Eio switch for resource management 51 @param net Eio network capability 52 @param proxy Proxy configuration including host, port, and optional auth 53 @param target_host Destination server hostname 54 @param target_port Destination server port (typically 443 for HTTPS) 55 @raise Error.Proxy_error if the CONNECT request fails 56 @raise Error.Tcp_connect_failed if cannot connect to proxy *) 57 58val connect_with_tls : 59 sw:Eio.Switch.t -> 60 net:_ Eio.Net.t -> 61 clock:_ Eio.Time.clock -> 62 proxy:Proxy.config -> 63 target_host:string -> 64 target_port:int -> 65 ?tls_config:Tls.Config.client -> 66 unit -> 67 Eio.Flow.two_way_ty Eio.Resource.t 68(** [connect_with_tls ~sw ~net ~clock ~proxy ~target_host ~target_port ?tls_config ()] 69 establishes an HTTPS tunnel and performs TLS handshake. 70 71 This is a convenience function that combines {!connect} with TLS wrapping: 72 1. Establishes the tunnel via {!connect} 73 2. Performs TLS handshake with the target host through the tunnel 74 3. Returns the TLS-wrapped connection ready for HTTPS requests 75 76 @param sw Eio switch for resource management 77 @param net Eio network capability 78 @param clock Eio clock for TLS operations 79 @param proxy Proxy configuration 80 @param target_host Destination server hostname (used for SNI) 81 @param target_port Destination server port 82 @param tls_config Optional custom TLS configuration. If not provided, 83 uses default 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 96 a CONNECT 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 104 v} 105 106 This is exposed for testing and custom tunnel implementations. *) 107 108val parse_connect_response : 109 Eio.Buf_read.t -> 110 proxy:Proxy.config -> 111 target:string -> 112 unit 113(** [parse_connect_response r ~proxy ~target] reads and validates 114 the CONNECT response from the proxy. 115 116 @raise Error.Proxy_error if the response status is not 2xx *)