example: update echo to provide a /stats/ endpoint using a middleware

This commit is contained in:
Simon Cruanes 2021-12-15 16:27:28 -05:00
parent 5827328993
commit 7685505f28
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4

View file

@ -1,6 +1,28 @@
module S = Tiny_httpd
let now_ = Unix.gettimeofday
(* util: a little middleware collecting statistics *)
let middleware_stat () : S.Middleware.t * (unit -> string) =
let n_req = ref 0 in
let total_time_ = ref 0. in
let m h req ~resp =
incr n_req;
let t1 = now_ () in
h req ~resp:(fun response ->
resp response;
let t2 = now_ () in
total_time_ := !total_time_ +. (t2 -. t1);
)
and get_stat () =
Printf.sprintf "%d requests (average response time: %.3fs)"
!n_req (!total_time_ /. float !n_req)
in
m, get_stat
let () =
let port_ = ref 8080 in
let j = ref 32 in
@ -10,12 +32,19 @@ let () =
"--debug", Arg.Unit (fun () -> S._enable_debug true), " enable debug";
"-j", Arg.Set_int j, " maximum number of connections";
]) (fun _ -> raise (Arg.Bad "")) "echo [option]*";
let server = S.create ~port:!port_ ~max_connections:!j () in
Tiny_httpd_camlzip.setup ~compress_above:1024 ~buf_size:(16*1024) server;
let m_stats, get_stats = middleware_stat () in
S.add_middleware server ~stage:(`Stage 1) m_stats;
(* 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")));
(* compressed file access *)
S.add_route_handler ~meth:`GET server
S.Route.(exact "zcat" @/ string_urlencoded @/ return)
(fun path _req ->
@ -33,6 +62,7 @@ let () =
in
S.Response.make_stream ~headers:mime_type (Ok str)
);
(* echo request *)
S.add_route_handler server
S.Route.(exact "echo" @/ return)
@ -43,6 +73,8 @@ let () =
in
S.Response.make_string
(Ok (Format.asprintf "echo:@ %a@ (query: %s)@." S.Request.pp req q)));
(* file upload *)
S.add_route_handler_stream ~meth:`PUT server
S.Route.(exact "upload" @/ string @/ return)
(fun path req ->
@ -56,6 +88,28 @@ let () =
with e ->
S.Response.fail ~code:500 "couldn't upload file: %s" (Printexc.to_string e)
);
(* stats *)
S.add_route_handler server S.Route.(exact "stats" @/ return)
(fun _req ->
let stats = get_stats() in
S.Response.make_string @@ Ok stats
);
(* main page *)
S.add_route_handler server S.Route.(return)
(fun _req ->
let s = "<head></head><body>\n\
<p><b>welcome!</b>\n<p>endpoints are:\n<ul>\
<li><pre>/hello/'name' (GET)</pre></li>\n\
<li><pre>/echo/ (GET) echoes back query</pre></li>\n\
<li><pre>/upload/'path' (PUT) to upload a file</pre></li>\n\
<li><pre>/zcat/'path' (GET) to download a file (compressed)</pre></li>\n\
<li><pre>/stats/ (GET) to access statistics</pre></li>\n\
</ul></body>"
in
S.Response.make_string ~headers:["content-type", "text/html"] @@ Ok s);
Printf.printf "listening on http://%s:%d\n%!" (S.addr server) (S.port server);
match S.run server with
| Ok () -> ()