A batteries included HTTP/1.1 client in OCaml
at main 112 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: 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. *)