tiny_httpd/src/Tiny_httpd.mli
2019-11-18 19:06:12 -06:00

200 lines
5.4 KiB
OCaml

type stream = {
is_fill_buf: 'a. (bytes -> int -> int -> 'a) -> 'a;
is_consume: int -> unit;
is_close: unit -> unit;
}
(** A buffer input stream, with a view into the current buffer (or refill if empty),
and a function to consume [n] bytes *)
(** {2 Tiny buffer implementation} *)
module Buf_ : sig
type t
val size : t -> int
val clear : t -> unit
val create : ?size:int -> unit -> t
val contents : t -> string
end
(** {2 Generic stream of data} *)
module Stream_ : sig
type t = stream
val close : t -> unit
val of_chan : in_channel -> t
val of_chan_close_noerr : in_channel -> t
val of_bytes : ?i:int -> ?len:int -> bytes -> t
val with_file : string -> (t -> 'a) -> 'a
(** Open a file with given name, and obtain an input stream *)
val read_line : ?buf:Buf_.t -> t -> string
val read_all : ?buf:Buf_.t -> t -> string
end
module Meth : sig
type t = [
| `GET
| `PUT
| `POST
| `HEAD
| `DELETE
]
val pp : Format.formatter -> t -> unit
val to_string : t -> string
end
module Headers : sig
type t = (string * string) list
val get : ?f:(string->string) -> string -> t -> string option
val set : string -> string -> t -> t
val remove : string -> t -> t
val contains : string -> t -> bool
val pp : Format.formatter -> t -> unit
end
module Request : sig
type 'body t = {
meth: Meth.t;
headers: Headers.t;
path: string;
body: 'body;
}
val pp : Format.formatter -> string t -> unit
val pp_ : Format.formatter -> _ t -> unit
val headers : _ t -> Headers.t
val get_header : ?f:(string->string) -> _ t -> string -> string option
val get_header_int : _ t -> string -> int option
val set_header : 'a t -> string -> string -> 'a t
val meth : _ t -> Meth.t
val path : _ t -> string
val body : 'b t -> 'b
val read_body_full : stream t -> string t
end
module Response_code : sig
type t = int
val ok : t
val not_found : t
val descr : t -> string
end
module Response : sig
type body = [`String of string | `Stream of stream]
type t = {
code: Response_code.t;
headers: Headers.t;
body: body;
}
val make_raw :
?headers:Headers.t ->
code:Response_code.t ->
string ->
t
val make_raw_stream :
?headers:Headers.t ->
code:Response_code.t ->
stream ->
t
val make :
?headers:Headers.t ->
(body, Response_code.t * string) result -> t
val make_string :
?headers:Headers.t ->
(string, Response_code.t * string) result -> t
val make_stream :
?headers:Headers.t ->
(stream, Response_code.t * string) result -> t
val fail : ?headers:Headers.t -> code:int ->
('a, unit, string, t) format4 -> 'a
(** Make 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) format4 -> 'a
(** Similar 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 -> unit
end
type t
val create :
?masksigpipe:bool ->
?new_thread:((unit -> unit) -> unit) ->
?addr:string ->
?port:int ->
unit ->
t
(** TODO: document *)
val addr : t -> string
val port : t -> int
val add_decode_request_cb :
t ->
(unit Request.t -> (unit Request.t * (stream -> stream)) option) -> unit
(** Add a callback for every request.
The callback can provide a stream transformer and a new request (with
modified headers, typically).
*)
val add_encode_response_cb:
t -> (string Request.t -> Response.t -> Response.t option) -> unit
(** Add 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) -> unit
(** Setup a handler called by default.
If not installed, unhandled paths will return a 404 not found. *)
val add_path_handler :
?accept:(unit Request.t -> (unit, Response_code.t * string) result) ->
?meth:Meth.t ->
t ->
('a, Scanf.Scanning.in_channel,
'b, 'c -> string Request.t -> Response.t, 'a -> 'd, 'd) format6 ->
'c -> unit
(** [add_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.
@param meth if provided, only accept requests with the given method
@param accept should return [true] if the given request (before its body
is read) should be accepted, [false] if it's to be rejected (e.g. because
its content is too big, or for some permission error).
*)
val stop : t -> unit
(** Ask the server to stop. This might not have an immediate effect
as {!run} might currently be waiting on IO. *)
val run : t -> (unit, exn) result
(** Run 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. *)
(**/**)
val _debug : ((('a, out_channel, unit, unit, unit, unit) format6 -> 'a) -> unit) -> unit
val _enable_debug: bool -> unit
(**/**)