ocaml http/1, http/2 and websocket client and server library
at main 5.0 kB view raw
1(** TLS configuration and helpers for HCS. 2 3 This module provides TLS configuration that works with tls-eio and ca-certs 4 for system certificate loading. *) 5 6(** {1 ALPN Protocol Identifiers} *) 7 8(** HTTP/2 over TLS ALPN identifier *) 9let alpn_h2 = "h2" 10 11(** HTTP/1.1 ALPN identifier *) 12let alpn_http11 = "http/1.1" 13 14(** HTTP/2 cleartext (h2c) identifier - used in Upgrade header, not ALPN *) 15let alpn_h2c = "h2c" 16 17(** Protocol type for negotiation results *) 18type protocol = HTTP_1_1 | HTTP_2 19 20(** Convert ALPN string to protocol type *) 21let protocol_of_alpn = function 22 | s when s = alpn_h2 -> Some HTTP_2 23 | s when s = alpn_http11 -> Some HTTP_1_1 24 | _ -> None 25 26(** Convert protocol to ALPN string *) 27let alpn_of_protocol = function HTTP_2 -> alpn_h2 | HTTP_1_1 -> alpn_http11 28 29(** Client TLS configuration *) 30module Client = struct 31 (** Certificate verification mode *) 32 type verification = 33 | System_certs (** Use system CA certificates *) 34 | No_verify (** Disable verification (INSECURE!) *) 35 36 type t = { 37 verification : verification; 38 alpn_protocols : string list option; (** ALPN: ["h2"; "http/1.1"] *) 39 } 40 41 let default = 42 { verification = System_certs; alpn_protocols = Some [ "http/1.1" ] } 43 44 (** TLS config for HTTP/2 - advertises h2 protocol *) 45 let h2 = { verification = System_certs; alpn_protocols = Some [ "h2" ] } 46 47 (** TLS config that prefers HTTP/2 but falls back to HTTP/1.1 *) 48 let h2_or_http11 = 49 { verification = System_certs; alpn_protocols = Some [ "h2"; "http/1.1" ] } 50 51 let with_alpn protocols config = 52 { config with alpn_protocols = Some protocols } 53 54 let insecure = 55 { verification = No_verify; alpn_protocols = Some [ "http/1.1" ] } 56 57 let insecure_h2 = { verification = No_verify; alpn_protocols = Some [ "h2" ] } 58 59 (** Create tls-eio authenticator from config *) 60 let make_authenticator config = 61 match config.verification with 62 | System_certs -> ( 63 match Ca_certs.authenticator () with 64 | Ok auth -> Ok auth 65 | Error (`Msg msg) -> Error msg) 66 | No_verify -> Ok (fun ?ip:_ ~host:_ _ -> Ok None) 67 68 (** Create Tls.Config.client from our config *) 69 let to_tls_config config ~host:_ = 70 match make_authenticator config with 71 | Error msg -> Error msg 72 | Ok authenticator -> ( 73 match 74 Tls.Config.client ~authenticator ?alpn_protocols:config.alpn_protocols 75 () 76 with 77 | Ok tls_config -> Ok tls_config 78 | Error (`Msg msg) -> Error msg) 79end 80 81module Server = struct 82 type t = { 83 certificate : Tls.Config.own_cert; 84 alpn_protocols : string list option; 85 } 86 87 let with_alpn protocols config = 88 { config with alpn_protocols = Some protocols } 89 90 let h1_only config = with_alpn [ alpn_http11 ] config 91 let h2_only config = with_alpn [ alpn_h2 ] config 92 let h2_or_http11 config = with_alpn [ alpn_h2; alpn_http11 ] config 93 94 let of_pem ~cert_file ~key_file = 95 try 96 let cert_pem = In_channel.with_open_bin cert_file In_channel.input_all in 97 let key_pem = In_channel.with_open_bin key_file In_channel.input_all in 98 let certs = X509.Certificate.decode_pem_multiple cert_pem in 99 let key = X509.Private_key.decode_pem key_pem in 100 match (certs, key) with 101 | Ok certs, Ok key -> 102 Ok 103 { 104 certificate = `Single (certs, key); 105 alpn_protocols = Some [ alpn_h2; alpn_http11 ]; 106 } 107 | Error (`Msg msg), _ -> Error ("Certificate error: " ^ msg) 108 | _, Error (`Msg msg) -> Error ("Key error: " ^ msg) 109 with Sys_error msg -> Error ("File error: " ^ msg) 110 111 (** Create Tls.Config.server from our config *) 112 let to_tls_config config = 113 Tls.Config.server ~certificates:config.certificate 114 ?alpn_protocols:config.alpn_protocols () 115end 116 117(** Convert TLS failure to string *) 118let failure_to_string failure = Tls.Engine.string_of_failure failure 119 120(** Wrap an Eio flow with TLS (client side) *) 121let client_wrap ~config flow = 122 match Client.to_tls_config config ~host:"" with 123 | Error msg -> Error msg 124 | Ok tls_config -> ( 125 try 126 let tls_flow = Tls_eio.client_of_flow tls_config flow in 127 Ok tls_flow 128 with 129 | Tls_eio.Tls_failure failure -> Error (failure_to_string failure) 130 | exn -> Error (Printexc.to_string exn)) 131 132(** Wrap an Eio flow with TLS (server side) *) 133let server_wrap config flow = 134 match Server.to_tls_config config with 135 | Error (`Msg msg) -> Error msg 136 | Ok tls_config -> ( 137 try 138 let tls_flow = Tls_eio.server_of_flow tls_config flow in 139 Ok tls_flow 140 with 141 | Tls_eio.Tls_failure failure -> Error (failure_to_string failure) 142 | exn -> Error (Printexc.to_string exn)) 143 144let negotiated_protocol (tls_flow : Tls_eio.t) : protocol option = 145 match Tls_eio.epoch tls_flow with 146 | Error () -> None 147 | Ok epoch_data -> ( 148 match epoch_data.Tls.Core.alpn_protocol with 149 | None -> None 150 | Some alpn -> protocol_of_alpn alpn)