Skip to content
Very lightweight HTTP server using Lwt or Async
OCaml JavaScript Other
Pull request Compare This branch is 61 commits behind mirage:master.
Latest commit 167e5fa Dec 20, 2015 @rgrinberg rgrinberg Merge pull request #451 from rgrinberg/test-449
Test for bug fixed #448
Failed to load latest commit information.
async Async: don't wait forever to close connection Nov 15, 2015
bin Small improvements to cohttp_server_async.ml Oct 28, 2015
docs Add documentation improvements May 4, 2015
examples Surround source copyright with fold markers Jul 19, 2015
js get rid of lwt.syntax in xhr backend Jul 29, 2015
lib release 0.19.3 Sep 28, 2015
lib_test Test for bug fixed #448 Dec 20, 2015
lwt-core Fix issue #448 after breakage from PR #447 Dec 15, 2015
lwt IO.write_line is not used anywhere Jul 29, 2015
scripts Surround source copyright with fold markers Jul 19, 2015
top regenerate oasis Jun 2, 2015
.gitignore add test out dir to gitignore May 20, 2015
.merlin Update .merlin Dec 12, 2015
.ocp-indent ocp-indent config file for lwt Feb 2, 2014
.travis.yml Travis: add more revdeps on opium and slacko Jul 12, 2015
CHANGES release 0.19.3 Sep 28, 2015
DESIGN.md Add a very skeletal `DESIGN.md` document to explain the library struc… Apr 24, 2015
LICENSE Replace LICENSE with the ISC, which has been true of the individual Jan 16, 2014
Makefile Add documentation improvements May 5, 2015
README.md Document findlib packages in README.md Oct 23, 2015
TODO.md update TODO Jul 26, 2015
_oasis release 0.19.3 Sep 28, 2015
_opam Add several fields and versions constraints in _oasis Jan 1, 2015
_tags remove oasis Jul 29, 2015
cohttp.install Actually installs binaries fixing regression in bd626e for #252 Mar 30, 2015
myocamlbuild.ml regenerate oasis Jun 18, 2015
opam sync local `opam` file with opam-repository version Sep 28, 2015
setup.ml release 0.19.3 Sep 28, 2015

README.md

Join the chat at https://gitter.im/mirage/ocaml-cohttp

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 (see the mirage-http repository).
  • 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.js - Jsoo (XHR) client
  • cohttp.lwt - Unix based lwt backend
  • cohttp.lwt-core - Lwt backend without unix specifics.
  • 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:

open Lwt
open Cohttp
open Cohttp_lwt_unix

let body =
  Client.get (Uri.of_string "http://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)

There's a few things to notice:

  • We open 2 modules. Cohttp contains the backend independent stuff and Cohttp_lwt_unix is the lwt + unix specific stuff.

  • Client.get accepts a Uri.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 of body which we then print.

Consult the following modules for reference:

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 information
  • Cohttp.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.

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)

The following modules are useful references:

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 "http://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'
Something went wrong with that request. Please try again.