package tiny_httpd

  1. Overview
  2. Docs
Minimal HTTP server using good old threads


Dune Dependency








Minimal HTTP server using good old threads, with stream abstractions, simple routing, URL encoding/decoding, static asset serving, and optional compression with camlzip. It also supports server-sent events (w3c)

Free from all forms of ppx, async monads, etc. 🙃

Note: it can be useful to add the jemalloc opam package for long running server, as it does a good job at controlling memory usage.

The basic echo server from src/examples/

module S = Tiny_httpd

let () =
  let server = S.create () in
  (* say hello *)
  S.add_route_handler ~meth:`GET server
    S.Route.(exact "hello" @/ string @/ return)
    (fun name _req -> S.Response.make_string (Ok ("hello " ^name ^"!\n")));
  (* echo request *)
  S.add_route_handler server
    S.Route.(exact "echo" @/ return)
    (fun req -> S.Response.make_string (Ok (Format.asprintf "echo:@ %a@." S.Request.pp req)));
  Printf.printf "listening on http://%s:%d\n%!" (S.addr server) (S.port server);
  match server with
  | Ok () -> ()
  | Error e -> raise e
$ dune exec src/examples/echo.exe &
listening on

# the path "hello/name" greets you.
$ curl -X GET http://localhost:8080/hello/quadrarotaphile
hello quadrarotaphile!

# the path "echo" just prints the request.
$ curl -X GET http://localhost:8080/echo --data "howdy y'all" 
 headers=Host: localhost:8080
         User-Agent: curl/7.66.0
         Accept: */*
         Content-Length: 10
         Content-Type: application/x-www-form-urlencoded;
 path="/echo"; body="howdy y'all"}


Similar to python -m http.server, a simple program http_of_dir is provided. It serves files from the current directory.

$ http_of_dir . -p 8080 &
$ curl -X GET http://localhost:8080
<html list of current dir>

Static assets and files

The program http_of_dir relies on the module Tiny_httpd_dir, which can serve directories, as well as virtual file systems.

In 'examples/dune', we produce an OCaml module using the program tiny-httpd-vfs-pack. This module contains a VFS (virtual file system) which can be served as if it were an actual directory.

The dune rule:

  (deps (source_tree files) (:out test_output.txt.expected))
  (enabled_if (= %{system} "linux"))
  (action (run ../src/bin/vfs_pack.exe -o %{targets}

The code to serve the VFS from is as follows:

  Tiny_httpd_dir.add_vfs server
    ~config:(Tiny_httpd_dir.config ~download:true
               ~dir_behavior:Tiny_httpd_dir.Index_or_lists ())
    ~vfs:Vfs.vfs ~prefix:"vfs";

it allows downloading the files, and listing directories. If a directory contains index.html then this will be served instead of listing the content.

Socket activation

Since version 0.10, socket activation is supported indirectly, by allowing a socket to be explicitly passed in to the create function:

module S = Tiny_httpd

let not_found _ _ = ~code:404 "Not Found\n"

let () =
  (* Module [Daemon] is from the [ocaml-systemd] package *)
  let server = match Daemon.listen_fds () with
    (* If no socket passed in, assume server was started explicitly i.e. without
       socket activation *)
    | [] -> S.create ()

    (* If a socket passed in e.g. by systemd, listen on that *)
    | sock :: _ -> S.create ~sock ()
  S.add_route_handler server S.Route.rest_of_path not_found;
  Printf.printf "Listening on http://%s:%d\n%!" (S.addr server) (S.port server);
  match server with
  | Ok () -> ()
  | Error e -> raise e

On Linux, this requires the ocaml-systemd package:

opam install ocaml-systemd

Tip: in the dune file, the package name should be systemd.

In case you're not familiar with socket activation, Lennart Poettering's blog post explains it well.


Why not? If you just want a super basic local server (perhaps for exposing data from a local demon, like Cups or Syncthing do), no need for a ton of dependencies or high scalability libraries.

Use cases might include:

  • serve content directly from a static blog generator;

  • provide a web UI to some tool (like CUPS and syncthing do);

  • implement a basic monitoring page for a service;

  • provide a simple json API for a service, on top of http;

  • use http_of_dir to serve odoc-generated docs or some assets directory.





Dependencies (5)

  1. ocaml >= "4.04.0"
  2. seq
  3. result
  4. base-threads
  5. dune >= "2.0"

Dev Dependencies (6)

  1. ptime with-test
  2. ounit2 with-test
  3. qcheck with-test & >= "0.9"
  4. conf-libcurl with-test
  5. qtest >= "2.9" & with-test
  6. odoc with-doc