A batteries included HTTP/1.1 client in OCaml
at main 118 lines 4.5 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** TLS configuration utilities 7 8 This module provides shared TLS configuration creation to ensure consistent 9 behavior across session-based and one-shot request modes. 10 11 Supports ALPN (Application-Layer Protocol Negotiation) for HTTP/2 upgrade 12 per {{:https://datatracker.ietf.org/doc/html/rfc9113#section-3.3}RFC 9113 Section 3.3}. *) 13 14val src : Logs.src 15(** Logs source for this module *) 16 17(** {1 TLS Version Types} *) 18 19(** Minimum TLS version configuration. 20 Per Recommendation #6: Allow enforcing minimum TLS version. *) 21type tls_version = 22 | TLS_1_2 (** TLS 1.2 minimum (default, widely compatible) *) 23 | TLS_1_3 (** TLS 1.3 minimum (most secure, may not work with older servers) *) 24 25val tls_version_to_tls : tls_version -> Tls.Core.tls_version 26(** Convert our TLS version type to the underlying library's type *) 27 28(** {1 ALPN Protocol Negotiation} 29 30 Per {{:https://datatracker.ietf.org/doc/html/rfc9113#section-3.3}RFC 9113 Section 3.3}, 31 HTTP/2 connections over TLS use ALPN to negotiate the protocol. *) 32 33(** ALPN protocol identifiers. *) 34val alpn_h2 : string 35(** ALPN identifier for HTTP/2: "h2" *) 36 37val alpn_http11 : string 38(** ALPN identifier for HTTP/1.1: "http/1.1" *) 39 40(** HTTP protocol mode for ALPN negotiation. *) 41type protocol_mode = 42 | Auto (** Prefer HTTP/2 if available, fall back to HTTP/1.1 *) 43 | Http1_only (** Use HTTP/1.1 only *) 44 | Http2_only (** Require HTTP/2 *) 45 46val alpn_protocols : protocol_mode -> string list 47(** [alpn_protocols mode] returns the ALPN protocol list for the given mode. 48 - Auto: ["h2"; "http/1.1"] 49 - Http1_only: ["http/1.1"] 50 - Http2_only: ["h2"] *) 51 52(** {1 Configuration Creation} *) 53 54val create_client : 55 ?verify_tls:bool -> 56 ?min_tls_version:tls_version -> 57 ?protocol_mode:protocol_mode -> 58 host:string -> 59 unit -> 60 Tls.Config.client 61(** [create_client ~host ()] creates a TLS client configuration. 62 63 @param verify_tls If true (default), use system CA certificates for verification 64 @param min_tls_version Minimum TLS version to accept (default TLS_1_2) 65 @param protocol_mode HTTP protocol mode for ALPN (default Auto) 66 @param host Hostname for error messages 67 @return TLS client configuration 68 @raise Error.Tls_handshake_failed if configuration cannot be created *) 69 70val create_client_opt : 71 ?existing_config:Tls.Config.client -> 72 verify_tls:bool -> 73 min_tls_version:tls_version -> 74 ?protocol_mode:protocol_mode -> 75 host:string -> 76 unit -> 77 Tls.Config.client option 78(** [create_client_opt ~verify_tls ~min_tls_version ~host ()] creates a TLS 79 client configuration, or returns the existing one if provided. 80 81 @param existing_config If provided, return this instead of creating new 82 @param verify_tls If true, use system CA certificates for verification 83 @param min_tls_version Minimum TLS version to accept 84 @param protocol_mode HTTP protocol mode for ALPN (default Auto) 85 @param host Hostname for error messages 86 @return Some TLS client configuration *) 87 88(** {1 ALPN Result Extraction} 89 90 Helper functions for extracting negotiated protocol from TLS epoch. *) 91 92(** Negotiated HTTP protocol from ALPN. *) 93type negotiated_protocol = 94 | Http1_1 (** HTTP/1.1 *) 95 | Http2 (** HTTP/2 *) 96 97val get_alpn_from_epoch : Tls.Core.epoch_data -> string option 98(** [get_alpn_from_epoch epoch] extracts the negotiated ALPN protocol 99 from TLS epoch data. Returns [None] if ALPN was not negotiated. *) 100 101val negotiated_of_alpn : string -> negotiated_protocol option 102(** [negotiated_of_alpn alpn] parses ALPN result string. 103 - "h2" -> Some Http2 104 - "http/1.1" -> Some Http1_1 105 - other -> None *) 106 107val default_protocol : negotiated_protocol 108(** Default protocol (HTTP/1.1) when ALPN is not available. *) 109 110val detect_protocol : mode:protocol_mode -> string option -> negotiated_protocol 111(** [detect_protocol ~mode alpn_result] determines the protocol to use. 112 @raise Failure if Http2_only mode but HTTP/2 not negotiated *) 113 114val negotiated_to_string : negotiated_protocol -> string 115(** Convert negotiated protocol to string ("HTTP/1.1" or "HTTP/2"). *) 116 117val pp_negotiated : Format.formatter -> negotiated_protocol -> unit 118(** Pretty print negotiated protocol. *)