Repository: ostera/httpkit
Branch: master
Commit: ecfe4297bb3d
Files: 47
Total size: 60.6 KB
Directory structure:
gitextract_lfbuuy6e/
├── .gitattributes
├── .gitignore
├── 3rdparty/
│ ├── dune-project
│ ├── httpaf-lwt-unix/
│ │ ├── buffer.ml
│ │ ├── buffer.mli
│ │ ├── dune
│ │ ├── httpaf_lwt_unix.ml
│ │ ├── httpaf_lwt_unix.mli
│ │ ├── ssl_io.ml
│ │ └── tls_io.ml
│ └── httpaf-lwt-unix.opam
├── README.md
├── bench/
│ ├── .gitignore
│ ├── Gemfile
│ ├── README.md
│ ├── echo.go
│ ├── echo.js
│ ├── echo.py
│ └── echo.rb
├── dune-project
├── esy.json
├── examples/
│ ├── dune
│ ├── echo_server_http.re
│ ├── echo_server_http2.re
│ ├── request_http.re
│ └── request_http2.re
├── httpkit-lwt-unix-h2.opam
├── httpkit-lwt-unix-httpaf.opam
├── httpkit.opam
├── lwt-unix-h2/
│ ├── client_http.re
│ ├── client_https.re
│ ├── client_request.re
│ ├── client_response.re
│ ├── dune
│ ├── httpkit_lwt_unix_h2.re
│ ├── server_http.re
│ └── server_request.re
├── lwt-unix-httpaf/
│ ├── client_http.re
│ ├── client_https.re
│ ├── client_request.re
│ ├── client_response.re
│ ├── dune
│ ├── httpkit_lwt_unix_httpaf.re
│ ├── server_http.re
│ └── server_request.re
└── src/
├── dune
└── httpkit.re
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
bench/* linguist-vendored
*.re linguist-language=Reason
*.rei linguist-language=Reason
*.ml linguist-language=OCaml
*.mli linguist-language=OCaml
================================================
FILE: .gitignore
================================================
_esy
_build
_public
.merlin
/lua
/lua_modules
*.install
================================================
FILE: 3rdparty/dune-project
================================================
(lang dune 1.9)
(name httpaf-lwt-unix)
================================================
FILE: 3rdparty/httpaf-lwt-unix/buffer.ml
================================================
open Lwt.Infix
(* Based on the Buffer module in httpaf_async.ml. *)
type t =
{ buffer : Lwt_bytes.t
; mutable off : int
; mutable len : int }
let create size =
let buffer = Lwt_bytes.create size in
{ buffer; off = 0; len = 0 }
let compress t =
if t.len = 0
then begin
t.off <- 0;
t.len <- 0;
end else if t.off > 0
then begin
Lwt_bytes.blit t.buffer t.off t.buffer 0 t.len;
t.off <- 0;
end
let get t ~f =
let n = f t.buffer ~off:t.off ~len:t.len in
t.off <- t.off + n;
t.len <- t.len - n;
if t.len = 0
then t.off <- 0;
n
let put t ~f =
compress t;
f t.buffer ~off:(t.off + t.len) ~len:(Lwt_bytes.length t.buffer - t.len)
>>= fun n ->
t.len <- t.len + n;
Lwt.return n
================================================
FILE: 3rdparty/httpaf-lwt-unix/buffer.mli
================================================
type t
val create : int -> t
val get : t -> f:(Lwt_bytes.t -> off:int -> len:int -> int) -> int
val put : t -> f:(Lwt_bytes.t -> off:int -> len:int -> int Lwt.t) -> int Lwt.t
================================================
FILE: 3rdparty/httpaf-lwt-unix/dune
================================================
(library
(name httpaf_lwt_unix)
(public_name httpaf-lwt-unix)
(libraries faraday-lwt-unix httpaf lwt.unix ssl lwt_ssl)
(modules buffer httpaf_lwt_unix tls_io ssl_io)
(flags (:standard -safe-string)))
================================================
FILE: 3rdparty/httpaf-lwt-unix/httpaf_lwt_unix.ml
================================================
(*----------------------------------------------------------------------------
Copyright (c) 2018 Inhabited Type LLC.
Copyright (c) 2018 Anton Bachin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the author nor the names of his contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------*)
open Lwt.Infix
let read fd buffer =
Lwt.catch
(fun () ->
Buffer.put buffer ~f:(fun bigstring ~off ~len ->
Lwt_bytes.read fd bigstring off len))
(function
| Unix.Unix_error (Unix.EBADF, _, _) as exn ->
Lwt.fail exn
| exn ->
Lwt.async (fun () ->
Lwt_unix.close fd);
Lwt.fail exn)
>>= fun bytes_read ->
if bytes_read = 0 then
Lwt.return `Eof
else
Lwt.return (`Ok bytes_read)
let shutdown socket command =
try Lwt_unix.shutdown socket command
with Unix.Unix_error (Unix.ENOTCONN, _, _) -> ()
module Config = Httpaf.Config
module Server = struct
module Server_connection = Httpaf.Server_connection
let start_read_write_loops
?(readf=read)
?(writev=Faraday_lwt_unix.writev_of_fd)
~config
~socket
connection =
let read_buffer = Buffer.create config.Config.read_buffer_size in
let read_loop_exited, notify_read_loop_exited = Lwt.wait () in
let rec read_loop () =
let rec read_loop_step () =
match Server_connection.next_read_operation connection with
| `Read ->
readf socket read_buffer >>= begin function
| `Eof ->
Buffer.get read_buffer ~f:(fun bigstring ~off ~len ->
Server_connection.read_eof connection bigstring ~off ~len)
|> ignore;
read_loop_step ()
| `Ok _ ->
Buffer.get read_buffer ~f:(fun bigstring ~off ~len ->
Server_connection.read connection bigstring ~off ~len)
|> ignore;
read_loop_step ()
end
| `Yield ->
Server_connection.yield_reader connection read_loop;
Lwt.return_unit
| `Close ->
Lwt.wakeup_later notify_read_loop_exited ();
if not (Lwt_unix.state socket = Lwt_unix.Closed) then begin
shutdown socket Unix.SHUTDOWN_RECEIVE
end;
Lwt.return_unit
in
Lwt.async (fun () ->
Lwt.catch
read_loop_step
(fun exn ->
Server_connection.report_exn connection exn;
Lwt.return_unit))
in
let writev = writev socket in
let write_loop_exited, notify_write_loop_exited = Lwt.wait () in
let rec write_loop () =
let rec write_loop_step () =
match Server_connection.next_write_operation connection with
| `Write io_vectors ->
writev io_vectors >>= fun result ->
Server_connection.report_write_result connection result;
write_loop_step ()
| `Yield ->
Server_connection.yield_writer connection write_loop;
Lwt.return_unit
| `Close _ ->
Lwt.wakeup_later notify_write_loop_exited ();
if not (Lwt_unix.state socket = Lwt_unix.Closed) then begin
shutdown socket Unix.SHUTDOWN_SEND
end;
Lwt.return_unit
in
Lwt.async (fun () ->
Lwt.catch
write_loop_step
(fun exn ->
Server_connection.report_exn connection exn;
Lwt.return_unit))
in
read_loop ();
write_loop ();
Lwt.join [read_loop_exited; write_loop_exited] >>= fun () ->
if Lwt_unix.state socket <> Lwt_unix.Closed then
Lwt.catch
(fun () -> Lwt_unix.close socket)
(fun _exn -> Lwt.return_unit)
else
Lwt.return_unit
let create_connection_handler ?(config=Config.default) ~request_handler ~error_handler =
fun client_addr socket ->
let connection =
Server_connection.create
~config
~error_handler:(error_handler client_addr)
(request_handler client_addr)
in
start_read_write_loops ~config ~socket connection
module TLS = struct
let create_connection_handler
?server
?certfile
?keyfile
?(config=Config.default)
~request_handler
~error_handler =
let make_tls_server = Tls_io.make_server ?server ?certfile ?keyfile in
fun client_addr socket ->
let connection =
Server_connection.create
~config
~error_handler:(error_handler client_addr)
(request_handler client_addr)
in
make_tls_server socket >>= fun tls_server ->
let readf = Tls_io.readf tls_server in
let writev = Tls_io.writev tls_server in
start_read_write_loops ~config ~readf ~writev ~socket connection
>>= Lwt.return
end
module SSL = struct
let create_connection_handler
?server
?certfile
?keyfile
?(config=Config.default)
~request_handler
~error_handler =
let make_ssl_server = Ssl_io.make_server ?server ?certfile ?keyfile in
fun client_addr socket ->
let connection =
Server_connection.create
~config
~error_handler:(error_handler client_addr)
(request_handler client_addr)
in
make_ssl_server socket >>= fun tls_server ->
let readf = Ssl_io.readf tls_server in
let writev = Ssl_io.writev tls_server in
start_read_write_loops ~config ~readf ~writev ~socket connection
>>= Lwt.return
end
end
module Client = struct
module Client_connection = Httpaf.Client_connection
let start_read_write_loops
?(readf=read)
?(writev=Faraday_lwt_unix.writev_of_fd)
~config
~socket
connection =
let read_buffer = Buffer.create config.Config.read_buffer_size in
let read_loop_exited, notify_read_loop_exited = Lwt.wait () in
let read_loop () =
let rec read_loop_step () =
match Client_connection.next_read_operation connection with
| `Read ->
readf socket read_buffer >>= begin function
| `Eof ->
Buffer.get read_buffer ~f:(fun bigstring ~off ~len ->
Client_connection.read_eof connection bigstring ~off ~len)
|> ignore;
read_loop_step ()
| `Ok _ ->
Buffer.get read_buffer ~f:(fun bigstring ~off ~len ->
Client_connection.read connection bigstring ~off ~len)
|> ignore;
read_loop_step ()
end
| `Close ->
Lwt.wakeup_later notify_read_loop_exited ();
if not (Lwt_unix.state socket = Lwt_unix.Closed) then begin
shutdown socket Unix.SHUTDOWN_RECEIVE
end;
Lwt.return_unit
in
Lwt.async (fun () ->
Lwt.catch
read_loop_step
(fun exn ->
Client_connection.report_exn connection exn;
Lwt.return_unit))
in
let writev = writev socket in
let write_loop_exited, notify_write_loop_exited = Lwt.wait () in
let rec write_loop () =
let rec write_loop_step () =
match Client_connection.next_write_operation connection with
| `Write io_vectors ->
writev io_vectors >>= fun result ->
Client_connection.report_write_result connection result;
write_loop_step ()
| `Yield ->
Client_connection.yield_writer connection write_loop;
Lwt.return_unit
| `Close _ ->
Lwt.wakeup_later notify_write_loop_exited ();
Lwt.return_unit
in
Lwt.async (fun () ->
Lwt.catch
write_loop_step
(fun exn ->
Client_connection.report_exn connection exn;
Lwt.return_unit))
in
read_loop ();
write_loop ();
Lwt.async (fun () ->
Lwt.join [read_loop_exited; write_loop_exited] >>= fun () ->
if Lwt_unix.state socket <> Lwt_unix.Closed then
Lwt.catch
(fun () -> Lwt_unix.close socket)
(fun _exn -> Lwt.return_unit)
else
Lwt.return_unit)
let request ?(config=Config.default) socket request ~error_handler ~response_handler =
let request_body, connection =
Client_connection.request ~config request ~error_handler ~response_handler
in
start_read_write_loops ~config ~socket connection;
request_body
module TLS = struct
let request ?client ?(config=Config.default) socket request ~error_handler ~response_handler =
let request_body, connection =
Client_connection.request ~config request ~error_handler ~response_handler
in
Lwt.async(fun () ->
Tls_io.make_client ?client socket >|= fun tls_client ->
let readf = Tls_io.readf tls_client in
let writev = Tls_io.writev tls_client in
start_read_write_loops ~config ~readf ~writev ~socket connection);
request_body
end
module SSL = struct
let request ?client ?(config=Config.default) socket request ~error_handler ~response_handler =
let request_body, connection =
Client_connection.request ~config request ~error_handler ~response_handler
in
Lwt.async(fun () ->
Ssl_io.make_client ?client socket >|= fun tls_client ->
let readf = Ssl_io.readf tls_client in
let writev = Ssl_io.writev tls_client in
start_read_write_loops ~config ~readf ~writev ~socket connection);
request_body
end
end
================================================
FILE: 3rdparty/httpaf-lwt-unix/httpaf_lwt_unix.mli
================================================
open Httpaf
(* The function that results from [create_connection_handler] should be passed
to [Lwt_io.establish_server_with_client_socket]. For an example, see
[examples/lwt_echo_server.ml]. *)
module Server : sig
val create_connection_handler
: ?config : Config.t
-> request_handler : (Unix.sockaddr -> Server_connection.request_handler)
-> error_handler : (Unix.sockaddr -> Server_connection.error_handler)
-> Unix.sockaddr
-> Lwt_unix.file_descr
-> unit Lwt.t
module TLS : sig
val create_connection_handler
: ?server : Tls_io.server
-> ?certfile : string
-> ?keyfile : string
-> ?config : Config.t
-> request_handler : (Unix.sockaddr -> Server_connection.request_handler)
-> error_handler : (Unix.sockaddr -> Server_connection.error_handler)
-> Unix.sockaddr
-> Lwt_unix.file_descr
-> unit Lwt.t
end
module SSL : sig
val create_connection_handler
: ?server : Ssl_io.server
-> ?certfile : string
-> ?keyfile : string
-> ?config : Config.t
-> request_handler : (Unix.sockaddr -> Server_connection.request_handler)
-> error_handler : (Unix.sockaddr -> Server_connection.error_handler)
-> Unix.sockaddr
-> Lwt_unix.file_descr
-> unit Lwt.t
end
end
(* For an example, see [examples/lwt_get.ml]. *)
module Client : sig
val request
: ?config : Config.t
-> Lwt_unix.file_descr
-> Request.t
-> error_handler : Client_connection.error_handler
-> response_handler : Client_connection.response_handler
-> [`write] Body.t
module TLS : sig
val request
: ?client : Tls_io.client
-> ?config : Config.t
-> Lwt_unix.file_descr
-> Request.t
-> error_handler : Client_connection.error_handler
-> response_handler : Client_connection.response_handler
-> [`write] Body.t
end
module SSL : sig
val request
: ?client : Ssl_io.client
-> ?config : Config.t
-> Lwt_unix.file_descr
-> Request.t
-> error_handler : Client_connection.error_handler
-> response_handler : Client_connection.response_handler
-> [`write] Body.t
end
end
================================================
FILE: 3rdparty/httpaf-lwt-unix/ssl_io.ml
================================================
open Lwt.Infix
let () = Ssl.init ()
let readf socket =
fun _fd buffer ->
Lwt.catch
(fun () ->
Buffer.put buffer ~f:(fun bigstring ~off ~len ->
Lwt_unix.blocking (Lwt_ssl.get_fd socket) >>= fun _ ->
Lwt_ssl.read_bytes socket bigstring off len))
(function
| Unix.Unix_error (Unix.EBADF, _, _) as exn ->
Lwt.fail exn
| exn ->
Lwt.async (fun () ->
Lwt_ssl.ssl_shutdown socket >>= fun () ->
Lwt_ssl.close socket);
Lwt.fail exn)
>>= fun bytes_read ->
if bytes_read = 0 then
Lwt.return `Eof
else
Lwt.return (`Ok bytes_read)
let writev socket _fd =
fun iovecs ->
Lwt.catch
(fun () ->
Lwt_list.fold_left_s (fun acc {Faraday.buffer; off; len} ->
Lwt_ssl.write_bytes socket buffer off len
>|= fun written -> acc + written) 0 iovecs
>|= fun n -> `Ok n)
(function
| Unix.Unix_error (Unix.EBADF, "check_descriptor", _) ->
Lwt.return `Closed
| exn ->
Lwt.fail exn)
type client = Lwt_ssl.socket
type server = Lwt_ssl.socket
let make_client ?client socket =
match client with
| Some client -> Lwt.return client
| None ->
let client_ctx = Ssl.create_context Ssl.SSLv23 Ssl.Client_context in
Ssl.disable_protocols client_ctx [Ssl.SSLv23];
Ssl.honor_cipher_order client_ctx;
Lwt_ssl.ssl_connect socket client_ctx
let make_server ?server ?certfile ?keyfile socket
=
match server, certfile, keyfile with
| Some server, _, _ -> Lwt.return server
| None, Some cert, Some priv_key ->
let server_ctx = Ssl.create_context Ssl.SSLv23 Ssl.Server_context in
Ssl.disable_protocols server_ctx [Ssl.SSLv23];
Ssl.use_certificate server_ctx cert priv_key;
Lwt_ssl.ssl_accept socket server_ctx
| _ ->
Lwt.fail (Invalid_argument "Certfile and Keyfile required when server isn't provided")
================================================
FILE: 3rdparty/httpaf-lwt-unix/tls_io.ml
================================================
let readf _tls =
fun _fd _buffer ->
Lwt.fail_with "Tls not available"
let writev _tls _fd =
fun _iovecs ->
Lwt.fail_with "Tls not available"
type client = [ `Tls_not_available ]
type server = [ `Tls_not_available ]
let[@ocaml.warning "-21"] make_client ?client:_ =
failwith "TLS not available";
fun _socket -> Lwt.return `Tls_not_available
let[@ocaml.warning "-21"] make_server ?server:_ ?certfile:_ ?keyfile:_ =
failwith "TLS not available";
fun _socket -> Lwt.fail_with "TLS not available"
================================================
FILE: 3rdparty/httpaf-lwt-unix.opam
================================================
================================================
FILE: README.md
================================================
# ⚡️HttpKit — high-level, high-performance HTTP1.1/2 clients/servers in Reason
> NOTE: under heavy reconstruction. Latest stable version was [`660d1c8`](https://github.com/ostera/httpkit/tree/660d1c8b7438d207be2717495d8590a529bf5a1f)
HttpKit is a high-level library for building and consuming web servers over
HTTP, HTTPS, and HTTP2.
It serves as a thin layer over `h2` and `http/af`, and when it can it allows you
to seamlessly transition from one to the other.
0. [Roadmap](#roadmap)
1. [Getting Started](#getting-started)
1. [Running the Examples](#running-the-examples)
## Roadmap
| Feature | HTTP/1.1 | HTTPS/1.1 | HTTP/2 | HTTPS/2 |
|------------------|----------|-----------|--------|---------|
| Listen as Server | Yes | No | Yes | No |
| Send Request | Yes | Yes | No | No |
| Server Push | - | - | No | No |
| | | | | |
## Getting Started
#### Usage
`httpkit` can be used both to build servers and to make requests as a client.
Documentation is still a work-in-progress, but there's examples in the
`examples` section that can give you a better idea of how to use the libraries.
In short:
For making a request:
```reason
open Lwt_result.Infix;
module Httpkit = Httpkit_lwt_unix_httpaf;
let req =
Httpkit.Request.create(
~headers=[("User-Agent", "Reason HttpKit")],
`GET,
Uri.of_string("http://api.github.com/repos/ostera/httpkit"),
);
/* Send over HTTP */
req
|> Httpkit.Client.Http.send
>>= Httpkit.Client.Response.body
|> Lwt_main.run
/* Send over HTTPS */
req
|> Httpkit.Client.Https.send
>>= Httpkit.Client.Response.body
|> Lwt_main.run
```
For making a server:
```reason
module Httpkit = Httpkit_lwt_unix_httpaf;
let port = 8080;
let on_start = (~hoststring) =>
Logs.app(m => m("Running on %s", hoststring));
let handler: Httpkit.Server.handler =
(req, reply, kill_server) => {
let method = req |> Httpkit.Request.meth |> H2.Method.to_string;
let path = req |> Httpkit.Request.path;
Logs.app(m => m("%s %s", method, path));
reply(200, "hi");
kill_server();
};
/* Start server over HTTP */
Httpkit.Server.Http.listen(~port, ~address=`Any, ~handler, ~on_start)
|> Lwt_main.run;
/* Start server over HTTPS */
Httpkit.Server.Http.listen(~port, ~address=`Any, ~handler, ~on_start)
|> Lwt_main.run;
```
#### Installing with esy
You can install by dropping the following dependencies in your `package.json`:
```json
{
"dependencies": {
"@opam/httpkit": "*",
"@opam/httpkit-lwt-unix-httpaf": "*",
"@opam/logs": "*",
"@opam/fmt": "*",
// ...
},
"resolutions": {
"@opam/httpkit": "ostera/httpkit:httpkit.opam#f738417",
"@opam/httpkit-lwt-unix-httpaf": "ostera/httpkit:httpkit-lwt-unix-httpaf.opam#f738417",
}
}
```
> NOTE: For `httpkit` make sure you're using the latest commit hash!
## Running the Examples
All of the examples are runnable as binaries after compilation, so you can
either run `esy build` and find them within
`./_esy/default/build/default/examples/*.exe` or you can ask dune to run them
for you:
```sh
ostera/httpkit λ esy dune exec ./examples/Request.exe
```
================================================
FILE: bench/.gitignore
================================================
echo
================================================
FILE: bench/Gemfile
================================================
source "https://rubygems.org"
gem "rack"
================================================
FILE: bench/README.md
================================================
# Benchmarking HttpKit ⚡️
In order to ensure that `httpkit` stays fast, here you'll find a number of small
servers written in other languages to benchmark against.
They should all do the same thing:
1. Log down the time, method and path
2. Reply with the path
They will all be called with the same command:
```sh
wrk2 \
--threads=12 \
--connections=400 \
--duration=30s \
--rate 30K
https://localhost:8080/bench-it-chewie!
```
## Results
| Lang | Lib | KB/s | RPS | Total Req |
|---------|---------------------|----------|-------|-----------|
| OCaml | httpkit+http/af+lwt | 414.17KB | 10874 | 326230 |
| OCaml | http/af+lwt | 414.76KB | 10890 | 326792 |
| Ruby | rack | 149.70KB | 806 | 24349 |
| Node.js | stdlib http | 591.80KB | 5179 | 155767 |
| Golang | stdlib http | 0.95MB | 13314 | 399390 |
| Python | BaseHTTPServer | 65.50KB | 519 | 15704 |
## Details
### OCaml/httpkit+httpaf+lwt
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:9999/what
Running 30s test @ http://localhost:9999/what
12 threads and 400 connections
Thread calibration: mean lat.: 3092.528ms, rate sampling interval: 11714ms
Thread calibration: mean lat.: 3241.606ms, rate sampling interval: 12107ms
Thread calibration: mean lat.: 3183.128ms, rate sampling interval: 11763ms
Thread calibration: mean lat.: 3235.211ms, rate sampling interval: 12009ms
Thread calibration: mean lat.: 3109.703ms, rate sampling interval: 11780ms
Thread calibration: mean lat.: 3214.701ms, rate sampling interval: 12017ms
Thread calibration: mean lat.: 3174.243ms, rate sampling interval: 11862ms
Thread calibration: mean lat.: 3067.300ms, rate sampling interval: 11575ms
Thread calibration: mean lat.: 3066.558ms, rate sampling interval: 11755ms
Thread calibration: mean lat.: 3101.464ms, rate sampling interval: 11722ms
Thread calibration: mean lat.: 3084.410ms, rate sampling interval: 11829ms
Thread calibration: mean lat.: 3075.844ms, rate sampling interval: 11444ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 12.87s 3.62s 20.76s 58.95%
Req/Sec 0.92k 18.58 0.95k 58.33%
326230 requests in 30.00s, 12.13MB read
Socket errors: connect 0, read 40, write 3, timeout 4
Requests/sec: 10874.67
Transfer/sec: 414.17KB
```
### OCaml/httpaf+lwt
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:9999/what
Running 30s test @ http://localhost:9999/what
12 threads and 400 connections
Thread calibration: mean lat.: 3103.933ms, rate sampling interval: 11304ms
Thread calibration: mean lat.: 3074.874ms, rate sampling interval: 11141ms
Thread calibration: mean lat.: 3190.094ms, rate sampling interval: 11370ms
Thread calibration: mean lat.: 2974.283ms, rate sampling interval: 10543ms
Thread calibration: mean lat.: 2987.974ms, rate sampling interval: 11091ms
Thread calibration: mean lat.: 2895.381ms, rate sampling interval: 10919ms
Thread calibration: mean lat.: 2908.596ms, rate sampling interval: 10870ms
Thread calibration: mean lat.: 3164.646ms, rate sampling interval: 11526ms
Thread calibration: mean lat.: 3000.368ms, rate sampling interval: 11182ms
Thread calibration: mean lat.: 3051.481ms, rate sampling interval: 11386ms
Thread calibration: mean lat.: 3005.150ms, rate sampling interval: 11165ms
Thread calibration: mean lat.: 2956.432ms, rate sampling interval: 10960ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 12.55s 3.75s 21.20s 59.01%
Req/Sec 0.91k 15.95 0.95k 75.00%
326792 requests in 30.01s, 12.15MB read
Socket errors: connect 0, read 97, write 4, timeout 11
Requests/sec: 10890.16
Transfer/sec: 414.76KB
```
### Ruby/Rack
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:8080/bench-it-chewie!
Running 30s test @ http://localhost:8080/bench-it-chewie!
12 threads and 400 connections
Thread calibration: mean lat.: 4611.970ms, rate sampling interval: 16515ms
Thread calibration: mean lat.: 4602.015ms, rate sampling interval: 16154ms
Thread calibration: mean lat.: 4654.559ms, rate sampling interval: 16613ms
Thread calibration: mean lat.: 4516.088ms, rate sampling interval: 16179ms
Thread calibration: mean lat.: 4601.379ms, rate sampling interval: 16269ms
Thread calibration: mean lat.: 4900.919ms, rate sampling interval: 16449ms
Thread calibration: mean lat.: 4552.994ms, rate sampling interval: 16523ms
Thread calibration: mean lat.: 9223372036854776.000ms, rate sampling interval: 10ms
Thread calibration: mean lat.: 9223372036854776.000ms, rate sampling interval: 10ms
Thread calibration: mean lat.: 9223372036854776.000ms, rate sampling interval: 10ms
Thread calibration: mean lat.: 9223372036854776.000ms, rate sampling interval: 10ms
Thread calibration: mean lat.: 4669.880ms, rate sampling interval: 16556ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 17.89s 5.12s 27.25s 57.47%
Req/Sec 0.11 3.31 108.00 99.89%
24349 requests in 30.18s, 4.41MB read
Socket errors: connect 0, read 0, write 0, timeout 4276
Requests/sec: 806.78
Transfer/sec: 149.70KB
```
### Node/Http
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:8080/bench-it-chewie!
Running 30s test @ http://localhost:8080/bench-it-chewie!
12 threads and 400 connections
Thread calibration: mean lat.: 3976.372ms, rate sampling interval: 13983ms
Thread calibration: mean lat.: 4418.043ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 4419.606ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 4413.743ms, rate sampling interval: 15409ms
Thread calibration: mean lat.: 4421.132ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 4418.656ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 4422.277ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 4420.156ms, rate sampling interval: 15409ms
Thread calibration: mean lat.: 4420.646ms, rate sampling interval: 15409ms
Thread calibration: mean lat.: 3972.669ms, rate sampling interval: 13885ms
Thread calibration: mean lat.: 4421.550ms, rate sampling interval: 15417ms
Thread calibration: mean lat.: 3966.959ms, rate sampling interval: 13877ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 16.14s 4.73s 24.72s 56.45%
Req/Sec 456.33 0.75 458.00 100.00%
155767 requests in 30.07s, 17.38MB read
Socket errors: connect 0, read 1, write 0, timeout 0
Requests/sec: 5179.50
Transfer/sec: 591.80KB
```
### Golang/Http
The golang standard library `http` module seems flexible enough for us to build
an incredibly fast echo server! Closer to 3 times faster than node's, and around
2000 requests per second more than `httpkit`.
If the superior type-safety offered by `httpkit` is not what you're looking for,
have a look at this:
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:8080/bench-it-chewie!
Running 30s test @ http://localhost:8080/bench-it-chewie!
12 threads and 400 connections
Thread calibration: mean lat.: 2694.872ms, rate sampling interval: 9592ms
Thread calibration: mean lat.: 2674.729ms, rate sampling interval: 9551ms
Thread calibration: mean lat.: 2644.030ms, rate sampling interval: 9437ms
Thread calibration: mean lat.: 2680.816ms, rate sampling interval: 9568ms
Thread calibration: mean lat.: 2667.644ms, rate sampling interval: 9502ms
Thread calibration: mean lat.: 2691.502ms, rate sampling interval: 9560ms
Thread calibration: mean lat.: 2490.139ms, rate sampling interval: 8781ms
Thread calibration: mean lat.: 2652.057ms, rate sampling interval: 9461ms
Thread calibration: mean lat.: 2665.217ms, rate sampling interval: 9519ms
Thread calibration: mean lat.: 2687.348ms, rate sampling interval: 9584ms
Thread calibration: mean lat.: 2697.834ms, rate sampling interval: 9560ms
Thread calibration: mean lat.: 2684.353ms, rate sampling interval: 9519ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 11.02s 3.20s 17.10s 58.01%
Req/Sec 1.09k 23.03 1.12k 62.50%
399390 requests in 30.00s, 28.57MB read
Requests/sec: 13314.83
Transfer/sec: 0.95MB
```
### Python/BaseHTTPServer
```sh
ostera/httpkit λ wrk2 --threads=12 --connections=400 --duration=30s --rate 30K http://localhost:8080/bench-it-chewie!
Running 30s test @ http://localhost:8080/bench-it-chewie!
12 threads and 400 connections
Thread calibration: mean lat.: 4190.046ms, rate sampling interval: 14639ms
Thread calibration: mean lat.: 4269.527ms, rate sampling interval: 14483ms
Thread calibration: mean lat.: 4213.688ms, rate sampling interval: 15704ms
Thread calibration: mean lat.: 3632.081ms, rate sampling interval: 14434ms
Thread calibration: mean lat.: 3932.717ms, rate sampling interval: 13344ms
Thread calibration: mean lat.: 4980.591ms, rate sampling interval: 15450ms
Thread calibration: mean lat.: 3027.887ms, rate sampling interval: 12361ms
Thread calibration: mean lat.: 4010.051ms, rate sampling interval: 12828ms
Thread calibration: mean lat.: 4807.472ms, rate sampling interval: 18104ms
Thread calibration: mean lat.: 4543.476ms, rate sampling interval: 14524ms
Thread calibration: mean lat.: 4214.574ms, rate sampling interval: 14008ms
Thread calibration: mean lat.: 4466.553ms, rate sampling interval: 16506ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 10.99s 2.92s 20.97s 63.93%
Req/Sec 6.50 1.89 10.00 91.67%
15704 requests in 30.20s, 1.93MB read
Socket errors: connect 0, read 1533, write 57, timeout 4489
Requests/sec: 519.94
Transfer/sec: 65.50KB
```
### Lua/http
### Rust/hyper+tokio
================================================
FILE: bench/echo.go
================================================
package main
import (
"bytes"
"log"
"net/http"
)
const DefaultPort = "8080"
func EchoHandler(writer http.ResponseWriter, request *http.Request) {
stamp := request.Method + " " + request.URL.Path
log.Println(stamp)
buf := bytes.NewBufferString(request.URL.Path)
request.Write(buf)
}
func main() {
log.Println("Listening on port " + DefaultPort)
http.HandleFunc("/", EchoHandler)
http.ListenAndServe(":"+DefaultPort, nil)
}
================================================
FILE: bench/echo.js
================================================
const httpServer = require('http');
httpServer.createServer((req, res) => {
console.log(new Date(), req.method, req.url);
res.end(req.url);
})
.listen(8080, () => console.log("Server started..."));;
================================================
FILE: bench/echo.py
================================================
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
PORT_NUMBER = 8080
class handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Length',len(self.path))
self.end_headers()
self.wfile.write(self.path)
return
server = HTTPServer(('', PORT_NUMBER), handler)
print 'Listening on port ' , PORT_NUMBER
server.serve_forever()
================================================
FILE: bench/echo.rb
================================================
require 'rack'
app = Proc.new do |env|
req = Rack::Request.new(env)
meth = req.request_method
path = req.path
now = Time.now
puts "#{now} - #{meth} #{path}"
['200', {'Content-Type' => 'text/html'}, [path]]
end
Rack::Handler::WEBrick.run app
================================================
FILE: dune-project
================================================
(lang dune 1.5)
(name httpkit)
(using fmt 1.0)
================================================
FILE: esy.json
================================================
{
"dependencies": {
"@esy-ocaml/reason": "3.4.0",
"@opam/dune": "*",
"@opam/fmt": "*",
"@opam/h2": "0.2.0",
"@opam/h2-lwt": "0.2.0",
"@opam/h2-lwt-unix": "0.2.0",
"@opam/httpaf": "*",
"@opam/logs": "*",
"@opam/lwt": "4.2.1",
"@opam/lwt_ssl": "1.1.2",
"@opam/merlin": "*",
"@opam/odoc": "*",
"@opam/uri": "*",
"@opam/yojson": "*",
"ocaml": "~4.7.0",
"refmterr": "*"
},
"resolutions": {
"@opam/conf-libev": "esy-packages/libev:package.json#86d244e",
"@opam/conf-autoconf": "esy-packages/esy-autoconf:package.json#71a8836",
"@opam/conf-openssl": {
"source": "no-source:",
"override": {
"dependencies": {
"@opam/conf-pkg-config": "*",
"@esy-packages/esy-openssl": "esy-packages/esy-openssl#f6107d6",
"@opam/conf-autoconf": "*"
}
}
},
"@opam/httpaf": "anmonteiro/httpaf:httpaf.opam#6d2c80e3a16ecf85d74df76005ab68136457e111",
"@opam/ssl": "anmonteiro/ocaml-ssl:ssl.opam#b965d15"
}
}
================================================
FILE: examples/dune
================================================
(executable
(name request_http)
(modules request_http)
(ocamlopt_flags -O3)
(libraries httpkit-lwt-unix-httpaf httpkit logs.fmt fmt.tty))
(executable
(name request_http2)
(modules request_http2)
(ocamlopt_flags -O3)
(libraries httpkit-lwt-unix-h2 httpkit logs.fmt fmt.tty))
(executable
(name echo_server_http)
(modules echo_server_http)
(ocamlopt_flags -O3)
(libraries httpkit-lwt-unix-httpaf httpkit logs.fmt fmt.tty))
(executable
(name echo_server_http2)
(modules echo_server_http2)
(ocamlopt_flags -O3)
(libraries httpkit-lwt-unix-h2 httpkit logs.fmt fmt.tty))
================================================
FILE: examples/echo_server_http.re
================================================
/** Handle sigpipe internally */
Sys.(set_signal(sigpipe, Signal_ignore));
/** Setup loggers */
Fmt_tty.setup_std_outputs();
Logs.set_level(Some(Logs.Debug));
Logs.set_reporter(Logs_fmt.reporter());
module Httpkit = Httpkit_lwt_unix_httpaf;
let port = 8080;
let on_start = (~hoststring) =>
Logs.app(m => m("Running on %s", hoststring));
let handler: Httpkit.Server.handler =
(req, reply, close) => {
let method = req |> Httpkit.Request.meth |> H2.Method.to_string;
let path = req |> Httpkit.Request.path;
Logs.app(m => m("%s %s", method, path));
reply(200, "hi");
close();
};
Httpkit.Server.Http.listen(~port, ~address=`Any, ~handler, ~on_start)
|> Lwt_main.run;
================================================
FILE: examples/echo_server_http2.re
================================================
/** Handle sigpipe internally */
Sys.(set_signal(sigpipe, Signal_ignore));
/** Setup loggers */
Fmt_tty.setup_std_outputs();
Logs.set_level(Some(Logs.Debug));
Logs.set_reporter(Logs_fmt.reporter());
module Httpkit = Httpkit_lwt_unix_h2;
let port = 8080;
let on_start = (~hoststring) =>
Logs.app(m => m("Running on %s", hoststring));
let handler: Httpkit.Server.handler =
(req, reply, close) => {
let method = req |> Httpkit.Request.meth |> H2.Method.to_string;
let path = req |> Httpkit.Request.path;
Logs.app(m => m("%s %s", method, path));
reply(200, "hi");
close();
};
Httpkit.Server.Http.listen(~port, ~address=`Any, ~handler, ~on_start)
|> Lwt_main.run;
================================================
FILE: examples/request_http.re
================================================
open Lwt_result.Infix;
/** Handle sigpipe internally */
Sys.(set_signal(sigpipe, Signal_ignore));
/** Setup loggers */
Fmt_tty.setup_std_outputs();
Logs.set_level(Some(Logs.Debug));
Logs.set_reporter(Logs_fmt.reporter());
module Httpkit = Httpkit_lwt_unix_httpaf;
/**
Sample HTTPS Request using No Authentication :tm:
*/
let https_url = Sys.argv[1];
Logs.app(m => m("Requesting: %s", https_url));
switch (
Httpkit.Request.create(
~headers=[
("User-Agent", "Reason HttpKit"),
("Accept", "*/*"),
],
`GET,
https_url |> Uri.of_string,
)
|> Httpkit.Client.Https.send
>>= Httpkit.Client.Response.body
|> Lwt_main.run
) {
| exception e => Logs.err(m => m("%s", Printexc.to_string(e)))
| Ok(body) => Logs.app(m => m("Response: %s", body))
| Error(`Connection_error(`Invalid_response_body_length(req))) =>
let str = Buffer.create(1024);
let fmt = Format.formatter_of_buffer(str);
Httpaf.Response.pp_hum(fmt, req);
Logs.err(m =>
m(
"Connection Error (Invalid response body length): %s",
str |> Buffer.to_bytes |> Bytes.to_string,
)
);
| Error(`Connection_error(`Malformed_response(str))) =>
Logs.err(m => m("Connection Error (Malformed response): %s", str))
| Error(`Connection_error(`Exn(ex))) =>
Logs.err(m =>
m("Connection Error (Exception): %s", ex |> Printexc.to_string)
)
};
================================================
FILE: examples/request_http2.re
================================================
open Lwt_result.Infix;
/** Handle sigpipe internally */
Sys.(set_signal(sigpipe, Signal_ignore));
/** Setup loggers */
Fmt_tty.setup_std_outputs();
Logs.set_level(Some(Logs.Debug));
Logs.set_reporter(Logs_fmt.reporter());
module Httpkit = Httpkit_lwt_unix_h2;
/**
Sample HTTPS Request using No Authentication :tm:
*/
let https_url = Sys.argv[1];
Logs.app(m => m("Requesting: %s", https_url));
switch (
Httpkit.Request.create(`GET, https_url |> Uri.of_string)
|> Httpkit.Client.Https.send
>>= Httpkit.Client.Response.body
|> Lwt_main.run
) {
| exception e => Logs.err(m => m("%s", Printexc.to_string(e)))
| Ok(body) => Logs.app(m => m("Response: %s", body))
| Error(`Connection_error(`Invalid_response_body_length(req))) =>
let str = Buffer.create(1024);
let fmt = Format.formatter_of_buffer(str);
H2.Response.pp_hum(fmt, req);
Logs.err(m =>
m(
"Connection Error (Invalid response body length): %s",
str |> Buffer.to_bytes |> Bytes.to_string,
)
);
| Error(`Connection_error(`Malformed_response(str))) =>
Logs.err(m => m("Connection Error (Malformed response): %s", str))
| Error(`Connection_error(`Exn(ex))) =>
Logs.err(m =>
m("Connection Error (Exception): %s", ex |> Printexc.to_string)
)
| Error(`Connection_error(`Protocol_error)) =>
Logs.err(m => m("Connection Error (Protocol Error)"))
};
================================================
FILE: httpkit-lwt-unix-h2.opam
================================================
opam-version: "2.0"
name: "httpkit-lwt-unix-h2"
version: "0.13"
synopsis: "High-level, High-performance HTTP(S) Clients/Servers with Lwt"
maintainer: "Leandro Ostera <leandro@ostera.io>"
authors: "Leandro Ostera <leandro@ostera.io>"
license: "MIT"
homepage: "https//github.com/ostera/httpkit"
bug-reports: "https//github.com/ostera/httpkit/issues"
depends: [
"httpkit"
"h2"
"h2-lwt"
"h2-lwt-unix"
"lwt"
"lwt_ssl"
"ssl"
"tls"
"dune" {build}
"reason" {build}
]
build: ["dune" "build" "-p" name]
install: ["dune" "install" name "--prefix" prefix "--root" "."]
================================================
FILE: httpkit-lwt-unix-httpaf.opam
================================================
opam-version: "2.0"
name: "httpkit-lwt-unix-httpaf"
version: "0.13"
synopsis: "High-level, High-performance HTTP(S) Clients/Servers with Lwt"
maintainer: "Leandro Ostera <leandro@ostera.io>"
authors: "Leandro Ostera <leandro@ostera.io>"
license: "MIT"
homepage: "https//github.com/ostera/httpkit"
bug-reports: "https//github.com/ostera/httpkit/issues"
depends: [
"httpkit"
"httpaf"
"httpaf-lwt-unix"
"lwt"
"ssl"
"tls"
"dune" {build}
"reason" {build}
]
build: ["dune" "build" "-p" name]
install: ["dune" "install" name "--prefix" prefix "--root" "."]
================================================
FILE: httpkit.opam
================================================
opam-version: "2.0"
name: "httpkit"
version: "0.13"
synopsis: "High-level, High-performance HTTP(S) Clients/Servers"
maintainer: "Leandro Ostera <leandro@ostera.io>"
authors: "Leandro Ostera <leandro@ostera.io>"
license: "MIT"
homepage: "https//github.com/ostera/httpkit"
bug-reports: "https//github.com/ostera/httpkit/issues"
depends: [
"logs"
"uri"
"httpaf"
"h2"
"dune" {build}
"reason" {build}
]
build: ["dune" "build" "-p" name]
install: ["dune" "install" name "--prefix" prefix "--root" "."]
================================================
FILE: lwt-unix-h2/client_http.re
================================================
open Lwt.Infix;
let send:
(~config: H2.Config.t=?, Httpkit.Request.t) =>
Lwt_result.t(
(H2.Response.t, H2.Body.t([ | `read])),
[> | `Connection_error(H2.Client_connection.error)],
) =
(~config=H2.Config.default, req) => {
let uri = Httpkit.Request.uri(req);
let response_handler = (notify_response_received, response, response_body) => {
Logs.debug(m => m("Handling response..."));
Lwt.wakeup_later(
notify_response_received,
(response, response_body) |> Lwt_result.return,
);
};
let error_handler = (notify_response_received, error) => {
Logs.debug(m => m("Handling errors..."));
Lwt.wakeup_later(
notify_response_received,
`Connection_error(error) |> Lwt_result.fail,
);
};
let host = Uri.host_with_default(uri);
let port =
switch (Uri.port(uri)) {
| None => "443"
| Some(number) => string_of_int(number)
};
Lwt_unix.getaddrinfo(host, port, [Unix.(AI_FAMILY(PF_INET))])
>>= (
addresses => {
Logs.debug(m => m("Got address..."));
let socket_addr = List.hd(addresses).Unix.ai_addr;
let socket = Lwt_unix.socket(Unix.PF_INET, Unix.SOCK_STREAM, 0);
Lwt_unix.connect(socket, socket_addr)
>>= (
() => {
Logs.debug(m => m("Opened socket..."));
let (response_received, notify_response_received) = Lwt.wait();
let response_handler = response_handler(notify_response_received);
let error_handler = error_handler(notify_response_received);
let write_body = request_body => {
Logs.debug(m => m("Writing body..."));
switch (Httpkit.Request.body(req)) {
| None => ()
| Some(str) => H2.Body.write_string(request_body, str)
};
H2.Body.close_writer(request_body);
Logs.debug(m => m("Closed writer..."));
response_received >>= (x => x);
};
let request = Client_request.of_httpkit_request(req);
H2_lwt_unix.Client.create_connection(
~config,
~error_handler,
socket,
)
>>= (
connection => {
H2_lwt_unix.Client.request(
connection,
request,
~error_handler,
~response_handler,
)
|> write_body;
}
);
}
);
}
);
};
================================================
FILE: lwt-unix-h2/client_https.re
================================================
open Lwt.Infix;
Ssl_threads.init();
Ssl.init();
let default_ssl_context = Ssl.create_context(Ssl.SSLv23, Ssl.Client_context);
Ssl.disable_protocols(default_ssl_context, [Ssl.SSLv23]);
Ssl.set_context_alpn_protos(default_ssl_context, ["h2"]);
Ssl.honor_cipher_order(default_ssl_context);
let send:
(~config: H2.Config.t=?, Httpkit.Request.t) =>
Lwt_result.t(
(H2.Response.t, H2.Body.t([ | `read])),
[> | `Connection_error(H2.Client_connection.error)],
) =
(~config=H2.Config.default, req) => {
let uri = Httpkit.Request.uri(req);
let response_handler = (notify_response_received, response, response_body) => {
Logs.debug(m => m("Handling response..."));
Lwt.wakeup_later(
notify_response_received,
(response, response_body) |> Lwt_result.return,
);
};
let error_handler = (notify_response_received, error) => {
Logs.debug(m => m("Handling errors..."));
Lwt.wakeup_later(
notify_response_received,
`Connection_error(error) |> Lwt_result.fail,
);
};
let host = Uri.host_with_default(uri);
let port =
switch (Uri.port(uri)) {
| None => "443"
| Some(number) => string_of_int(number)
};
Lwt_unix.getaddrinfo(host, port, [Unix.(AI_FAMILY(PF_INET))])
>>= (
addresses => {
let socket_addr = List.hd(addresses).Unix.ai_addr;
let socket = Lwt_unix.socket(Unix.PF_INET, Unix.SOCK_STREAM, 0);
Lwt_unix.connect(socket, socket_addr)
>>= (
() => {
Lwt_ssl.ssl_connect(socket, default_ssl_context)
>>= (
ssl_client => {
let (response_received, notify_response_received) =
Lwt.wait();
let response_handler =
response_handler(notify_response_received);
let error_handler = error_handler(notify_response_received);
let write_body = request_body => {
switch (Httpkit.Request.body(req)) {
| None => ()
| Some(str) => H2.Body.write_string(request_body, str)
};
H2.Body.flush(
request_body,
() => {
H2.Body.close_writer(request_body);
Logs.debug(m => m("Closed body writer..."));
},
);
response_received >>= (x => x);
};
let request = Client_request.of_httpkit_request(req);
Logs.debug(m => {
let buffer = Buffer.create(1024);
let fmt = Format.formatter_of_buffer(buffer);
H2.Request.pp_hum(fmt, request);
m("%s", buffer |> Buffer.contents);
});
let handle_ssl_connection = connection =>
H2_lwt_unix.Client.SSL.request(
connection,
request,
~error_handler,
~response_handler,
)
|> write_body;
let connect = () =>
H2_lwt_unix.Client.SSL.create_connection(
~client=ssl_client,
~config,
~error_handler,
socket,
);
connect() >>= handle_ssl_connection;
}
);
}
);
}
);
};
================================================
FILE: lwt-unix-h2/client_request.re
================================================
let of_httpkit_request = req => {
open Httpkit;
let host = req |> Request.host;
let scheme = req |> Request.scheme;
let meth = req |> Request.meth;
let path = req |> Request.path;
let headers =
[
(":authority", host),
(":method", meth |> Method.to_string),
(":path", path),
(":scheme", scheme),
]
@ (req |> Request.headers)
|> H2.Headers.of_list;
H2.Request.create(~headers, ~scheme, meth, path);
};
let to_httpkit_request = (~body, ~uri, req) => {
Httpkit.Request.create(
~headers=req.H2.Request.headers |> H2.Headers.to_list,
~body=
switch (body) {
| None => ""
| Some(b) => b
},
req.meth,
uri,
);
};
================================================
FILE: lwt-unix-h2/client_response.re
================================================
let body:
((H2.Response.t, H2.Body.t([ | `read]))) => Lwt.t(result(string, 'b)) =
((_response, body)) => {
open Lwt.Infix;
let buffer = Buffer.create(1024);
Logs.debug(m => m("Buffering response..."));
let (next, wakeup) = Lwt.wait();
Lwt.async(() => {
let rec read_response = () =>
H2.Body.schedule_read(
body,
~on_eof=
() => Lwt.wakeup_later(wakeup, Ok(Buffer.contents(buffer))),
~on_read=
(response_fragment, ~off, ~len) => {
let response_fragment_string = Bytes.create(len);
Lwt_bytes.blit_to_bytes(
response_fragment,
off,
response_fragment_string,
0,
len,
);
Buffer.add_bytes(buffer, response_fragment_string);
read_response();
},
);
read_response() |> Lwt.return;
})
|> ignore;
next >>= Lwt_result.lift;
};
================================================
FILE: lwt-unix-h2/dune
================================================
(library
(name httpkit_lwt_unix_h2)
(public_name httpkit-lwt-unix-h2)
(libraries httpkit h2 h2-lwt h2-lwt-unix lwt lwt.unix uri logs logs.lwt fpath bigstringaf))
================================================
FILE: lwt-unix-h2/httpkit_lwt_unix_h2.re
================================================
module Method = Httpkit.Method;
module Status = Httpkit.Status;
module Request = Httpkit.Request;
module Server = {
include Httpkit.Server;
module Http = Server_http;
};
module Client = {
module Https = Client_https;
module Http = Client_http;
module Response = Client_response;
};
================================================
FILE: lwt-unix-h2/server_http.re
================================================
open Lwt.Infix;
let make_request_handler:
(
~uri: Uri.t,
~handler: Httpkit.Server.handler,
~closer: unit => unit,
Unix.sockaddr
) =>
H2.Server_connection.request_handler =
(~uri, ~handler, ~closer, _client, reqd) => {
let req = reqd |> H2.Reqd.request;
Logs.debug(m => m("Handling request..."));
let respond = (~headers=?, status, content) => {
let headers =
(
switch (headers) {
| None => []
| Some(hs) => hs
}
)
@ [("content-length", content |> String.length |> string_of_int)]
|> H2.Headers.of_list;
let res =
H2.Response.create(status |> H2.Status.of_code, ~headers);
H2.Reqd.respond_with_string(reqd, res, content);
};
Server_request.read_body(reqd)
>|= (
body => {
let uri = Uri.with_path(uri, req.target);
let req = Client_request.to_httpkit_request(~uri, ~body, req);
let () = handler(req, respond, closer);
();
}
)
|> ignore;
};
let error_handler = (_client, ~request as _=?, err, _get) => {
Logs.err(m =>
m(
"Something went wrong! %s",
switch (err) {
| `Bad_gateway => "Bad gateway"
| `Bad_request => "Bad request"
| `Internal_server_error => "Internal_server_error"
| `Exn(exn) => Printexc.to_string(exn)
},
)
);
();
};
let listen:
(
~address: [ | `Loopback | `Any | `Of_string(string)]=?,
~port: int,
~on_start: (~hoststring: string) => unit,
~handler: Httpkit.Server.handler
) =>
Lwt.t(unit) =
(~address=`Any, ~port, ~on_start, ~handler) => {
let host =
switch (address) {
| `Loopback => "127.0.0.1"
| `Any => "0.0.0.0"
| `Of_string(str) => str
};
let uri = Uri.make(~scheme="http", ~host, ~port, ());
let (forever, awaker) = Lwt.wait();
let closer = () => Lwt.wakeup_later(awaker, ());
let address =
switch (address) {
| `Loopback => Unix.inet_addr_loopback
| `Any => Unix.inet_addr_any
| `Of_string(str) => Unix.inet_addr_of_string(str)
};
let listening_address = Unix.(ADDR_INET(address, port));
let connection_handler =
H2_lwt_unix.Server.create_connection_handler(
~config=H2.Config.default,
~request_handler=make_request_handler(~uri, ~handler, ~closer),
~error_handler,
);
Lwt_io.establish_server_with_client_socket(
listening_address,
connection_handler,
)
>|= (_ => on_start(~hoststring=Uri.to_string(uri)))
|> ignore;
forever;
};
================================================
FILE: lwt-unix-h2/server_request.re
================================================
let read_body = reqd => {
let (next, awake) = Lwt.wait();
Lwt.async(() => {
let body = reqd |> H2.Reqd.request_body;
let body_str = ref("");
let on_eof = () => Lwt.wakeup_later(awake, Some(body_str^));
let rec on_read = (request_data, ~off, ~len) => {
let read = Bigstringaf.substring(~off, ~len, request_data);
body_str := body_str^ ++ read;
H2.Body.schedule_read(body, ~on_read, ~on_eof);
};
H2.Body.schedule_read(body, ~on_read, ~on_eof);
Lwt.return_unit;
});
next;
};
================================================
FILE: lwt-unix-httpaf/client_http.re
================================================
open Lwt.Infix;
let send:
(~config: Httpaf.Config.t=?, Httpkit.Request.t) =>
Lwt_result.t(
(Httpaf.Response.t, Httpaf.Body.t([ | `read])),
[> | `Connection_error(Httpaf.Client_connection.error)],
) =
(~config=Httpaf.Config.default, req) => {
let uri = Httpkit.Request.uri(req);
let response_handler = (notify_response_received, response, response_body) => {
Logs.debug(m => m("Handling response..."));
Lwt.wakeup_later(
notify_response_received,
(response, response_body) |> Lwt_result.return,
);
};
let error_handler = (notify_response_received, error) => {
Logs.debug(m => m("Handling errors..."));
Lwt.wakeup_later(
notify_response_received,
`Connection_error(error) |> Lwt_result.fail,
);
};
let host = Uri.host_with_default(uri);
let port =
switch (Uri.port(uri)) {
| None => "80"
| Some(number) => string_of_int(number)
};
Logs.debug(m => m("Getting address for %s:%s", host, port));
Lwt_unix.getaddrinfo(host, port, [Unix.(AI_FAMILY(PF_INET))])
>>= (
addresses => {
let socket_addr = List.hd(addresses).Unix.ai_addr;
let socket = Lwt_unix.socket(Unix.PF_INET, Unix.SOCK_STREAM, 0);
Logs.debug(m => m("Opening socket to %s:%s", host, port));
Lwt_unix.connect(socket, socket_addr)
>>= (
() => {
let (response_received, notify_response_received) = Lwt.wait();
let response_handler = response_handler(notify_response_received);
let error_handler = error_handler(notify_response_received);
let write_body = request_body => {
switch (Httpkit.Request.body(req)) {
| None => ()
| Some(str) => Httpaf.Body.write_string(request_body, str)
};
Httpaf.Body.close_writer(request_body);
Logs.debug(m => m("Request sent. Awaiting for response..."));
response_received >>= (x => x);
};
let request = Client_request.of_httpkit_request(req);
Logs.debug(m =>
m("Sending request: \n\n%s", req |> Httpkit.Request.to_string)
);
Httpaf_lwt_unix.Client.request(
~config,
~error_handler,
~response_handler,
socket,
request,
)
|> write_body;
}
);
}
);
};
================================================
FILE: lwt-unix-httpaf/client_https.re
================================================
open Lwt.Infix;
type client_security = [
| `No_authentication
];
let send:
(
~config: Httpaf.Config.t=?,
~client: client_security=?,
Httpkit.Request.t
) =>
Lwt_result.t(
(Httpaf.Response.t, Httpaf.Body.t([ | `read])),
[> | `Connection_error(Httpaf.Client_connection.error)],
) =
(~config=Httpaf.Config.default, ~client as _=`No_authentication, req) => {
let uri = Httpkit.Request.uri(req);
let response_handler = (notify_response_received, response, response_body) => {
Logs.debug(m => m("Handling response..."));
Lwt.wakeup_later(
notify_response_received,
(response, response_body) |> Lwt_result.return,
);
};
let error_handler = (notify_response_received, error) => {
Logs.debug(m => m("Handling errors..."));
Lwt.wakeup_later(
notify_response_received,
`Connection_error(error) |> Lwt_result.fail,
);
};
let host = Uri.host_with_default(uri);
let port =
switch (Uri.port(uri)) {
| None => "443"
| Some(number) => string_of_int(number)
};
Lwt_unix.getaddrinfo(host, port, [Unix.(AI_FAMILY(PF_INET))])
>>= (
addresses => {
Logs.debug(m => m("Got address..."));
let socket_addr = List.hd(addresses).Unix.ai_addr;
let socket = Lwt_unix.socket(Unix.PF_INET, Unix.SOCK_STREAM, 0);
Lwt_unix.connect(socket, socket_addr)
>>= (
() => {
Logs.debug(m => m("Opened socket..."));
let (response_received, notify_response_received) = Lwt.wait();
let response_handler = response_handler(notify_response_received);
let error_handler = error_handler(notify_response_received);
let write_body = request_body => {
Logs.debug(m => m("Writing body..."));
switch (Httpkit.Request.body(req)) {
| None => ()
| Some(str) => Httpaf.Body.write_string(request_body, str)
};
Httpaf.Body.close_writer(request_body);
response_received >>= (x => x);
};
let request = Client_request.of_httpkit_request(req);
Httpaf_lwt_unix.Client.SSL.request(
~config,
~error_handler,
~response_handler,
socket,
request
)
|> write_body
}
);
}
);
};
================================================
FILE: lwt-unix-httpaf/client_request.re
================================================
let of_httpkit_request = req => {
Httpkit.(
Httpaf.Request.create(
~headers=req |> Request.headers |> Httpaf.Headers.of_list,
req |> Request.meth,
req |> Request.path,
)
);
};
let to_httpkit_request = (~body, ~uri, req) => {
Httpkit.Request.create(
~headers=req.Httpaf.Request.headers |> Httpaf.Headers.to_list,
~body=
switch (body) {
| None => ""
| Some(b) => b
},
req.meth,
uri,
);
};
================================================
FILE: lwt-unix-httpaf/client_response.re
================================================
let body:
((Httpaf.Response.t, Httpaf.Body.t([ | `read]))) => Lwt_result.t(string, 'b) =
((_response, body)) => {
open Lwt.Infix;
let buffer = Buffer.create(2048);
Logs.debug(m => m("Prepared buffer for response body..."));
let (next, wakeup) = Lwt.wait();
Lwt.async(() => {
let rec read_response = () =>
Httpaf.Body.schedule_read(
body,
~on_eof=
() => Lwt.wakeup_later(wakeup, Ok(Buffer.contents(buffer))),
~on_read=
(response_fragment, ~off, ~len) => {
let response_fragment_string = Bytes.create(len);
Lwt_bytes.blit_to_bytes(
response_fragment,
off,
response_fragment_string,
0,
len,
);
Buffer.add_bytes(buffer, response_fragment_string);
read_response();
},
);
read_response() |> Lwt.return;
})
|> ignore;
next >>= Lwt_result.lift;
};
================================================
FILE: lwt-unix-httpaf/dune
================================================
(library
(name httpkit_lwt_unix_httpaf)
(public_name httpkit-lwt-unix-httpaf)
(libraries httpkit httpaf httpaf-lwt-unix lwt lwt.unix uri logs logs.lwt bigstringaf))
================================================
FILE: lwt-unix-httpaf/httpkit_lwt_unix_httpaf.re
================================================
module Method = Httpkit.Method;
module Status = Httpkit.Status;
module Request = Httpkit.Request;
module Server = {
include Httpkit.Server;
module Http = Server_http;
};
module Client = {
module Https = Client_https;
module Http = Client_http;
module Response = Client_response;
};
================================================
FILE: lwt-unix-httpaf/server_http.re
================================================
open Lwt.Infix;
let make_request_handler:
(
~uri: Uri.t,
~handler: Httpkit.Server.handler,
~closer: unit => unit,
Unix.sockaddr
) =>
Httpaf.Server_connection.request_handler =
(~uri, ~handler, ~closer, _client, reqd) => {
let req = reqd |> Httpaf.Reqd.request;
Logs.debug(m => m("Handling request..."));
let respond = (~headers=?, status, content) => {
let headers =
(
switch (headers) {
| None => []
| Some(hs) => hs
}
)
@ [("content-length", content |> String.length |> string_of_int)]
|> Httpaf.Headers.of_list;
let res =
Httpaf.Response.create(status |> Httpaf.Status.of_code, ~headers);
Httpaf.Reqd.respond_with_string(reqd, res, content);
};
Server_request.read_body(reqd)
>|= (
body => {
let uri = Uri.with_path(uri, req.target);
let req = Client_request.to_httpkit_request(~uri, ~body, req);
let () = handler(req, respond, closer);
();
}
)
|> ignore;
};
let error_handler = (_client, ~request as _=?, err, _get) => {
Logs.err(m =>
m(
"Something went wrong! %s",
switch (err) {
| `Bad_gateway => "Bad gateway"
| `Bad_request => "Bad request"
| `Internal_server_error => "Internal_server_error"
| `Exn(exn) => Printexc.to_string(exn)
},
)
);
();
};
let listen:
(
~address: [ | `Loopback | `Any | `Of_string(string)]=?,
~port: int,
~on_start: (~hoststring: string) => unit,
~handler: Httpkit.Server.handler
) =>
Lwt.t(unit) =
(~address=`Any, ~port, ~on_start, ~handler) => {
let host =
switch (address) {
| `Loopback => "127.0.0.1"
| `Any => "0.0.0.0"
| `Of_string(str) => str
};
let uri = Uri.make(~scheme="http", ~host, ~port, ());
let (forever, awaker) = Lwt.wait();
let closer = () => Lwt.wakeup_later(awaker, ());
let address =
switch (address) {
| `Loopback => Unix.inet_addr_loopback
| `Any => Unix.inet_addr_any
| `Of_string(str) => Unix.inet_addr_of_string(str)
};
let listening_address = Unix.(ADDR_INET(address, port));
let connection_handler =
Httpaf_lwt_unix.Server.create_connection_handler(
~config=Httpaf.Config.default,
~request_handler=make_request_handler(~uri, ~handler, ~closer),
~error_handler,
);
Lwt_io.establish_server_with_client_socket(
listening_address,
connection_handler,
)
>|= (_ => on_start(~hoststring=Uri.to_string(uri)))
|> ignore;
forever;
};
================================================
FILE: lwt-unix-httpaf/server_request.re
================================================
let read_body = reqd => {
let (next, awake) = Lwt.wait();
Lwt.async(() => {
let body = reqd |> Httpaf.Reqd.request_body;
let body_str = ref("");
let on_eof = () => Lwt.wakeup_later(awake, Some(body_str^));
let rec on_read = (request_data, ~off, ~len) => {
let read = Bigstringaf.substring(~off, ~len, request_data);
body_str := body_str^ ++ read;
Httpaf.Body.schedule_read(body, ~on_read, ~on_eof);
};
Httpaf.Body.schedule_read(body, ~on_read, ~on_eof);
Lwt.return_unit;
});
next;
};
================================================
FILE: src/dune
================================================
(library
(name httpkit)
(public_name httpkit)
(libraries httpaf h2 uri logs))
================================================
FILE: src/httpkit.re
================================================
module Method = H2.Method;
module Status = H2.Status;
module Request = {
type t = {
meth: Method.t,
uri: Uri.t,
body: option(string),
headers: list((string, string)),
};
let body = t => t.body;
let content_length = t =>
switch (t.body) {
| None => 0
| Some(body) => String.length(body)
};
let meth = t => t.meth;
let uri = t => t.uri;
let path = t => t.uri |> Uri.path_and_query;
let headers = t => t.headers;
let host = t => t.uri |> Uri.host_with_default;
let scheme = t =>
switch (t |> uri |> Uri.scheme) {
| None => ""
| Some(s) => s
};
let create = (~headers=[], ~body="", meth, uri) => {
let host = Uri.host_with_default(uri);
let content_length = body |> String.length |> string_of_int;
let headers =
[("host", host), ("content-length", content_length)]
@ (headers |> List.map(((k, v)) => (k |> String.lowercase_ascii, v)));
let body =
switch (body) {
| "" => None
| _ => Some(body)
};
{meth, uri, headers, body};
};
let to_string = req => {
(req.meth |> H2.Method.to_string)
++ " "
++ (req.uri |> Uri.path)
++ "\n"
++ (req.headers |> H2.Headers.of_list |> H2.Headers.to_string)
++ (
switch (req.body) {
| None => ""
| Some(s) => "\n\n" ++ s
}
);
};
};
module Server = {
type replier = (~headers: list((string, string))=?, int, string) => unit;
type closer = unit => unit;
type handler = (Request.t, replier, closer) => unit;
};
gitextract_lfbuuy6e/
├── .gitattributes
├── .gitignore
├── 3rdparty/
│ ├── dune-project
│ ├── httpaf-lwt-unix/
│ │ ├── buffer.ml
│ │ ├── buffer.mli
│ │ ├── dune
│ │ ├── httpaf_lwt_unix.ml
│ │ ├── httpaf_lwt_unix.mli
│ │ ├── ssl_io.ml
│ │ └── tls_io.ml
│ └── httpaf-lwt-unix.opam
├── README.md
├── bench/
│ ├── .gitignore
│ ├── Gemfile
│ ├── README.md
│ ├── echo.go
│ ├── echo.js
│ ├── echo.py
│ └── echo.rb
├── dune-project
├── esy.json
├── examples/
│ ├── dune
│ ├── echo_server_http.re
│ ├── echo_server_http2.re
│ ├── request_http.re
│ └── request_http2.re
├── httpkit-lwt-unix-h2.opam
├── httpkit-lwt-unix-httpaf.opam
├── httpkit.opam
├── lwt-unix-h2/
│ ├── client_http.re
│ ├── client_https.re
│ ├── client_request.re
│ ├── client_response.re
│ ├── dune
│ ├── httpkit_lwt_unix_h2.re
│ ├── server_http.re
│ └── server_request.re
├── lwt-unix-httpaf/
│ ├── client_http.re
│ ├── client_https.re
│ ├── client_request.re
│ ├── client_response.re
│ ├── dune
│ ├── httpkit_lwt_unix_httpaf.re
│ ├── server_http.re
│ └── server_request.re
└── src/
├── dune
└── httpkit.re
SYMBOL INDEX (5 symbols across 2 files)
FILE: bench/echo.go
constant DefaultPort (line 9) | DefaultPort = "8080"
function EchoHandler (line 11) | func EchoHandler(writer http.ResponseWriter, request *http.Request) {
function main (line 18) | func main() {
FILE: bench/echo.py
class handler (line 5) | class handler(BaseHTTPRequestHandler):
method do_GET (line 6) | def do_GET(self):
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (67K chars).
[
{
"path": ".gitattributes",
"chars": 146,
"preview": "bench/* linguist-vendored\n*.re linguist-language=Reason\n*.rei linguist-language=Reason\n*.ml linguist-language=OCaml\n*.ml"
},
{
"path": ".gitignore",
"chars": 56,
"preview": "_esy\n_build\n_public\n.merlin\n/lua\n/lua_modules\n*.install\n"
},
{
"path": "3rdparty/dune-project",
"chars": 39,
"preview": "(lang dune 1.9)\n(name httpaf-lwt-unix)\n"
},
{
"path": "3rdparty/httpaf-lwt-unix/buffer.ml",
"chars": 734,
"preview": "open Lwt.Infix\n\n(* Based on the Buffer module in httpaf_async.ml. *)\ntype t =\n { buffer : Lwt_bytes.t\n ; mutable "
},
{
"path": "3rdparty/httpaf-lwt-unix/buffer.mli",
"chars": 177,
"preview": "type t\n\nval create : int -> t\n\nval get : t -> f:(Lwt_bytes.t -> off:int -> len:int -> int) -> int\nval put : t -> f:(Lwt_"
},
{
"path": "3rdparty/httpaf-lwt-unix/dune",
"chars": 205,
"preview": "(library\n (name httpaf_lwt_unix)\n (public_name httpaf-lwt-unix)\n (libraries faraday-lwt-unix httpaf lwt.unix ssl lwt_ssl"
},
{
"path": "3rdparty/httpaf-lwt-unix/httpaf_lwt_unix.ml",
"chars": 10876,
"preview": "(*----------------------------------------------------------------------------\n Copyright (c) 2018 Inhabited Type LLC"
},
{
"path": "3rdparty/httpaf-lwt-unix/httpaf_lwt_unix.mli",
"chars": 2325,
"preview": "open Httpaf\n\n\n(* The function that results from [create_connection_handler] should be passed\n to [Lwt_io.establish_ser"
},
{
"path": "3rdparty/httpaf-lwt-unix/ssl_io.ml",
"chars": 1868,
"preview": "open Lwt.Infix\n\nlet () = Ssl.init ()\n\nlet readf socket =\n fun _fd buffer ->\n Lwt.catch\n (fun () ->\n Buffer.put"
},
{
"path": "3rdparty/httpaf-lwt-unix/tls_io.ml",
"chars": 512,
"preview": "let readf _tls =\n fun _fd _buffer ->\n Lwt.fail_with \"Tls not available\"\n\nlet writev _tls _fd =\n fun _iovecs ->\n Lwt."
},
{
"path": "3rdparty/httpaf-lwt-unix.opam",
"chars": 0,
"preview": ""
},
{
"path": "README.md",
"chars": 3224,
"preview": "# ⚡️HttpKit — high-level, high-performance HTTP1.1/2 clients/servers in Reason\n\n> NOTE: under heavy reconstruction. Late"
},
{
"path": "bench/.gitignore",
"chars": 5,
"preview": "echo\n"
},
{
"path": "bench/Gemfile",
"chars": 42,
"preview": "source \"https://rubygems.org\"\n\ngem \"rack\"\n"
},
{
"path": "bench/README.md",
"chars": 10058,
"preview": "# Benchmarking HttpKit ⚡️\n\nIn order to ensure that `httpkit` stays fast, here you'll find a number of small\nservers writ"
},
{
"path": "bench/echo.go",
"chars": 436,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/http\"\n)\n\nconst DefaultPort = \"8080\"\n\nfunc EchoHandler(writer http.ResponseW"
},
{
"path": "bench/echo.js",
"chars": 204,
"preview": "const httpServer = require('http');\n\nhttpServer.createServer((req, res) => {\n console.log(new Date(), req.method, req.u"
},
{
"path": "bench/echo.py",
"chars": 424,
"preview": "from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer\n\nPORT_NUMBER = 8080\n\nclass handler(BaseHTTPRequestHandler):"
},
{
"path": "bench/echo.rb",
"chars": 257,
"preview": "require 'rack'\n \napp = Proc.new do |env|\n req = Rack::Request.new(env)\n meth = req.request_method\n path = req.path\n "
},
{
"path": "dune-project",
"chars": 47,
"preview": "(lang dune 1.5)\n(name httpkit)\n(using fmt 1.0)\n"
},
{
"path": "esy.json",
"chars": 1037,
"preview": "{\n \"dependencies\": {\n \"@esy-ocaml/reason\": \"3.4.0\",\n \"@opam/dune\": \"*\",\n \"@opam/fmt\": \"*\",\n \"@opam/h2\": \"0."
},
{
"path": "examples/dune",
"chars": 595,
"preview": "(executable\n (name request_http)\n (modules request_http)\n\t(ocamlopt_flags -O3)\n (libraries httpkit-lwt-unix-httpaf ht"
},
{
"path": "examples/echo_server_http.re",
"chars": 695,
"preview": "/** Handle sigpipe internally */\nSys.(set_signal(sigpipe, Signal_ignore));\n\n/** Setup loggers */\nFmt_tty.setup_std_outpu"
},
{
"path": "examples/echo_server_http2.re",
"chars": 691,
"preview": "/** Handle sigpipe internally */\nSys.(set_signal(sigpipe, Signal_ignore));\n\n/** Setup loggers */\nFmt_tty.setup_std_outpu"
},
{
"path": "examples/request_http.re",
"chars": 1356,
"preview": "open Lwt_result.Infix;\n\n/** Handle sigpipe internally */\nSys.(set_signal(sigpipe, Signal_ignore));\n\n/** Setup loggers */"
},
{
"path": "examples/request_http2.re",
"chars": 1351,
"preview": "open Lwt_result.Infix;\n\n/** Handle sigpipe internally */\nSys.(set_signal(sigpipe, Signal_ignore));\n\n/** Setup loggers */"
},
{
"path": "httpkit-lwt-unix-h2.opam",
"chars": 578,
"preview": "opam-version: \"2.0\"\nname: \"httpkit-lwt-unix-h2\"\nversion: \"0.13\"\nsynopsis: \"High-level, High-performance HTTP(S) Clients/"
},
{
"path": "httpkit-lwt-unix-httpaf.opam",
"chars": 567,
"preview": "opam-version: \"2.0\"\nname: \"httpkit-lwt-unix-httpaf\"\nversion: \"0.13\"\nsynopsis: \"High-level, High-performance HTTP(S) Clie"
},
{
"path": "httpkit.opam",
"chars": 510,
"preview": "opam-version: \"2.0\"\nname: \"httpkit\"\nversion: \"0.13\"\nsynopsis: \"High-level, High-performance HTTP(S) Clients/Servers\"\nmai"
},
{
"path": "lwt-unix-h2/client_http.re",
"chars": 2565,
"preview": "open Lwt.Infix;\n\nlet send:\n (~config: H2.Config.t=?, Httpkit.Request.t) =>\n Lwt_result.t(\n (H2.Response.t, H2.Body."
},
{
"path": "lwt-unix-h2/client_https.re",
"chars": 3505,
"preview": "open Lwt.Infix;\n\nSsl_threads.init();\nSsl.init();\nlet default_ssl_context = Ssl.create_context(Ssl.SSLv23, Ssl.Client_con"
},
{
"path": "lwt-unix-h2/client_request.re",
"chars": 704,
"preview": "let of_httpkit_request = req => {\n open Httpkit;\n let host = req |> Request.host;\n let scheme = req |> Request.scheme"
},
{
"path": "lwt-unix-h2/client_response.re",
"chars": 996,
"preview": "let body:\n ((H2.Response.t, H2.Body.t([ | `read]))) => Lwt.t(result(string, 'b)) =\n ((_response, body)) => {\n open "
},
{
"path": "lwt-unix-h2/dune",
"chars": 168,
"preview": "(library\n (name httpkit_lwt_unix_h2)\n (public_name httpkit-lwt-unix-h2)\n (libraries httpkit h2 h2-lwt h2-lwt-unix lwt"
},
{
"path": "lwt-unix-h2/httpkit_lwt_unix_h2.re",
"chars": 296,
"preview": "module Method = Httpkit.Method;\n\nmodule Status = Httpkit.Status;\n\nmodule Request = Httpkit.Request;\n\nmodule Server = {\n "
},
{
"path": "lwt-unix-h2/server_http.re",
"chars": 2599,
"preview": "open Lwt.Infix;\n\nlet make_request_handler:\n (\n ~uri: Uri.t,\n ~handler: Httpkit.Server.handler,\n ~closer: unit "
},
{
"path": "lwt-unix-h2/server_request.re",
"chars": 528,
"preview": "let read_body = reqd => {\n let (next, awake) = Lwt.wait();\n\n Lwt.async(() => {\n let body = reqd |> H2.Reqd.request_"
},
{
"path": "lwt-unix-httpaf/client_http.re",
"chars": 2491,
"preview": "open Lwt.Infix;\n\nlet send:\n (~config: Httpaf.Config.t=?, Httpkit.Request.t) =>\n Lwt_result.t(\n (Httpaf.Response.t, "
},
{
"path": "lwt-unix-httpaf/client_https.re",
"chars": 2447,
"preview": "open Lwt.Infix;\n\ntype client_security = [\n | `No_authentication\n];\n\nlet send:\n (\n ~config: Httpaf.Config.t=?,\n ~"
},
{
"path": "lwt-unix-httpaf/client_request.re",
"chars": 462,
"preview": "let of_httpkit_request = req => {\n Httpkit.(\n Httpaf.Request.create(\n ~headers=req |> Request.headers |> Httpaf"
},
{
"path": "lwt-unix-httpaf/client_response.re",
"chars": 1022,
"preview": "let body:\n ((Httpaf.Response.t, Httpaf.Body.t([ | `read]))) => Lwt_result.t(string, 'b) =\n ((_response, body)) => {\n "
},
{
"path": "lwt-unix-httpaf/dune",
"chars": 171,
"preview": "(library\n (name httpkit_lwt_unix_httpaf)\n (public_name httpkit-lwt-unix-httpaf)\n (libraries httpkit httpaf httpaf-lwt"
},
{
"path": "lwt-unix-httpaf/httpkit_lwt_unix_httpaf.re",
"chars": 296,
"preview": "module Method = Httpkit.Method;\n\nmodule Status = Httpkit.Status;\n\nmodule Request = Httpkit.Request;\n\nmodule Server = {\n "
},
{
"path": "lwt-unix-httpaf/server_http.re",
"chars": 2631,
"preview": "open Lwt.Infix;\n\nlet make_request_handler:\n (\n ~uri: Uri.t,\n ~handler: Httpkit.Server.handler,\n ~closer: unit "
},
{
"path": "lwt-unix-httpaf/server_request.re",
"chars": 540,
"preview": "let read_body = reqd => {\n let (next, awake) = Lwt.wait();\n\n Lwt.async(() => {\n let body = reqd |> Httpaf.Reqd.requ"
},
{
"path": "src/dune",
"chars": 84,
"preview": "(library\n (name httpkit)\n (public_name httpkit)\n (libraries httpaf h2 uri logs))\n"
},
{
"path": "src/httpkit.re",
"chars": 1535,
"preview": "module Method = H2.Method;\n\nmodule Status = H2.Status;\n\nmodule Request = {\n type t = {\n meth: Method.t,\n uri: Uri"
}
]
About this extraction
This page contains the full source code of the ostera/httpkit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (60.6 KB), approximately 17.9k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.