## Paf le chien - A MirageOS compatible layer for [HTTP/AF][httpaf] and [H2][h2]

This library wants to provide an easy way to use HTTP/AF & H2 into a unikernel.
It implements the global /loop/ with a protocol implementation. The code, due to
the compatibility with MirageOS, can work for `unix` too.

The protocol implementation is given by [mimic][mimic] and should be the
[mirage-tcpip][mirage-tcpip] implementation.

It does the composition between the TLS encryption layer and the
[StackV4V6][stackv4v6] implementation to provide a way to initiate a TLS
server. Via `mimic`, it still keeps the abstraction of the underlying
TCP/IP connection used.

```ocaml
module Make (Stack : Mirage_stack.V4V6) = struct
  module Paf = Paf_mirage.Make (Stack.TCP)

  let start stack =
    let* t = Paf.init ~port:80 (Stack.tcp stack) in
    let service = Paf.http_service ~error_handler request_handler in
    let `Initialized th = Paf.serve service t in
    th
end

(* For UNIX with mirage-time-unix & tcpip.stack-socket *)

include Make (Tcpip_stack_socket.V4V6.TCP)

let stack () =
  let open Tcpip_stack_socket.V4V6 in
  TCP.connect ~ipv4_only:false ~ipv6_only:false
    Ipaddr.V4.Prefix.global None

let () = Lwt_main.run ((Tcpip_stack_socket.V4V6.tcp stack) () >>= start)
```

It also provides a client-side with the logic of mimic and let the user to
implement the resolution process to determine if the connection needs the TLS
encryption (and how) layer or not.

### Mimic

Paf wants to provide an agnostic implementation of HTTP with the ability to
launch a server or a client from an user-defined context: a `Mimic.ctx`. It
does not exist one and unique way to use Paf because the context can be:
- a MirageOS
- a simple executable
- something else like a JavaScript script (with `js_of_ocaml`)

Mimic ensures the ability to gives a [Mirage_flow.S][mirage-flow] to Paf
(client side). The underlying implementation of this /flow/ depends on what the
user wants. It can be:
- [ocaml-tls][ocaml-tls]
- [lwt_ssl][lwt_ssl]
- [mirage-tcpip][mirage-tcpip]
- The host TCP/IP stack (see the `Unix` module)

All of these choices **is not** done by Paf but must be defined by the user.
Then, the CoHTTP layer trusts on [mirage-tcpip][mirage-tcpip] and
[ocaml-tls][ocaml-tls] to easily communicate with a peer from a given `Uri.t`.
Even if it seems to be the easy way to do HTTP requests (over TLS or not), the
user is able to choose some others possibilities/paths.

For example, the user is able to start a connection with an Unix domain socket:

```ocaml
module Unix_domain_socket : Mimic.Mirage_protocol.S
  with type flow = Unix.file_descr
   and type endpoint = Fpath.t

let unix_domain_socket =
  Mimic.register ~name:"unix-domain-socket" (module Unix_domain_socket)

let ctx =
  Mimic.add unix_domain_socket 
    (Fpath.v "/var/my_domain.sock") Mimic.empty
    
let run =
  Mimic.resolve ~ctx >>= function
  | Error _ as err -> Lwt.return err
  | Ok flow ->
    let body, conn = Httpaf.Client_connection.request ?config:None req
      ~error_handler ~response_handler in
    Paf.run (module Httpaf.Client_connection) conn flow >>= fun () ->
    Lwt.return_ok body
```

### CoHTTP layer

Paf comes with a not-fully-implemented compatible layer with CoHTTP. From this
sub-package and the [letsencrypt][letsencrypt] package, Paf provides a process
to download a Let's encrypt TLS certificate ready to launch an HTTPS server.

```ocaml
let cfg =
  { LE.email= Result.to_option (Emile.of_string "romain@x25519.net")
  ; LE.account_seed= None
  ; LE.account_key_type= `ED25519
  ; LE.account_key_bits= None
  ; LE.certificate_seed= None
  ; LE.certificate_key_type= `ED25519
  ; LE.certificate_key_bits= None
  ; LE.hostname= Domain_name.(host_exn (of_string_exn "x25519.net")) }

let ctx = ... (* see [mimic] *)

module Paf = Paf_mirage.Make (Time) (Tcpip_stack_socket.V4V6)

let get_tls_certificate stack =
  Lwt_switch.with_switch @@ fun stop ->
  let* t = Paf.init ~port:80 stack in
  let service = Paf.http_service
    ~error_handler
    (fun _ -> LE.request_handler) in
  let `Initialized th = Paf.serve ~stop service in
  let fiber =
    LE.provision_certificate ~production:false cfg
      (LE.ctx ~gethostbyname ~authenticator) >>= fun res ->
    Lwt_switch.turn_off stop >>= fun () -> Lwt.return res in
  Lwt.both (th, fiber) >>= fun (_, tls) -> Lwt.return tls
```

### Application Layer Protocol Negotiation

Paf provides the logic behind ALPN negotiation according a _certain_ TLS/SSL
implementation. In other words, Paf is able to correctly dispatch which
protocol the client wants without a requirement of [ocaml-tls][ocaml-tls] or
[lwt_ssl][lwt_ssl]. The module `Alpn` is a HTTP service which handles:
- HTTP/1.1
- H2

`Alpn` requires:
- the `accept` and the `close` function
- a way to extract the result of the Application Layer Protocol Negotiation
- the Mimic's _injection_
- `error_handler` and `request_handler` which handle HTTP/1.0, HTTP/1.1 and
  H2 requests

Here is an example with HTTP (without TLS):
```ocaml
let _, protocol
  : Unix.sockaddr Mimic.value
    * (Unix.sockaddr, Lwt_unix.file_descr) Mimic.protocol
  = Mimic.register ~name:"lwt-tcp" (module TCP)

let accept t =
  Lwt.catch begin fun () ->
    Lwt_unix.accept >>= fun (socket, _) ->
    Lwt.return_ok socket
  end @@ function
  | Unix.Unix_error (err, f, v) ->
    Lwt.return_error (`Unix (err, f, v))
  | exn -> raise exn

let info =
  let module R = (val Mimic.register protocol) in
  { Alpn.alpn= const None
  ; Alpn.peer= (fun socket ->
    sockaddr_to_string (Lwt_unix.getpeername socket))
  ; Alpn.injection=
    (fun socket -> R.T socket) }

let service = Alpn.service info handler
  accept Lwt_unix.close

let fiber =
  let t = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
  Lwt_unix.bind t (Unix.ADDR_INET (Unix.inet_addr_loopback, 8080))
  >>= fun () ->
  let `Initialized th = Paf.serve
    service t in th

let () = Lwt_main.run fiber
```

### Tests & Benchmark

The distribution comes with a tool which launch several clients to communicate
with a server. We record the time spent for each request and show as the result
the histogram of them. It's not really a benchmark as is but it a good
stress-test and we check that we don't have failure from the server.

[httpaf]: https://github.com/inhabitedtype/httpaf
[mimic]: https://github.com/mirage/ocaml-git
[mirage-tcpip]: https://github.com/mirage/mirage-tcpip
[letsencrypt]: https://github.com/mmaker/ocaml-letsencrypt
[stackv4v6]: https://github.com/mirage/mirage-stack
[ocaml-tls]: https://github.com/mirleft/ocaml-tls
[lwt_ssl]: https://github.com/ocsigen/lwt_ssl
[mirage-flow]: https://github.com/mirage/mirage-flow
[h2]: https://github.com/anmonteiro/ocaml-h2
