mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-06 11:15:35 -05:00
improve docs and opam, tidy up for 0.1; rename stream
This commit is contained in:
parent
8bfcf3673a
commit
20275c164f
5 changed files with 67 additions and 63 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
type stream = {
|
type byte_stream = {
|
||||||
is_fill_buf: unit -> (bytes * int * int);
|
bs_fill_buf: unit -> (bytes * int * int);
|
||||||
is_consume: int -> unit;
|
bs_consume: int -> unit;
|
||||||
is_close: unit -> unit;
|
bs_close: unit -> unit;
|
||||||
}
|
}
|
||||||
(** A buffer input stream, with a view into the current buffer (or refill if empty),
|
(** A buffer input stream, with a view into the current buffer (or refill if empty),
|
||||||
and a function to consume [n] bytes *)
|
and a function to consume [n] bytes *)
|
||||||
|
|
@ -54,23 +54,23 @@ module Buf_ = struct
|
||||||
x
|
x
|
||||||
end
|
end
|
||||||
|
|
||||||
module Stream_ = struct
|
module Byte_stream = struct
|
||||||
type t = stream
|
type t = byte_stream
|
||||||
|
|
||||||
let close self = self.is_close()
|
let close self = self.bs_close()
|
||||||
|
|
||||||
let of_chan_ ~close ic : t =
|
let of_chan_ ~close ic : t =
|
||||||
let i = ref 0 in
|
let i = ref 0 in
|
||||||
let len = ref 0 in
|
let len = ref 0 in
|
||||||
let buf = Bytes.make 4096 ' ' in
|
let buf = Bytes.make 4096 ' ' in
|
||||||
{ is_fill_buf=(fun () ->
|
{ bs_fill_buf=(fun () ->
|
||||||
if !i >= !len then (
|
if !i >= !len then (
|
||||||
i := 0;
|
i := 0;
|
||||||
len := input ic buf 0 (Bytes.length buf);
|
len := input ic buf 0 (Bytes.length buf);
|
||||||
);
|
);
|
||||||
buf, !i,!len - !i);
|
buf, !i,!len - !i);
|
||||||
is_consume=(fun n -> i := !i + n);
|
bs_consume=(fun n -> i := !i + n);
|
||||||
is_close=(fun () -> close ic)
|
bs_close=(fun () -> close ic)
|
||||||
}
|
}
|
||||||
|
|
||||||
let of_chan = of_chan_ ~close:close_in
|
let of_chan = of_chan_ ~close:close_in
|
||||||
|
|
@ -85,9 +85,9 @@ module Stream_ = struct
|
||||||
)
|
)
|
||||||
in
|
in
|
||||||
let i = ref i in
|
let i = ref i in
|
||||||
{ is_fill_buf=(fun () -> s, !i, !len);
|
{ bs_fill_buf=(fun () -> s, !i, !len);
|
||||||
is_close=(fun () -> ());
|
bs_close=(fun () -> ());
|
||||||
is_consume=(fun n -> i := !i + n; len := !len - n);
|
bs_consume=(fun n -> i := !i + n; len := !len - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
let with_file file f =
|
let with_file file f =
|
||||||
|
|
@ -102,10 +102,10 @@ module Stream_ = struct
|
||||||
|
|
||||||
(* Read as much as possible into [buf]. *)
|
(* Read as much as possible into [buf]. *)
|
||||||
let read_into_buf (self:t) (buf:Buf_.t) : int =
|
let read_into_buf (self:t) (buf:Buf_.t) : int =
|
||||||
let s, i, len = self.is_fill_buf () in
|
let s, i, len = self.bs_fill_buf () in
|
||||||
if len > 0 then (
|
if len > 0 then (
|
||||||
Buf_.add_bytes buf s i len;
|
Buf_.add_bytes buf s i len;
|
||||||
self.is_consume len;
|
self.bs_consume len;
|
||||||
);
|
);
|
||||||
len
|
len
|
||||||
|
|
||||||
|
|
@ -125,11 +125,11 @@ module Stream_ = struct
|
||||||
let offset = ref 0 in
|
let offset = ref 0 in
|
||||||
while !offset < n do
|
while !offset < n do
|
||||||
let n_read =
|
let n_read =
|
||||||
let s, i, len = self.is_fill_buf () in
|
let s, i, len = self.bs_fill_buf () in
|
||||||
let n_read = min len (n- !offset) in
|
let n_read = min len (n- !offset) in
|
||||||
Bytes.blit s i bytes !offset n_read;
|
Bytes.blit s i bytes !offset n_read;
|
||||||
offset := !offset + n_read;
|
offset := !offset + n_read;
|
||||||
self.is_consume n_read;
|
self.bs_consume n_read;
|
||||||
n_read
|
n_read
|
||||||
in
|
in
|
||||||
if n_read=0 then too_short();
|
if n_read=0 then too_short();
|
||||||
|
|
@ -140,7 +140,7 @@ module Stream_ = struct
|
||||||
Buf_.clear buf;
|
Buf_.clear buf;
|
||||||
let continue = ref true in
|
let continue = ref true in
|
||||||
while !continue do
|
while !continue do
|
||||||
let s, i, len = self.is_fill_buf () in
|
let s, i, len = self.bs_fill_buf () in
|
||||||
if len=0 then continue := false;
|
if len=0 then continue := false;
|
||||||
let j = ref i in
|
let j = ref i in
|
||||||
while !j < i+len && Bytes.get s !j <> '\n' do
|
while !j < i+len && Bytes.get s !j <> '\n' do
|
||||||
|
|
@ -149,11 +149,11 @@ module Stream_ = struct
|
||||||
if !j-i < len then (
|
if !j-i < len then (
|
||||||
assert (Bytes.get s !j = '\n');
|
assert (Bytes.get s !j = '\n');
|
||||||
Buf_.add_bytes buf s i (!j-i); (* without \n *)
|
Buf_.add_bytes buf s i (!j-i); (* without \n *)
|
||||||
self.is_consume (!j-i+1); (* remove \n *)
|
self.bs_consume (!j-i+1); (* remove \n *)
|
||||||
continue := false
|
continue := false
|
||||||
) else (
|
) else (
|
||||||
Buf_.add_bytes buf s i len;
|
Buf_.add_bytes buf s i len;
|
||||||
self.is_consume len;
|
self.bs_consume len;
|
||||||
)
|
)
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -237,9 +237,9 @@ module Headers = struct
|
||||||
let pp_pair out (k,v) = Format.fprintf out "@[<h>%s: %s@]" k v in
|
let pp_pair out (k,v) = Format.fprintf out "@[<h>%s: %s@]" k v in
|
||||||
Format.fprintf out "@[<v>%a@]" (Format.pp_print_list pp_pair) l
|
Format.fprintf out "@[<v>%a@]" (Format.pp_print_list pp_pair) l
|
||||||
|
|
||||||
let parse_ ~buf (is:stream) : t =
|
let parse_ ~buf (bs:byte_stream) : t =
|
||||||
let rec loop acc =
|
let rec loop acc =
|
||||||
let line = Stream_.read_line ~buf is in
|
let line = Byte_stream.read_line ~buf bs in
|
||||||
_debug (fun k->k "parsed header line %S" line);
|
_debug (fun k->k "parsed header line %S" line);
|
||||||
if line = "\r" then (
|
if line = "\r" then (
|
||||||
acc
|
acc
|
||||||
|
|
@ -283,16 +283,16 @@ module Request = struct
|
||||||
(Meth.to_string self.meth) self.host Headers.pp self.headers
|
(Meth.to_string self.meth) self.host Headers.pp self.headers
|
||||||
self.path self.body
|
self.path self.body
|
||||||
|
|
||||||
let read_body_exact (is:stream) (n:int) : string =
|
let read_body_exact (bs:byte_stream) (n:int) : string =
|
||||||
let bytes = Bytes.make n ' ' in
|
let bytes = Bytes.make n ' ' in
|
||||||
Stream_.read_exactly_ is bytes n
|
Byte_stream.read_exactly_ bs bytes n
|
||||||
~too_short:(fun () -> bad_reqf 400 "body is too short");
|
~too_short:(fun () -> bad_reqf 400 "body is too short");
|
||||||
Bytes.unsafe_to_string bytes
|
Bytes.unsafe_to_string bytes
|
||||||
|
|
||||||
(* decode a "chunked" stream into a normal stream *)
|
(* decode a "chunked" stream into a normal stream *)
|
||||||
let read_stream_chunked_ ?(buf=Buf_.create()) (is:stream) : stream =
|
let read_stream_chunked_ ?(buf=Buf_.create()) (bs:byte_stream) : byte_stream =
|
||||||
let read_next_chunk_len () : int =
|
let read_next_chunk_len () : int =
|
||||||
let line = Stream_.read_line ~buf is in
|
let line = Byte_stream.read_line ~buf bs in
|
||||||
(* parse chunk length, ignore extensions *)
|
(* parse chunk length, ignore extensions *)
|
||||||
let chunk_size = (
|
let chunk_size = (
|
||||||
if String.trim line = "" then 0
|
if String.trim line = "" then 0
|
||||||
|
|
@ -307,7 +307,7 @@ module Request = struct
|
||||||
let offset = ref 0 in
|
let offset = ref 0 in
|
||||||
let len = ref 0 in
|
let len = ref 0 in
|
||||||
let chunk_size = ref 0 in
|
let chunk_size = ref 0 in
|
||||||
{ is_fill_buf=
|
{ bs_fill_buf=
|
||||||
(fun () ->
|
(fun () ->
|
||||||
(* do we need to refill? *)
|
(* do we need to refill? *)
|
||||||
if !offset >= !len then (
|
if !offset >= !len then (
|
||||||
|
|
@ -319,9 +319,9 @@ module Request = struct
|
||||||
if !chunk_size > 0 then (
|
if !chunk_size > 0 then (
|
||||||
(* read the whole chunk, or [Bytes.length bytes] of it *)
|
(* read the whole chunk, or [Bytes.length bytes] of it *)
|
||||||
let to_read = min !chunk_size (Bytes.length bytes) in
|
let to_read = min !chunk_size (Bytes.length bytes) in
|
||||||
Stream_.read_exactly_
|
Byte_stream.read_exactly_
|
||||||
~too_short:(fun () -> bad_reqf 400 "chunk is too short")
|
~too_short:(fun () -> bad_reqf 400 "chunk is too short")
|
||||||
is bytes to_read;
|
bs bytes to_read;
|
||||||
len := to_read;
|
len := to_read;
|
||||||
chunk_size := !chunk_size - to_read;
|
chunk_size := !chunk_size - to_read;
|
||||||
) else (
|
) else (
|
||||||
|
|
@ -330,17 +330,17 @@ module Request = struct
|
||||||
);
|
);
|
||||||
bytes, !offset, !len
|
bytes, !offset, !len
|
||||||
);
|
);
|
||||||
is_consume=(fun n -> offset := !offset + n);
|
bs_consume=(fun n -> offset := !offset + n);
|
||||||
is_close=(fun () -> Stream_.close is);
|
bs_close=(fun () -> Byte_stream.close bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let read_body_chunked ~tr_stream ~buf ~size:max_size (is:stream) : string =
|
let read_body_chunked ~tr_stream ~buf ~size:max_size (bs:byte_stream) : string =
|
||||||
_debug (fun k->k "read body with chunked encoding (max-size: %d)" max_size);
|
_debug (fun k->k "read body with chunked encoding (max-size: %d)" max_size);
|
||||||
let is = tr_stream @@ read_stream_chunked_ ~buf is in
|
let is = tr_stream @@ read_stream_chunked_ ~buf bs in
|
||||||
let buf_res = Buf_.create() in (* store the accumulated chunks *)
|
let buf_res = Buf_.create() in (* store the accumulated chunks *)
|
||||||
(* TODO: extract this as a function [read_all_up_to ~max_size is]? *)
|
(* TODO: extract this as a function [read_all_up_to ~max_size is]? *)
|
||||||
let rec read_chunks () =
|
let rec read_chunks () =
|
||||||
let n = Stream_.read_into_buf is buf_res in
|
let n = Byte_stream.read_into_buf is buf_res in
|
||||||
if n = 0 then (
|
if n = 0 then (
|
||||||
Buf_.contents buf_res (* done *)
|
Buf_.contents buf_res (* done *)
|
||||||
) else (
|
) else (
|
||||||
|
|
@ -356,16 +356,16 @@ module Request = struct
|
||||||
read_chunks()
|
read_chunks()
|
||||||
|
|
||||||
(* parse request, but not body (yet) *)
|
(* parse request, but not body (yet) *)
|
||||||
let parse_req_start ~buf (is:stream) : unit t option resp_result =
|
let parse_req_start ~buf (bs:byte_stream) : unit t option resp_result =
|
||||||
try
|
try
|
||||||
let line = Stream_.read_line ~buf is in
|
let line = Byte_stream.read_line ~buf bs in
|
||||||
let meth, path =
|
let meth, path =
|
||||||
try Scanf.sscanf line "%s %s HTTP/1.1\r" (fun x y->x,y)
|
try Scanf.sscanf line "%s %s HTTP/1.1\r" (fun x y->x,y)
|
||||||
with _ -> raise (Bad_req (400, "Invalid request line"))
|
with _ -> raise (Bad_req (400, "Invalid request line"))
|
||||||
in
|
in
|
||||||
let meth = Meth.of_string meth in
|
let meth = Meth.of_string meth in
|
||||||
_debug (fun k->k "got meth: %s, path %S" (Meth.to_string meth) path);
|
_debug (fun k->k "got meth: %s, path %S" (Meth.to_string meth) path);
|
||||||
let headers = Headers.parse_ ~buf is in
|
let headers = Headers.parse_ ~buf bs in
|
||||||
let host =
|
let host =
|
||||||
try List.assoc "Host" headers
|
try List.assoc "Host" headers
|
||||||
with Not_found -> bad_reqf 400 "No 'Host' header in request"
|
with Not_found -> bad_reqf 400 "No 'Host' header in request"
|
||||||
|
|
@ -379,7 +379,7 @@ module Request = struct
|
||||||
|
|
||||||
(* parse body, given the headers.
|
(* parse body, given the headers.
|
||||||
@param tr_stream a transformation of the input stream. *)
|
@param tr_stream a transformation of the input stream. *)
|
||||||
let parse_body_ ~tr_stream ~buf (req:stream t) : string t resp_result =
|
let parse_body_ ~tr_stream ~buf (req:byte_stream t) : string t resp_result =
|
||||||
try
|
try
|
||||||
let size =
|
let size =
|
||||||
match List.assoc "Content-Length" req.headers |> int_of_string with
|
match List.assoc "Content-Length" req.headers |> int_of_string with
|
||||||
|
|
@ -401,9 +401,9 @@ module Request = struct
|
||||||
| e ->
|
| e ->
|
||||||
Error (400, Printexc.to_string e)
|
Error (400, Printexc.to_string e)
|
||||||
|
|
||||||
let read_body_full (self:stream t) : string t =
|
let read_body_full (self:byte_stream t) : string t =
|
||||||
try
|
try
|
||||||
let body = Stream_.read_all self.body in
|
let body = Byte_stream.read_all self.body in
|
||||||
{ self with body }
|
{ self with body }
|
||||||
with
|
with
|
||||||
| Bad_req _ as e -> raise e
|
| Bad_req _ as e -> raise e
|
||||||
|
|
@ -411,7 +411,7 @@ module Request = struct
|
||||||
end
|
end
|
||||||
|
|
||||||
module Response = struct
|
module Response = struct
|
||||||
type body = [`String of string | `Stream of stream]
|
type body = [`String of string | `Stream of byte_stream]
|
||||||
type t = {
|
type t = {
|
||||||
code: Response_code.t;
|
code: Response_code.t;
|
||||||
headers: Headers.t;
|
headers: Headers.t;
|
||||||
|
|
@ -457,14 +457,14 @@ module Response = struct
|
||||||
self.code Headers.pp self.headers pp_body self.body
|
self.code Headers.pp self.headers pp_body self.body
|
||||||
|
|
||||||
(* print a stream as a series of chunks *)
|
(* print a stream as a series of chunks *)
|
||||||
let output_stream_chunked_ (oc:out_channel) (str:stream) : unit =
|
let output_stream_chunked_ (oc:out_channel) (str:byte_stream) : unit =
|
||||||
let continue = ref true in
|
let continue = ref true in
|
||||||
while !continue do
|
while !continue do
|
||||||
(* next chunk *)
|
(* next chunk *)
|
||||||
let s, i, len = str.is_fill_buf () in
|
let s, i, len = str.bs_fill_buf () in
|
||||||
Printf.fprintf oc "%x\r\n" len;
|
Printf.fprintf oc "%x\r\n" len;
|
||||||
output oc s i len;
|
output oc s i len;
|
||||||
str.is_consume len;
|
str.bs_consume len;
|
||||||
if len = 0 then (
|
if len = 0 then (
|
||||||
continue := false;
|
continue := false;
|
||||||
);
|
);
|
||||||
|
|
@ -523,7 +523,8 @@ type t = {
|
||||||
masksigpipe: bool;
|
masksigpipe: bool;
|
||||||
mutable handler: (string Request.t -> Response.t);
|
mutable handler: (string Request.t -> Response.t);
|
||||||
mutable path_handlers : (unit Request.t -> cb_path_handler resp_result option) list;
|
mutable path_handlers : (unit Request.t -> cb_path_handler resp_result option) list;
|
||||||
mutable cb_decode_req: (unit Request.t -> (unit Request.t * (stream -> stream)) option) list;
|
mutable cb_decode_req:
|
||||||
|
(unit Request.t -> (unit Request.t * (byte_stream -> byte_stream)) option) list;
|
||||||
mutable cb_encode_resp: (string Request.t -> Response.t -> Response.t option) list;
|
mutable cb_encode_resp: (string Request.t -> Response.t -> Response.t option) list;
|
||||||
mutable running: bool;
|
mutable running: bool;
|
||||||
}
|
}
|
||||||
|
|
@ -583,7 +584,7 @@ let handle_client_ (self:t) (client_sock:Unix.file_descr) : unit =
|
||||||
let ic = Unix.in_channel_of_descr client_sock in
|
let ic = Unix.in_channel_of_descr client_sock in
|
||||||
let oc = Unix.out_channel_of_descr client_sock in
|
let oc = Unix.out_channel_of_descr client_sock in
|
||||||
let buf = Buf_.create() in
|
let buf = Buf_.create() in
|
||||||
let is = Stream_.of_chan ic in
|
let is = Byte_stream.of_chan ic in
|
||||||
let continue = ref true in
|
let continue = ref true in
|
||||||
while !continue && self.running do
|
while !continue && self.running do
|
||||||
_debug (fun k->k "read next request");
|
_debug (fun k->k "read next request");
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@
|
||||||
features by declaring a few endpoints, including one for uploading files:
|
features by declaring a few endpoints, including one for uploading files:
|
||||||
|
|
||||||
{[
|
{[
|
||||||
|
module S = Tiny_httpd
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
let server = S.create () in
|
let server = S.create () in
|
||||||
(* say hello *)
|
(* say hello *)
|
||||||
S.add_path_handler ~meth:`GET server
|
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 *)
|
(* echo request *)
|
||||||
S.add_path_handler server
|
S.add_path_handler server
|
||||||
"/echo" (fun req -> S.Response.make_string
|
"/echo" (fun req -> S.Response.make_string
|
||||||
|
|
@ -84,24 +86,24 @@ end
|
||||||
Streams are used to represent a series of bytes that can arrive progressively.
|
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. *)
|
For example, an uploaded file will be sent as a series of chunks. *)
|
||||||
|
|
||||||
type stream = {
|
type byte_stream = {
|
||||||
is_fill_buf: unit -> (bytes * int * int);
|
bs_fill_buf: unit -> (bytes * int * int);
|
||||||
(** See the current slice of the internal buffer as [bytes, i, len],
|
(** See the current slice of the internal buffer as [bytes, i, len],
|
||||||
where the slice is [bytes[i] .. [bytes[i+len-1]]].
|
where the slice is [bytes[i] .. [bytes[i+len-1]]].
|
||||||
Can block to refill the buffer if there is currently no content.
|
Can block to refill the buffer if there is currently no content.
|
||||||
If [len=0] then there is no more data. *)
|
If [len=0] then there is no more data. *)
|
||||||
is_consume: int -> unit;
|
bs_consume: int -> unit;
|
||||||
(** Consume n bytes from the buffer. This should only be called with [n <= len]
|
(** Consume n bytes from the buffer. This should only be called with [n <= len]
|
||||||
after a call to [is_fill_buf] that returns a slice of length [len]. *)
|
after a call to [is_fill_buf] that returns a slice of length [len]. *)
|
||||||
is_close: unit -> unit;
|
bs_close: unit -> unit;
|
||||||
(** Close the stream. *)
|
(** Close the stream. *)
|
||||||
}
|
}
|
||||||
(** A buffered stream, with a view into the current buffer (or refill if empty),
|
(** A buffered stream, with a view into the current buffer (or refill if empty),
|
||||||
and a function to consume [n] bytes.
|
and a function to consume [n] bytes.
|
||||||
See {!Stream_} for more details. *)
|
See {!Byte_stream} for more details. *)
|
||||||
|
|
||||||
module Stream_ : sig
|
module Byte_stream : sig
|
||||||
type t = stream
|
type t = byte_stream
|
||||||
|
|
||||||
val close : t -> unit
|
val close : t -> unit
|
||||||
|
|
||||||
|
|
@ -218,7 +220,7 @@ module Request : sig
|
||||||
val body : 'b t -> 'b
|
val body : 'b t -> 'b
|
||||||
(** Request body, possibly empty. *)
|
(** Request body, possibly empty. *)
|
||||||
|
|
||||||
val read_body_full : stream t -> string t
|
val read_body_full : byte_stream t -> string t
|
||||||
(** Read the whole body into a string. Potentially blocking. *)
|
(** Read the whole body into a string. Potentially blocking. *)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -244,7 +246,7 @@ end
|
||||||
(** {2 Response} *)
|
(** {2 Response} *)
|
||||||
|
|
||||||
module Response : sig
|
module Response : sig
|
||||||
type body = [`String of string | `Stream of stream]
|
type body = [`String of string | `Stream of byte_stream]
|
||||||
(** Body of a response, either as a simple string,
|
(** Body of a response, either as a simple string,
|
||||||
or a stream of bytes. *)
|
or a stream of bytes. *)
|
||||||
|
|
||||||
|
|
@ -266,7 +268,7 @@ module Response : sig
|
||||||
val make_raw_stream :
|
val make_raw_stream :
|
||||||
?headers:Headers.t ->
|
?headers:Headers.t ->
|
||||||
code:Response_code.t ->
|
code:Response_code.t ->
|
||||||
stream ->
|
byte_stream ->
|
||||||
t
|
t
|
||||||
(** Same as {!make_raw} but with a stream body. The body will be sent with
|
(** Same as {!make_raw} but with a stream body. The body will be sent with
|
||||||
the chunked transfer-encoding. *)
|
the chunked transfer-encoding. *)
|
||||||
|
|
@ -288,7 +290,7 @@ module Response : sig
|
||||||
|
|
||||||
val make_stream :
|
val make_stream :
|
||||||
?headers:Headers.t ->
|
?headers:Headers.t ->
|
||||||
(stream, Response_code.t * string) result -> t
|
(byte_stream, Response_code.t * string) result -> t
|
||||||
(** Same as {!make} but with a stream body. *)
|
(** Same as {!make} but with a stream body. *)
|
||||||
|
|
||||||
val fail : ?headers:Headers.t -> code:int ->
|
val fail : ?headers:Headers.t -> code:int ->
|
||||||
|
|
@ -346,7 +348,7 @@ val port : t -> int
|
||||||
|
|
||||||
val add_decode_request_cb :
|
val add_decode_request_cb :
|
||||||
t ->
|
t ->
|
||||||
(unit Request.t -> (unit Request.t * (stream -> stream)) option) -> unit
|
(unit Request.t -> (unit Request.t * (byte_stream -> byte_stream)) option) -> unit
|
||||||
(** Add a callback for every request.
|
(** Add a callback for every request.
|
||||||
The callback can provide a stream transformer and a new request (with
|
The callback can provide a stream transformer and a new request (with
|
||||||
modified headers, typically).
|
modified headers, typically).
|
||||||
|
|
@ -378,7 +380,7 @@ val add_path_handler :
|
||||||
'b, 'c -> string Request.t -> Response.t, 'a -> 'd, 'd) format6 ->
|
'b, 'c -> string Request.t -> Response.t, 'a -> 'd, 'd) format6 ->
|
||||||
'c -> unit
|
'c -> unit
|
||||||
(** [add_path_handler server "/some/path/%s@/%d/" f]
|
(** [add_path_handler server "/some/path/%s@/%d/" f]
|
||||||
calls [f request "foo" 42 ()] when a request with path "some/path/foo/42/"
|
calls [f "foo" 42 request] when a request with path "some/path/foo/42/"
|
||||||
is received.
|
is received.
|
||||||
|
|
||||||
This uses {!Scanf}'s splitting, which has some gotchas (in particular,
|
This uses {!Scanf}'s splitting, which has some gotchas (in particular,
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
(public_name http_of_dir)
|
(public_name http_of_dir)
|
||||||
(package tiny_httpd)
|
(package tiny_httpd)
|
||||||
(flags :standard -warn-error -3)
|
(flags :standard -warn-error -3)
|
||||||
(libraries tiny_httpd str))
|
(libraries tiny_httpd))
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ let serve ~config (dir:string) : _ result =
|
||||||
let ic = open_in full_path in
|
let ic = open_in full_path in
|
||||||
S.Response.make_raw_stream
|
S.Response.make_raw_stream
|
||||||
~headers:["Etag", Lazy.force mtime]
|
~headers:["Etag", Lazy.force mtime]
|
||||||
~code:200 (S.Stream_.of_chan ic)
|
~code:200 (S.Byte_stream.of_chan ic)
|
||||||
with e ->
|
with e ->
|
||||||
S.Response.fail ~code:500 "error while reading file: %s" (Printexc.to_string e)
|
S.Response.fail ~code:500 "error while reading file: %s" (Printexc.to_string e)
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ version: "0.1"
|
||||||
authors: ["Simon Cruanes"]
|
authors: ["Simon Cruanes"]
|
||||||
maintainer: "simon.cruanes.2007@m4x.org"
|
maintainer: "simon.cruanes.2007@m4x.org"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
description: "Minimal HTTP server using good old threads"
|
synopsis: "Minimal HTTP server using good old threads"
|
||||||
build: [
|
build: [
|
||||||
["dune" "build" "@install" "-p" name "-j" jobs]
|
["dune" "build" "@install" "-p" name "-j" jobs]
|
||||||
["dune" "build" "@doc" "-p" name] {with-doc}
|
["dune" "build" "@doc" "-p" name] {with-doc}
|
||||||
|
|
@ -20,3 +20,4 @@ homepage: "https://github.com/c-cube/tiny_httpd/"
|
||||||
doc: "https://c-cube.github.io/tiny_httpd/"
|
doc: "https://c-cube.github.io/tiny_httpd/"
|
||||||
bug-reports: "https://github.com/c-cube/tiny_httpd/issues"
|
bug-reports: "https://github.com/c-cube/tiny_httpd/issues"
|
||||||
dev-repo: "git+https://github.com/c-cube/tiny_httpd.git"
|
dev-repo: "git+https://github.com/c-cube/tiny_httpd.git"
|
||||||
|
post-messages: "tiny http server, with blocking IOs. Also ships with a `http_of_dir` program."
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue