open Lwt open Ex_common let split_on_char sep s = let r = ref [] in let j = ref (String.length s) in for i = String.length s - 1 downto 0 do if s.[i] = sep then begin r := String.sub s (i + 1) (!j - i - 1) :: !r; j := i end done; String.sub s 0 !j :: !r let serve_ssl alpn_protocols port callback = let tag = "server" in X509_lwt.private_of_pems ~cert:server_cert ~priv_key:server_key >>= fun certificate -> let server_s = let open Lwt_unix in let s = socket PF_INET SOCK_STREAM 0 in bind s (ADDR_INET (Unix.inet_addr_any, port)) >|= fun () -> listen s 10 ; s in let handle ep channels addr = let alpn = match ep with | Ok data -> (match data.Tls.Core.alpn_protocol with | Some a -> a | None -> "no alpn") | Error () -> "no session" in async @@ fun () -> Lwt.catch (fun () -> callback alpn channels addr >>= fun () -> yap ~tag "<- handler done") (function | Tls_lwt.Tls_alert a -> yap ~tag @@ "handler: " ^ Tls.Packet.alert_type_to_string a | exn -> yap ~tag "handler: exception" >>= fun () -> fail exn) in let ps = string_of_int port in yap ~tag ("-> start @ " ^ ps ^ " (use `openssl s_client -connect host:" ^ ps ^ " -alpn `), available protocols: " ^ String.concat "," alpn_protocols) >>= fun () -> let rec loop () = let config = get_ok (Tls.Config.server ~certificates:(`Single certificate) ~alpn_protocols ()) in server_s >>= fun s -> Tls_lwt.Unix.accept config s >>= fun (t, addr) -> yap ~tag "-> connect" >>= fun () -> ( handle (Tls_lwt.Unix.epoch t) (Tls_lwt.of_t t) addr ; loop () ) in loop () let echo_server protocols port = serve_ssl protocols port @@ fun alpn (ic, oc) _addr -> lines ic |> Lwt_stream.iter_s (fun line -> yap ~tag:("handler alpn: " ^ alpn) ("+ " ^ line) >>= fun () -> Lwt_io.write_line oc line) let () = let protocols = try split_on_char ',' Sys.argv.(1) with _ -> [ "h2" ; "http/1.1" ] in Lwt_main.run (echo_server protocols 4433)