|
1 | 1 | (** Tiny Http Server |
2 | 2 |
|
3 | 3 | This library implements a very simple, basic HTTP/1.1 server using blocking |
4 | | - IOs and threads. Basic routing based is provided for convenience, |
5 | | - so that several handlers can be registered. |
| 4 | + IOs and threads. Basic routing based is provided for convenience, so that |
| 5 | + several handlers can be registered. |
6 | 6 |
|
7 | 7 | It is possible to use a thread pool, see {!create}'s argument [new_thread]. |
8 | 8 |
|
9 | 9 | The [echo] example (see [src/examples/echo.ml]) demonstrates some of the |
10 | 10 | features by declaring a few endpoints, including one for uploading files: |
11 | 11 |
|
12 | 12 | {[ |
13 | | -module S = Tiny_httpd |
14 | | -
|
15 | | -let () = |
16 | | - let server = S.create () in |
17 | | -
|
18 | | - (* say hello *) |
19 | | - S.add_route_handler ~meth:`GET server |
20 | | - S.Route.(exact "hello" @/ string @/ return) |
21 | | - (fun name _req -> S.Response.make_string (Ok ("hello " ^name ^"!\n"))); |
22 | | -
|
23 | | - (* echo request *) |
24 | | - S.add_route_handler server |
25 | | - S.Route.(exact "echo" @/ return) |
26 | | - (fun req -> S.Response.make_string |
27 | | - (Ok (Format.asprintf "echo:@ %a@." S.Request.pp req))); |
28 | | -
|
29 | | - (* file upload *) |
30 | | - S.add_route_handler ~meth:`PUT server |
31 | | - S.Route.(exact "upload" @/ string_urlencoded @/ return) |
32 | | - (fun path req -> |
33 | | - try |
34 | | - let oc = open_out @@ "/tmp/" ^ path in |
35 | | - output_string oc req.S.Request.body; |
36 | | - flush oc; |
37 | | - S.Response.make_string (Ok "uploaded file") |
38 | | - with e -> |
39 | | - S.Response.fail ~code:500 "couldn't upload file: %s" |
40 | | - (Printexc.to_string e) |
41 | | - ); |
42 | | -
|
43 | | - (* run the server *) |
44 | | - Printf.printf "listening on http://%s:%d\n%!" (S.addr server) (S.port server); |
45 | | - match S.run server with |
46 | | - | Ok () -> () |
47 | | - | Error e -> raise e |
| 13 | + module S = Tiny_httpd |
| 14 | +
|
| 15 | + let () = |
| 16 | + let server = S.create () in |
| 17 | +
|
| 18 | + (* say hello *) |
| 19 | + S.add_route_handler ~meth:`GET server |
| 20 | + S.Route.(exact "hello" @/ string @/ return) |
| 21 | + (fun name _req -> |
| 22 | + S.Response.make_string (Ok ("hello " ^ name ^ "!\n"))); |
| 23 | +
|
| 24 | + (* echo request *) |
| 25 | + S.add_route_handler server |
| 26 | + S.Route.(exact "echo" @/ return) |
| 27 | + (fun req -> |
| 28 | + S.Response.make_string |
| 29 | + (Ok (Format.asprintf "echo:@ %a@." S.Request.pp req))); |
| 30 | +
|
| 31 | + (* file upload *) |
| 32 | + S.add_route_handler ~meth:`PUT server |
| 33 | + S.Route.(exact "upload" @/ string_urlencoded @/ return) |
| 34 | + (fun path req -> |
| 35 | + try |
| 36 | + let oc = open_out @@ "/tmp/" ^ path in |
| 37 | + output_string oc req.S.Request.body; |
| 38 | + flush oc; |
| 39 | + S.Response.make_string (Ok "uploaded file") |
| 40 | + with e -> |
| 41 | + S.Response.fail ~code:500 "couldn't upload file: %s" |
| 42 | + (Printexc.to_string e)); |
| 43 | +
|
| 44 | + (* run the server *) |
| 45 | + Printf.printf "listening on http://%s:%d\n%!" (S.addr server) |
| 46 | + (S.port server); |
| 47 | + match S.run server with |
| 48 | + | Ok () -> () |
| 49 | + | Error e -> raise e |
48 | 50 | ]} |
49 | 51 |
|
50 | 52 | It is then possible to query it using curl: |
51 | 53 |
|
52 | 54 | {[ |
53 | | -$ dune exec src/examples/echo.exe & |
54 | | -listening on http://127.0.0.1:8080 |
55 | | -
|
56 | | -# the path "hello/name" greets you. |
57 | | -$ curl -X GET http://localhost:8080/hello/quadrarotaphile |
58 | | -hello quadrarotaphile! |
59 | | -
|
60 | | -# the path "echo" just prints the request. |
61 | | -$ curl -X GET http://localhost:8080/echo --data "howdy y'all" |
62 | | -echo: |
63 | | -{meth=GET; |
64 | | - headers=Host: localhost:8080 |
65 | | - User-Agent: curl/7.66.0 |
66 | | - Accept: */* |
67 | | - Content-Length: 10 |
68 | | - Content-Type: application/x-www-form-urlencoded; |
69 | | - path="/echo"; body="howdy y'all"} |
70 | | -
|
71 | | -
|
72 | | - ]} |
73 | | -
|
74 | | -*) |
| 55 | + $ dune exec src/examples/echo.exe & |
| 56 | + listening on http://127.0.0.1:8080 |
| 57 | +
|
| 58 | + # the path "hello/name" greets you. |
| 59 | + $ curl -X GET http://localhost:8080/hello/quadrarotaphile |
| 60 | + hello quadrarotaphile! |
| 61 | +
|
| 62 | + # the path "echo" just prints the request. |
| 63 | + $ curl -X GET http://localhost:8080/echo --data "howdy y'all" |
| 64 | + echo: |
| 65 | + {meth=GET; |
| 66 | + headers=Host: localhost:8080 |
| 67 | + User-Agent: curl/7.66.0 |
| 68 | + Accept: */* |
| 69 | + Content-Length: 10 |
| 70 | + Content-Type: application/x-www-form-urlencoded; |
| 71 | + path="/echo"; body="howdy y'all"} |
| 72 | + ]} *) |
75 | 73 |
|
76 | 74 | (** {2 Tiny buffer implementation} |
77 | 75 |
|
78 | 76 | These buffers are used to avoid allocating too many byte arrays when |
79 | | - processing streams and parsing requests. |
80 | | -*) |
| 77 | + processing streams and parsing requests. *) |
81 | 78 |
|
82 | 79 | module Buf = Buf |
83 | 80 |
|
@@ -141,37 +138,42 @@ val create : |
141 | 138 | t |
142 | 139 | (** Create a new webserver using UNIX abstractions. |
143 | 140 |
|
144 | | - The server will not do anything until {!run} is called on it. |
145 | | - Before starting the server, one can use {!add_path_handler} and |
146 | | - {!set_top_handler} to specify how to handle incoming requests. |
| 141 | + The server will not do anything until {!run} is called on it. Before |
| 142 | + starting the server, one can use {!add_path_handler} and {!set_top_handler} |
| 143 | + to specify how to handle incoming requests. |
147 | 144 |
|
148 | | - @param masksigpipe if true, block the signal {!Sys.sigpipe} which otherwise |
149 | | - tends to kill client threads when they try to write on broken sockets. |
150 | | - Default: [true] except when on Windows, which defaults to [false]. |
| 145 | + @param masksigpipe |
| 146 | + if true, block the signal {!Sys.sigpipe} which otherwise tends to kill |
| 147 | + client threads when they try to write on broken sockets. Default: [true] |
| 148 | + except when on Windows, which defaults to [false]. |
151 | 149 |
|
152 | 150 | @param buf_size size for buffers (since 0.11) |
153 | 151 |
|
154 | | - @param new_thread a function used to spawn a new thread to handle a |
155 | | - new client connection. By default it is {!Thread.create} but one |
156 | | - could use a thread pool instead. |
157 | | - See for example {{: https://github.com/c-cube/tiny-httpd-moonpool-bench/blob/0dcbbffb4fe34ea4ad79d46343ad0cebb69ca69f/examples/t1.ml#L31} |
158 | | - this use of moonpool}. |
| 152 | + @param new_thread |
| 153 | + a function used to spawn a new thread to handle a new client connection. |
| 154 | + By default it is {!Thread.create} but one could use a thread pool instead. |
| 155 | + See for example |
| 156 | + {{:https://github.com/c-cube/tiny-httpd-moonpool-bench/blob/0dcbbffb4fe34ea4ad79d46343ad0cebb69ca69f/examples/t1.ml#L31} |
| 157 | + this use of moonpool}. |
159 | 158 |
|
160 | 159 | @param middlewares see {!add_middleware} for more details. |
161 | 160 |
|
162 | 161 | @param max_connections maximum number of simultaneous connections. |
163 | | - @param timeout connection is closed if the socket does not do read or |
164 | | - write for the amount of second. Default: 0.0 which means no timeout. |
165 | | - timeout is not recommended when using proxy. |
| 162 | + @param timeout |
| 163 | + connection is closed if the socket does not do read or write for the |
| 164 | + amount of second. Default: 0.0 which means no timeout. timeout is not |
| 165 | + recommended when using proxy. |
166 | 166 | @param addr address (IPv4 or IPv6) to listen on. Default ["127.0.0.1"]. |
167 | 167 | @param port to listen on. Default [8080]. |
168 | | - @param sock an existing socket given to the server to listen on, e.g. by |
169 | | - systemd on Linux (or launchd on macOS). If passed in, this socket will be |
170 | | - used instead of the [addr] and [port]. If not passed in, those will be |
171 | | - used. This parameter exists since 0.10. |
172 | | - @param enable_logging if true and [Logs] is installed, log requests. Default true. |
173 | | - This parameter exists since 0.18. Does not affect debug-level logs. |
174 | | -
|
175 | | - @param get_time_s obtain the current timestamp in seconds. |
176 | | - This parameter exists since 0.11. |
| 168 | + @param sock |
| 169 | + an existing socket given to the server to listen on, e.g. by systemd on |
| 170 | + Linux (or launchd on macOS). If passed in, this socket will be used |
| 171 | + instead of the [addr] and [port]. If not passed in, those will be used. |
| 172 | + This parameter exists since 0.10. |
| 173 | + @param enable_logging |
| 174 | + if true and [Logs] is installed, log requests. Default true. This |
| 175 | + parameter exists since 0.18. Does not affect debug-level logs. |
| 176 | +
|
| 177 | + @param get_time_s |
| 178 | + obtain the current timestamp in seconds. This parameter exists since 0.11. |
177 | 179 | *) |
0 commit comments