# Cohttp-eio Note: `Cohttp` is the proper module name and `cohttp` is the opam package/library. All further changes to be made in this PR. ---- --- author: Bikal Gurung title: "Cohttp-eio in OCaml 5" kind: article image: ../images/cohttp.jpg date: "2023-01-17" --- `cohttp-eio` is a HTTP/1.1 based web programming library for OCaml 5.0 and onwards. It is designed from the ground up to benefit from OCaml 5's headline feature - domains-based parallelism - and `eio,` an effects-based I/O concurrency library. One notable feature of the library is the absence of an I/O (Input/Output) monad in its API. Due to this, the ergonomics of the API matches that of the OCaml `stdlib`. Both seasoned and beginner OCaml developers will find that the API is inviting, approachable, and productive. ## Installing the `cohttp-eio` Package `cohttp-eio` needs a working opam environment and an OCaml 5.0 compiler. The OCaml 5.0 compiler can be installed in a working opam environment as such: ``` opam update opam switch create 5.0.0 ``` The current released version of `cohttp-eio` is `alpha0`. It can be installed as follows: `opam install cohttp-eio.6.0.0~alpha0` An OCaml executable program using `cohttp-eio` then declares the dependencies as such: ``` $ cat dune (executable (name hello_world) (libraries cohttp-eio eio eio_main)) ``` ## `cohttp-eio` API `cohttp-eio` provides modules `Server` and `Client` under the `Cohttp_eio` module. They implement an HTTP/1.1 server and client functionality, respectively. For the sake of brevity, assume that there is an `open Cohttp_eio` statement in the code fragments presented below and throughout the rest of the article. ### `Server.handler` `Server.handler` is an OCaml function which expects a HTTP/1.1 `Server.request` and produces a HTTP/1.1 `Server.response`. It is formally defined as: ``` type handler = Server.request -> Server.response ``` `cohttp-eio` provides a few convenient functions that can be used in a handler. These functions encapsulate adding/creating correct HTTP/1.1 response headers and bodies. A couple of these are as follows: `Server.text_response`, used to respond with plain text response in a handler. `Server.html_response`, used to respond with a HTML response in a handler. ### `Server.run` `Server.run` runs HTTP/1.1 server configured to execute a given handler. Optionally, we can specify the number of domains the server can use via the `~domains` parameter. Domain is a unit of parallelism in OCaml 5.0 and beyond. Semantically, it roughly corresponds to a CPU core in a computer. Though there is no hard, upper limit to number of domains, it is generally recommended that `~domains` matches the total number of CPU cores in the machine. The default value of `domains` is 1. Lastly, we specify the TCP/IP port of the listening server. A typical usage of `Server.run` looks like below: ``` let () = Eio_main.run @@ fun env -> Server.run ~domains:12 ~port:8080 env handler ``` ### Hello World Server A small demonstration of what a `cohttp-eio` server looks like is as follows: ``` $ cat hello_world.ml open Cohttp_eio let app : Server.handler = fun ((req, _,_) : Server.request) -> match Http.Request.resource req with | "/" -> Server.text_response "Hello world!" | "/html" -> Server.html_response "<h1>Hello world!</h1>" | _ -> Server.not_found_response let () = Eio_main.run @@ fun env -> Server.run ~port:8080 env app ``` `hello_world.ml` can be built with the `dune` file excerpt given above. A terminal shell session might look like this: ``` $ ls dune hello_world.ml $ dune b && dune exec ./hello_world.exe ``` ### Client `cohttp-eio` provides a `Cohttp_eio.Client` module, which implements HTTP/1.1 client functionality. Similarly to `Cohttp_eio.Server`, a client sends an HTTP request and expects an HTTP response. Broadly, HTTP client executions are categorised into two types: ``` type 'a body_disallowed_call = ?pipeline_requests:bool -> ?version:Http.Version.t -> ?headers:Http.Header.t -> conn:(#Eio.Flow.two_way as 'a) -> host -> resource_path -> response ``` and ``` type 'a body_allowed_call = ?pipeline_requests:bool -> ?version:Http.Version.t -> ?headers:Http.Header.t -> ?body:Body.t -> conn:(#Eio.Flow.two_way as 'a) -> host -> resource_path -> response ``` The main difference between the two call types, as the names respectively suggest, are that the first doesn't allow for including a request `body` in the call, but the second one does allow it. `Client.get/head/delete` are examples of the first, while `Client.post/put/patch` are examples of the second. An example of a `Cohtto_eio.Client` usage is as follows: ``` open Cohttp_eio let () = let host, port = ("www.example.org", 80) in Eio_main.run @@ fun env -> Eio.Net.with_tcp_connect ~host ~service:(string_of_int port) env#net (fun conn -> let host = (host, Some port) in let res = Client.get ~conn host "/" in print_string @@ Client.read_fixed res) ``` ## Summary `cohttp-eio` aims to implement HTTP Servers and Clients which are compliant to [RFC 9110 - HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110#section-6.6.1) specification. Although not discussed in this article, this includes full HTTP chunk encoding support.