package paf

  1. Overview
  2. Docs

Parameters

Signature

exception Error of Mimic.error
type stack = Stack.t
type service

The type of services.

val init : port:int -> Stack.t -> service Lwt.t

init ~port stack returns a service bound on port with stack.

val http : ?config:Httpaf.Config.t -> ?stop:Lwt_switch.t -> error_handler:((Ipaddr.t * int) -> Httpaf.Server_connection.error_handler) -> request_handler: ((Ipaddr.t * int) -> Httpaf.Server_connection.request_handler) -> service -> [ `Initialized of unit Lwt.t ]

http ?config ?stop ~error_handler ~request_handler service promises a service loop computation that is ready to receive connections. The inner promise is then determined once the service loop has ended - by default, only when an error occurs.

If passed, stop is a switch that terminates the service loop, for example to limit execution time to 10 seconds:

let* service = init ~port:80 stack in
let stop = Lwt_switch.create () in
let `Initialized server = http ~stop ... service in
Lwt.both (Lwt_unix.sleep 10. >>= fun () -> Lwt_switch.turn_off stop) server

This is useful when subsequent actions are reliant on the service loop having begin, such as when testing with a client-server pair:

let* service = init ~port:80 stack in
let `Initialized server = http ... service in
Lwt.both server (client >|= signal_stop)
val https : tls:Tls.Config.server -> ?config:Httpaf.Config.t -> ?stop:Lwt_switch.t -> error_handler:((Ipaddr.t * int) -> Httpaf.Server_connection.error_handler) -> request_handler: ((Ipaddr.t * int) -> Httpaf.Server_connection.request_handler) -> service -> [ `Initialized of unit Lwt.t ]

Same as http, but requires a TLS certificate tls.

val tcp_edn : (Stack.t * Ipaddr.t * int) Mimic.value
val tls_edn : ([ `host ] Domain_name.t option * Tls.Config.client * Stack.t * Ipaddr.t * int) Mimic.value
val request : ?config:Httpaf.Config.t -> ctx:Mimic.ctx -> error_handler: (Mimic.flow -> (Ipaddr.t * int) option -> Httpaf.Client_connection.error_handler) -> response_handler: ((Ipaddr.t * int) option -> Httpaf.Client_connection.response_handler) -> Httpaf.Request.t -> ([ `write ] Httpaf.Body.t, [> Mimic.error ]) Stdlib.result Lwt.t

request ?config ~ctx ~error_handler ~response_handler req returns a open HTTP body according to the given context ctx and the request req.

To be able to start a simple HTTP connection, you must:

  • know the target IP address
  • create the client stack
  • know the target port (default to 80)

These values must exist into the given ctx to, at least, be able to start a TCP/IP connection:

let ctx =
  Mimic.add Paf.tcp_edn
    (stack, ipaddr_of_google, 80)
    Mimic.empty Paf.request ~ctx ~error_handler ~response_handler req

The user is able to fill the ctx with some clever processes such as a DNS resolver:

let domain_name = Mimic.make ~name:"domain-name"

let ipaddr = Mimic.make ~name:"ipaddr"

let resolver domain_name =
  match Unix.gethostbyname (Domain_name.to_string domain_name) with
  | { Unix.h_addr_list; _ } ->
      if Array.length h_addr_list > 0
      then Lwt.return_some (Ipaddr_unix.of_inet_addr h_addr_list.(0))
      else Lwt.return_none
  | exception _ -> Lwt.return_none

let stack = Mimic.make ~name:"stack"

let port = Mimic.make ~name:"port"

let connect stack ipaddr port = Lwt.return_some (stack, ipaddr, port)

let ctx =
  let open Mimic in
  fold ipaddr Fun.[ req domain_name ] ~k:resolver Mimic.empty
  |> fold Paf.tcp_edn
       Fun.[ req stack; req ipaddr; dft port 80 ]
       ~k:connect

If mimic is able to create a tcp_edn value from the ctx and these functions, the user will be able to start a TCP/IP connection. ipaddr will come from a given domain_name and if the port is missing, we use 80 as the default value. The stack still is required.

For a more user-friendly interface, you should take a look into paf.cohttp.

OCaml

Innovation. Community. Security.