package cohttp-lwt
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=352a8ef1288572ad0b00f5c63be62ed6daa6bf5c3d1203da13fa9a4b09c5957b
sha512=9c32228e6284e372d70d236cba35dc0aa822180779ca457caaa1f3a048cb3d5f4c6473046ac4c92e2f608b15e44d03ff431163776bd46a093c9f1ed8fbcc821d
Description
This is a portable implementation of HTTP that uses the Lwt
concurrency library to multiplex IO. It implements as much of the
logic in an OS-independent way as possible, so that more specialised
modules can be tailored for different targets. For example, you
can install cohttp-lwt-unix
or cohttp-lwt-jsoo
for a Unix or
JavaScript backend, or cohttp-mirage
for the MirageOS unikernel
version of the library. All of these implementations share the same
IO logic from this module.
README
ocaml-cohttp -- an OCaml library for HTTP clients and servers
Cohttp is an OCaml library for creating HTTP daemons. It has a portable HTTP parser, and implementations using various asynchronous programming libraries:
Cohttp_lwt_unix
uses the Lwt library, and specifically the UNIX bindings.Cohttp_async
uses the Async library.Cohttp_lwt
exposes an OS-independent Lwt interface, which is used by the Mirage interface to generate standalone microkernels (use the cohttp-mirage subpackage).Cohttp_lwt_xhr
compiles to a JavaScript module that maps the Cohttp calls to XMLHTTPRequests. This is used to compile OCaml libraries like the GitHub bindings to JavaScript and still run efficiently.
You can implement other targets using the parser very easily. Look at the IO
signature in lib/s.mli
and implement that in the desired backend.
You can activate some runtime debugging by setting COHTTP_DEBUG
to any value, and all requests and responses will be written to stderr. Further debugging of the connection layer can be obtained by setting CONDUIT_DEBUG
to any value.
Installation
Latest stable version should be obtained from opam. Make sure to install the specific backends you want as well. E.g.
$ opam install cohttp lwt js_of_ocaml
You can also obtain the development release:
$ opam pin add cohttp --dev-repo
Findlib (Ocamlfind)
Cohttp ships with 6 findlib libraries:
cohttp - Base
Cohttp
module. No platform specific functionality.cohttp-async - Async backend
Cohttp_async
cohttp-lwt-jsoo - Jsoo (XHR) client
cohttp-lwt - Lwt backend without unix specifics.
cohttp-lwt-unix - Unix based lwt backend
cohttp-top - Print cohttp types in the toplevel (
#require "cohttp-top"
)
Client Tutorial
Cohttp provides clients for Async, Lwt, and jsoo (Lwt based). In this tutorial, we will use the lwt client but it should be easily translateable to Async.
To create a simple request, use one of the methods in Cohttp_lwt_unix.Client
. call
is the most general, there are also http method specialized such as get
, post
, etc.
For example downloading the reddit frontpage:
(* client_example.ml *)
open Lwt
open Cohttp
open Cohttp_lwt_unix
let body =
Client.get (Uri.of_string "https://www.reddit.com/") >>= fun (resp, body) ->
let code = resp |> Response.status |> Code.code_of_status in
Printf.printf "Response code: %d\n" code;
Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
Printf.printf "Body of length: %d\n" (String.length body);
body
let () =
let body = Lwt_main.run body in
print_endline ("Received body\n" ^ body)
Build with:
ocamlbuild -pkg cohttp-lwt-unix client_example.native
There's a few things to notice:
We open 2 modules.
Cohttp
contains the backend independent stuff andCohttp_lwt_unix
is the lwt + unix specific stuff.Client.get
accepts aUri.t
and makes an http request.Client.get
also accepts optional arguments for things like header information.The http response is returned in a tuple. The first element of the tuple contains the response's status code, headers, http version, etc. The second element contains the body.
The body is then converted to a string and is returned (after the length is printed). Note that
Cohttp_lwt.Body.to_string
hence it's up to us to keep a reference to the result.We must trigger lwt's event loop for the request to run.
Lwt_main.run
will run the event loop and return with final value ofbody
which we then print.
Note that in order to request an HTTPS page like in the above example, you'll need Cohttp to have been compiled with SSL or TLS. For SSL, you'll need to install both ssl
and lwt_ssl
before installing cohttp
. The TLS route will require installing tls
before cohttp
.
Consult the following modules for reference:
Docker Socket Client example
Cohttp provides a lot of utilites out of the box, but does not prevent the users to dig in and customise it for their needs. The following is an example of a unix socket client to communicate with Docker.
open Lwt.Infix
let t =
let resolver =
let h = Hashtbl.create 1 in
Hashtbl.add h "docker" (`Unix_domain_socket "/var/run/docker.sock");
Resolver_lwt_unix.static h in
let ctx = Cohttp_lwt_unix.Client.custom_ctx ~resolver () in
Cohttp_lwt_unix.Client.get ~ctx (Uri.of_string "http://docker/version") >>= fun (resp, body) ->
let open Cohttp in
let code = resp |> Response.status |> Code.code_of_status in
Printf.printf "Response code: %d\n" code;
Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
Printf.printf "Body of length: %d\n" (String.length body);
print_endline ("Received body\n" ^ body)
let _ = Lwt_main.run t
The main issue there is there no way to resolve a socket address, so you need to create a custom resolver to map a hostname to the Unix domain socket.
Basic Server Tutorial
Implementing a server in cohttp is mostly equivalent to implementing a function of type:
conn -> Cohttp.Request.t -> Cohttp_lwt.Body.t -> (Cohttp.Response.t * Cohttp_lwt.Body.t) Lwt.t
The parameters are self explanatory but we'll summarize them quickly here:
conn
- contains connection informationCohttp.Request.t
- Request information such as method, uri, headers, etc.Cohttp_lwt.Body.t
- Contains the request body. You must manually decode the request body into json, form encoded pairs, etc. For cohttp, the body is simply binary data.
Here's an example of a simple cohttp server that outputs back request information.
(* server_example.ml *)
open Lwt
open Cohttp
open Cohttp_lwt_unix
let server =
let callback _conn req body =
let uri = req |> Request.uri |> Uri.to_string in
let meth = req |> Request.meth |> Code.string_of_method in
let headers = req |> Request.headers |> Header.to_string in
body |> Cohttp_lwt.Body.to_string >|= (fun body ->
(Printf.sprintf "Uri: %s\nMethod: %s\nHeaders\nHeaders: %s\nBody: %s"
uri meth headers body))
>>= (fun body -> Server.respond_string ~status:`OK ~body ())
in
Server.create ~mode:(`TCP (`Port 8000)) (Server.make ~callback ())
let () = ignore (Lwt_main.run server)
Build with:
ocamlbuild -pkg cohttp-lwt-unix server_example.native
The following modules are useful references:
Cohttp_lwt.Server - Common to mirage and Unix
Cohttp_lwt_unix.Server - Unix specific.
Installed Binaries
Cohttp comes with a few simple binaries that are handy, useful testing cohttp itself, and serve as examples of how to use cohttp. The binaries come in two flavours - Async and Lwt based.
$ cohttp-curl-{lwt,async}
This is a simple curl utility implemented using cohttp. An example of an invocation is:
$ cohttp-curl-lwt -v -X GET "https://www.reddit.com/"
$ cohttp-server-{lwt,async}
This binary acts in a similar fashion to the Python SimpleHTTPServer
. Just run cohttp-server-async
in a directory and it will open up a local port and serve the files over HTTP.
$ cohttp-server-async
Assuming that the server is running in cohttp's source directory:
$ cohttp-curl-lwt 'http://0.0.0.0:8080/_oasis'
Important Links
Dependencies (7)
Dev Dependencies
None
Used by (43)
- aws-lwt
- bitcoin-cohttp-lwt
-
calculon-web
= "0.4"
- caldav
- canary
- clz
-
cohttp-lwt-jsoo
< "2.2.0" | = "2.5.5"
-
cohttp-lwt-unix
= "2.5.5"
-
cohttp-mirage
>= "2.2.0" & < "2.5.6"
-
current_github
>= "0.4" & < "0.6"
-
current_slack
>= "0.4" & < "0.6"
-
current_web
>= "0.4" & < "0.6"
-
dropbox
>= "0.2"
- git-cohttp
- git-cohttp-mirage
- git-cohttp-unix
-
git-http
>= "2.0.0"
-
git-paf
< "3.5.0"
-
github
>= "3.0.1" & < "4.4.0"
-
github-hooks
>= "0.3.0"
- irmin-cli
- irmin-graphql
-
irmin-http
>= "1.3.3"
-
irmin-mirage-graphql
>= "2.3.0"
-
irmin-unix
>= "2.3.0"
-
ldp_curl
>= "0.2.0"
-
learn-ocaml
>= "0.13.0"
-
letsencrypt
< "0.3.0"
-
links
>= "0.9"
-
mechaml
>= "1.0.0"
-
mirage-http
>= "3.2.0"
-
multipart_form-cohttp-lwt
< "0.6.0"
- opentelemetry-client-cohttp-lwt
- opium_kernel
-
paf
< "0.0.4"
- paf-cohttp
-
prometheus-app
>= "0.4" & < "1.2"
- resto-cohttp
-
resto-cohttp-client
< "1.0"
- resto-cohttp-self-serving-client
- session-cohttp-lwt
-
telegraml
>= "2.2.0"
-
vpnkit
>= "0.1.1"
Conflicts
None