Module Tiny_httpd.Buf_
Tiny buffer implementation
These buffers are used to avoid allocating too many byte arrays when processing streams and parsing requests.
diff --git a/0.1/tiny_httpd/Tiny_httpd/Buf_/index.html b/0.1/tiny_httpd/Tiny_httpd/Buf_/index.html index ebecdf1a..927c4c98 100644 --- a/0.1/tiny_httpd/Tiny_httpd/Buf_/index.html +++ b/0.1/tiny_httpd/Tiny_httpd/Buf_/index.html @@ -1,2 +1,2 @@ -
Tiny_httpd.Buf_These buffers are used to avoid allocating too many byte arrays when processing streams and parsing requests.
Tiny_httpd.Buf_Tiny_httpd.RequestA request sent by a client.
type 'body t = {meth : Meth.t; |
headers : Headers.t; |
path : string; |
body : 'body; |
}A request with method, path, headers, and a body.
The body is polymorphic because the request goes through several transformations. First it has no body, as only the request and headers are read; then it has a stream body; then the body might be entirely read as a string via read_body_full.
val pp : Format.formatter -> string t -> unitPretty print the request and its body
val pp_ : Format.formatter -> _ t -> unitPretty print the request without its body
val headers : _ t -> Headers.tval get_header : ?f:(string -> string) -> _ t -> string -> string optionval get_header_int : _ t -> string -> int optionval set_header : 'a t -> string -> string -> 'a tval meth : _ t -> Meth.tval path : _ t -> stringval body : 'b t -> 'bval read_body_full : stream t -> string tRead the whole body into a string. Potentially blocking.
Tiny_httpd.Requesttype 'body t = {meth : Meth.t; |
host : string; |
headers : Headers.t; |
path : string; |
body : 'body; |
}A request with method, path, host, headers, and a body, sent by a client.
The body is polymorphic because the request goes through several transformations. First it has no body, as only the request and headers are read; then it has a stream body; then the body might be entirely read as a string via read_body_full.
val pp : Format.formatter -> string t -> unitPretty print the request and its body
val pp_ : Format.formatter -> _ t -> unitPretty print the request without its body
val get_header : ?f:(string -> string) -> _ t -> string -> string optionval get_header_int : _ t -> string -> int optionval set_header : 'a t -> string -> string -> 'a tval host : _ t -> stringHost field of the request. It also appears in the headers.
val path : _ t -> stringRequest path.
val body : 'b t -> 'bRequest body, possibly empty.
val read_body_full : byte_stream t -> string tRead the whole body into a string. Potentially blocking.
Tiny_httpd.ResponseA response sent back to a client.
type body = [ | `String of string |
| `Stream of stream |
]Body of a response, either as a simple string, or a stream of bytes.
type t = {code : Response_code.t; | HTTP response code. See |
headers : Headers.t; | Headers of the reply. Some will be set by |
body : body; | Body of the response. Can be empty. |
}A response.
val make_raw : ?headers:Headers.t -> code:Response_code.t -> string -> tMake a response from its raw components, with a string body. Use "" to not send a body at all.
val make_raw_stream : ?headers:Headers.t -> code:Response_code.t -> stream -> tSame as make_raw but with a stream body. The body will be sent with the chunked transfer-encoding.
val make : ?headers:Headers.t -> (body, Response_code.t * string) Pervasives.result -> tmake r turns a result into a response.
make (Ok body) replies with 200 and the body.make (Error (code,msg)) replies with the given error code and message as body.val make_string : ?headers:Headers.t -> (string, Response_code.t * string) Pervasives.result -> tSame as make but with a string body.
val make_stream : ?headers:Headers.t -> (stream, Response_code.t * string) Pervasives.result -> tSame as make but with a stream body.
val fail : ?headers:Headers.t -> code:int -> ('a, unit, string, t) Pervasives.format4 -> 'aMake the current request fail with the given code and message. Example: fail ~code:404 "oh noes, %s not found" "waldo".
val fail_raise : code:int -> ('a, unit, string, 'b) Pervasives.format4 -> 'aSimilar to fail but raises an exception that exits the current handler. This should not be used outside of a (path) handler. Example: fail_raise ~code:404 "oh noes, %s not found" "waldo"; never_executed()
val pp : Format.formatter -> t -> unitPretty print the response.
Tiny_httpd.Responsetype body = [ | `String of string |
| `Stream of byte_stream |
]Body of a response, either as a simple string, or a stream of bytes.
type t = {code : Response_code.t; | HTTP response code. See |
headers : Headers.t; | Headers of the reply. Some will be set by |
body : body; | Body of the response. Can be empty. |
}A response to send back to a client.
val make_raw : ?headers:Headers.t -> code:Response_code.t -> string -> tMake a response from its raw components, with a string body. Use "" to not send a body at all.
val make_raw_stream : ?headers:Headers.t -> code:Response_code.t -> byte_stream -> tSame as make_raw but with a stream body. The body will be sent with the chunked transfer-encoding.
val make : ?headers:Headers.t -> (body, Response_code.t * string) Pervasives.result -> tmake r turns a result into a response.
make (Ok body) replies with 200 and the body.make (Error (code,msg)) replies with the given error code and message as body.val make_string : ?headers:Headers.t -> (string, Response_code.t * string) Pervasives.result -> tSame as make but with a string body.
val make_stream : ?headers:Headers.t -> (byte_stream, Response_code.t * string) Pervasives.result -> tSame as make but with a stream body.
val fail : ?headers:Headers.t -> code:int -> ('a, unit, string, t) Pervasives.format4 -> 'aMake the current request fail with the given code and message. Example: fail ~code:404 "oh noes, %s not found" "waldo".
val fail_raise : code:int -> ('a, unit, string, 'b) Pervasives.format4 -> 'aSimilar to fail but raises an exception that exits the current handler. This should not be used outside of a (path) handler. Example: fail_raise ~code:404 "oh noes, %s not found" "waldo"; never_executed()
val pp : Format.formatter -> t -> unitPretty print the response.
Tiny_httpd.Response_codeval ok : tThe code 200
val not_found : tThe code 404
val descr : t -> stringA description of some of the error codes. NOTE: this is not complete (yet).
Tiny_httpd.Response_codeval ok : tThe code 200
val not_found : tThe code 404
val descr : t -> stringA description of some of the error codes. NOTE: this is not complete (yet).
Tiny_httpd.Stream_Streams are used to represent a series of bytes that can arrive progressively. For example, an uploaded file will be sent as a series of chunks.
type t = streamval close : t -> unitval of_chan : Pervasives.in_channel -> tMake a buffered stream from the given channel.
val of_chan_close_noerr : Pervasives.in_channel -> tSame as of_chan but the close method will never fail.
val of_bytes : ?i:int -> ?len:int -> bytes -> tA stream that just returns the slice of bytes starting from i and of length len.
val with_file : string -> (t -> 'a) -> 'aOpen a file with given name, and obtain an input stream on its content. When the function returns, the stream (and file) are closed.
Tiny_httpd.Stream_type t = streamval close : t -> unitval of_chan : Pervasives.in_channel -> tMake a buffered stream from the given channel.
val of_chan_close_noerr : Pervasives.in_channel -> tSame as of_chan but the close method will never fail.
val of_bytes : ?i:int -> ?len:int -> bytes -> tA stream that just returns the slice of bytes starting from i and of length len.
val with_file : string -> (t -> 'a) -> 'aOpen a file with given name, and obtain an input stream on its content. When the function returns, the stream (and file) are closed.
Tiny_httpdThis library implements a very simple, basic HTTP/1.1 server using blocking IOs and threads. Basic routing based on Scanf is provided for convenience, so that several handlers can be registered.
It is possible to use a thread pool, see create's argument new_thread.
The echo example (see src/examples/echo.ml) demonstrates some of the features by declaring a few endpoints, including one for uploading files:
let () =
+Tiny_httpd (tiny_httpd.Tiny_httpd) Module Tiny_httpd
Tiny Http Server
This library implements a very simple, basic HTTP/1.1 server using blocking IOs and threads. Basic routing based on Scanf is provided for convenience, so that several handlers can be registered.
It is possible to use a thread pool, see create's argument new_thread.
The echo example (see src/examples/echo.ml) demonstrates some of the features by declaring a few endpoints, including one for uploading files:
module S = Tiny_httpd
+
+let () =
let server = S.create () in
(* say hello *)
S.add_path_handler ~meth:`GET server
- "/hello/%s@/" (fun name _req -> S.Response.make_string (Ok ("hello " ^name ^"!\n")));
+ "/hello/%s@/" (fun name _req ->
+ S.Response.make_string (Ok ("hello " ^name ^"!\n")));
(* echo request *)
S.add_path_handler server
- "/echo" (fun req -> S.Response.make_string (Ok (Format.asprintf "echo:@ %a@." S.Request.pp req)));
+ "/echo" (fun req -> S.Response.make_string
+ (Ok (Format.asprintf "echo:@ %a@." S.Request.pp req)));
S.add_path_handler ~meth:`PUT server
"/upload/%s" (fun path req ->
- debug_ (fun k->k "start upload %S\n%!" path);
try
let oc = open_out @@ "/tmp/" ^ path in
output_string oc req.S.Request.body;
flush oc;
S.Response.make_string (Ok "uploaded file")
with e ->
- S.Response.fail ~code:500 "couldn't upload file: %s" (Printexc.to_string e)
+ S.Response.fail ~code:500 "couldn't upload file: %s"
+ (Printexc.to_string e)
);
Printf.printf "listening on http://%s:%d\n%!" (S.addr server) (S.port server);
match S.run server with
@@ -37,4 +41,4 @@ echo:
Accept: */*
Content-Length: 10
Content-Type: application/x-www-form-urlencoded;
- path="/echo"; body="howdy y'all"}
type stream = {}A buffered stream, with a view into the current buffer (or refill if empty), and a function to consume n bytes. See Buf_ for more details.
module Buf_ : sig ... end
module Stream_ : sig ... end
module Meth : sig ... endmodule Headers : sig ... endmodule Request : sig ... end
module Response_code : sig ... end
module Response : sig ... end
type tA HTTP server. See create for more details.
val create : ?masksigpipe:bool -> ?new_thread:((unit -> unit) -> unit) -> ?addr:string -> ?port:int -> unit -> tCreate a new webserver.
The server will not do anything until run is called on it. Before starting the server, one can use add_path_handler and set_top_handler to specify how to handle incoming requests.
- parameter masksigpipe
if true, block the signal Sys.sigpipe which otherwise tends to kill client threads when they try to write on broken sockets. Default: true.
- parameter new_thread
a function used to spawn a new thread to handle a new client connection. By default it is Thread.create but one could use a thread pool instead.
- parameter addr
the address (IPv4) to listen on. Default "127.0.0.1".
- parameter port
to listen on. Default 8080.
val addr : t -> stringAddress on which the server listen.
val port : t -> intPort on which the server listen.
val add_decode_request_cb : t -> (unit Request.t -> (unit Request.t * (stream -> stream)) option) -> unitAdd a callback for every request. The callback can provide a stream transformer and a new request (with modified headers, typically). A possible use is to handle decompression by looking for a Transfer-Encoding header and returning a stream transformer that decompresses on the fly.
val add_encode_response_cb : t -> (string Request.t -> Response.t -> Response.t option) -> unitAdd a callback for every request/response pair. Similarly to add_encode_response_cb the callback can return a new response, for example to compress it. The callback is given the fully parsed query as well as the current response.
val set_top_handler : t -> (string Request.t -> Response.t) -> unitSetup a handler called by default.
This handler is called with any request not accepted by any handler installed via add_path_handler. If no top handler is installed, unhandled paths will return a 404 not found.
val add_path_handler : ?accept:(unit Request.t -> (unit, Response_code.t * string) Pervasives.result) -> ?meth:Meth.t -> t -> ('a, Scanf.Scanning.in_channel, 'b, 'c -> string Request.t -> Response.t, 'a -> 'd, 'd) Pervasives.format6 -> 'c -> unitadd_path_handler server "/some/path/%s@/%d/" f calls f request "foo" 42 () when a request with path "some/path/foo/42/" is received.
This uses Scanf's splitting, which has some gotchas (in particular, "%s" is eager, so it's generally necessary to delimit its scope with a "@/" delimiter. The "@" before a character indicates it's a separator.
Note that the handlers are called in the reverse order of their addition, so the last registered handler can override previously registered ones.
- parameter meth
if provided, only accept requests with the given method. Typically one could react to `GET or `PUT.
- parameter accept
should return Ok() if the given request (before its body is read) should be accepted, Error (code,message) if it's to be rejected (e.g. because its content is too big, or for some permission error). See the http_of_dir program for an example of how to use accept to filter uploads that are too large before the upload even starts.
val stop : t -> unitAsk the server to stop. This might not have an immediate effect as run might currently be waiting on IO.
val run : t -> (unit, exn) Pervasives.resultRun the main loop of the server, listening on a socket described at the server's creation time, using new_thread to start a thread for each new client.
This returns Ok () if the server exits gracefully, or Error e if it exits with an error.
\ No newline at end of file
+ path="/echo"; body="howdy y'all"}These buffers are used to avoid allocating too many byte arrays when processing streams and parsing requests.
module Buf_ : sig ... endStreams are used to represent a series of bytes that can arrive progressively. For example, an uploaded file will be sent as a series of chunks.
type byte_stream = {}A buffered stream, with a view into the current buffer (or refill if empty), and a function to consume n bytes. See Byte_stream for more details.
module Byte_stream : sig ... endmodule Meth : sig ... endmodule Headers : sig ... endmodule Request : sig ... endmodule Response_code : sig ... endmodule Response : sig ... endtype tA HTTP server. See create for more details.
val create : ?masksigpipe:bool -> ?max_connections:int -> ?new_thread:((unit -> unit) -> unit) -> ?addr:string -> ?port:int -> unit -> tCreate a new webserver.
The server will not do anything until run is called on it. Before starting the server, one can use add_path_handler and set_top_handler to specify how to handle incoming requests.
if true, block the signal Sys.sigpipe which otherwise tends to kill client threads when they try to write on broken sockets. Default: true.
a function used to spawn a new thread to handle a new client connection. By default it is Thread.create but one could use a thread pool instead.
maximum number of simultaneous connections.
the address (IPv4) to listen on. Default "127.0.0.1".
to listen on. Default 8080.
val addr : t -> stringAddress on which the server listens.
val port : t -> intPort on which the server listens.
val add_decode_request_cb : t -> (unit Request.t -> (unit Request.t * (byte_stream -> byte_stream)) option) -> unitAdd a callback for every request. The callback can provide a stream transformer and a new request (with modified headers, typically). A possible use is to handle decompression by looking for a Transfer-Encoding header and returning a stream transformer that decompresses on the fly.
val add_encode_response_cb : t -> (string Request.t -> Response.t -> Response.t option) -> unitAdd a callback for every request/response pair. Similarly to add_encode_response_cb the callback can return a new response, for example to compress it. The callback is given the fully parsed query as well as the current response.
val set_top_handler : t -> (string Request.t -> Response.t) -> unitSetup a handler called by default.
This handler is called with any request not accepted by any handler installed via add_path_handler. If no top handler is installed, unhandled paths will return a 404 not found.
val add_path_handler : ?accept:(unit Request.t -> (unit, Response_code.t * string) Pervasives.result) -> ?meth:Meth.t -> t -> ('a, Scanf.Scanning.in_channel, 'b, 'c -> string Request.t -> Response.t, 'a -> 'd, 'd) Pervasives.format6 -> 'c -> unitadd_path_handler server "/some/path/%s@/%d/" f calls f "foo" 42 request when a request with path "some/path/foo/42/" is received.
This uses Scanf's splitting, which has some gotchas (in particular, "%s" is eager, so it's generally necessary to delimit its scope with a "@/" delimiter. The "@" before a character indicates it's a separator.
Note that the handlers are called in the reverse order of their addition, so the last registered handler can override previously registered ones.
if provided, only accept requests with the given method. Typically one could react to `GET or `PUT.
should return Ok() if the given request (before its body is read) should be accepted, Error (code,message) if it's to be rejected (e.g. because its content is too big, or for some permission error). See the http_of_dir program for an example of how to use accept to filter uploads that are too large before the upload even starts.
val stop : t -> unitAsk the server to stop. This might not have an immediate effect as run might currently be waiting on IO.
val run : t -> (unit, exn) Pervasives.resultRun the main loop of the server, listening on a socket described at the server's creation time, using new_thread to start a thread for each new client.
This returns Ok () if the server exits gracefully, or Error e if it exits with an error.