(* * This code was adapted from * https://github.com/mirage/mirage-www/blob/master/src/dispatch_tls.ml. * * Its copyright header is retained below. * * Copyright (c) 2015 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) open Lwt.Infix module type HTTP = Httpun_mirage.Server module type HTTP2 = H2_mirage.Server module Dispatch (Http : HTTP) (Https : HTTP) (Http2 : HTTP2) = struct let redirect = Http.create_connection_handler ?config:None ~request_handler:Http1_handler.redirect_handler ~error_handler:Http1_handler.redirect_error_handler let http1_handler = Https.create_connection_handler ?config:None ~request_handler:Http1_handler.request_handler ~error_handler:Http1_handler.error_handler let h2_handler = Http2.create_connection_handler ?config:None ~request_handler:H2_handler.request_handler ~error_handler:H2_handler.error_handler end module Make (Random : Mirage_random.S) (S : Mirage_stack.V4) (KEYS : Mirage_kv.RO) (C : Mirage_console.S) (Clock : Mirage_clock.PCLOCK) = struct module X509 = Tls_mirage.X509 (KEYS) (Clock) module TCP = S.TCPV4 module TLS = Tls_mirage.Make (TCP) module Http = Httpun_mirage.Server (TCP) module Https = Httpun_mirage.Server (TLS) module Http2 = H2_mirage.Server (TLS) module D = Dispatch (Http) (Https) (Http2) let log_src = Logs.Src.create "dispatch_tls" ~doc:"web-over-tls server" module Log = (val Logs.src_log log_src : Logs.LOG) let log c fmt = Printf.ksprintf (C.log c) fmt let with_tls cfg tcp ~f = let peer, port = TCP.dst tcp in let log str = Log.debug (fun f -> f "[%s:%d] %s" (Ipaddr.V4.to_string peer) port str) in TLS.server_of_flow cfg tcp >>= function | Error _ -> log "TLS failed"; TCP.close tcp | Ok tls_server -> log "TLS ok"; f tls_server >>= fun () -> TLS.close tls_server let tls_init kv = X509.certificate kv `Default >|= fun certificate -> Tls.Config.server ~alpn_protocols:[ "h2"; "http/1.1" ] (* accept h2 before http/1.1 *) ~certificates:(`Single certificate) () let start _random stack keys c _clock = tls_init keys >>= fun tls_config -> log c "started unikernel listen (http: 8080, https: 9443)" >>= fun () -> S.listen_tcpv4 stack ~port:8080 D.redirect; S.listen_tcpv4 stack ~port:9443 (fun flow -> with_tls tls_config flow ~f:(fun flow -> match TLS.epoch flow with | Error () -> Lwt_io.eprintlf "Unable to fetch session data. Did the handshake fail?" | Ok { Tls.Core.alpn_protocol; _ } -> (match alpn_protocol with | None -> (* Unable to negotiate a protocol *) Lwt.return_unit | Some "http/1.1" -> D.http1_handler flow | Some "h2" -> D.h2_handler flow | _ -> (* Can't really happen - would mean that TLS negotiated a * protocol that we didn't specify. *) assert false))); S.listen stack >>= fun () -> let forever, _ = Lwt.wait () in forever end