diff --git a/dune-project b/dune-project index 468a58dd..370ef50a 100644 --- a/dune-project +++ b/dune-project @@ -36,5 +36,6 @@ (depends (tiny_httpd (= :version)) (camlzip (>= 1.06)) + iostream-camlzip (logs :with-test) (odoc :with-doc))) diff --git a/src/camlzip/Tiny_httpd_camlzip.ml b/src/camlzip/Tiny_httpd_camlzip.ml index 7d390211..4e8f5172 100644 --- a/src/camlzip/Tiny_httpd_camlzip.ml +++ b/src/camlzip/Tiny_httpd_camlzip.ml @@ -1,175 +1,59 @@ -module S = Tiny_httpd_server -module BS = Tiny_httpd_stream -module W = Tiny_httpd_io.Writer -module Out = Tiny_httpd_io.Output -module Log = Tiny_httpd.Log +module W = IO.Writer -let decode_deflate_stream_ ~buf_size (is : S.byte_stream) : S.byte_stream = +(* TODO: just use iostream-camlzip? *) + +let decode_deflate_stream_ ~buf_size (ic : IO.Input.t) : IO.Input.t = Log.debug (fun k -> k "wrap stream with deflate.decode"); - let zlib_str = Zlib.inflate_init false in - let is_done = ref false in - BS.make ~bs:(Bytes.create buf_size) - ~close:(fun _ -> - Zlib.inflate_end zlib_str; - BS.close is) - ~consume:(fun self len -> - if len > self.len then - S.Response.fail_raise ~code:400 - "inflate: error during decompression: invalid consume len %d (max %d)" - len self.len; - self.off <- self.off + len; - self.len <- self.len - len) - ~fill:(fun self -> - (* refill [buf] if needed *) - if self.len = 0 && not !is_done then ( - is.fill_buf (); - (try - let finished, used_in, used_out = - Zlib.inflate zlib_str self.bs 0 (Bytes.length self.bs) is.bs is.off - is.len Zlib.Z_SYNC_FLUSH - in - is.consume used_in; - self.off <- 0; - self.len <- used_out; - if finished then is_done := true; - Log.debug (fun k -> - k "decode %d bytes as %d bytes from inflate (finished: %b)" - used_in used_out finished) - with Zlib.Error (e1, e2) -> - S.Response.fail_raise ~code:400 - "inflate: error during decompression:\n%s %s" e1 e2); - Log.debug (fun k -> - k "inflate: refill %d bytes into internal buf" self.len) - )) - () + Iostream_camlzip.decompress_in_buf ~buf_size ic let encode_deflate_writer_ ~buf_size (w : W.t) : W.t = Log.debug (fun k -> k "wrap writer with deflate.encode"); - let zlib_str = Zlib.deflate_init 4 false in - let o_buf = Bytes.create buf_size in - let o_off = ref 0 in - let o_len = ref 0 in - - (* write output buffer to out *) - let write_out (oc : Out.t) = - if !o_len > 0 then ( - Out.output oc o_buf !o_off !o_len; - o_off := 0; - o_len := 0 - ) + let { IO.Writer.write } = w in + let write' (oc : IO.Output.t) = + let oc' = Iostream_camlzip.compressed_out ~buf_size ~level:4 oc in + write (oc' :> IO.Output.t) in + IO.Writer.make ~write:write' () - let flush_zlib ~flush (oc : Out.t) = - let continue = ref true in - while !continue do - let finished, used_in, used_out = - Zlib.deflate zlib_str Bytes.empty 0 0 o_buf 0 (Bytes.length o_buf) flush - in - assert (used_in = 0); - o_len := !o_len + used_out; - if finished then continue := false; - write_out oc - done - in - - (* compress and consume input buffer *) - let write_zlib ~flush (oc : Out.t) buf i len = - let i = ref i in - let len = ref len in - while !len > 0 do - let _finished, used_in, used_out = - Zlib.deflate zlib_str buf !i !len o_buf 0 (Bytes.length o_buf) flush - in - i := !i + used_in; - len := !len - used_in; - o_len := !o_len + used_out; - write_out oc - done - in - - let write (oc : Out.t) : unit = - let output buf i len = write_zlib ~flush:Zlib.Z_NO_FLUSH oc buf i len in - - let bchar = Bytes.create 1 in - let output_char c = - Bytes.set bchar 0 c; - output bchar 0 1 - in - - let flush () = - flush_zlib oc ~flush:Zlib.Z_FINISH; - assert (!o_len = 0); - oc.flush () - in - let close () = - flush (); - Zlib.deflate_end zlib_str; - oc.close () - in - (* new output channel that compresses on the fly *) - let oc' = { Out.flush; close; output; output_char } in - w.write oc'; - oc'.close () - in - - W.make ~write () - -let split_on_char ?(f = fun x -> x) c s : string list = - let rec loop acc i = - match String.index_from s i c with - | exception Not_found -> - let acc = - if i = String.length s then - acc - else - f (String.sub s i (String.length s - i)) :: acc - in - List.rev acc - | j -> - let acc = f (String.sub s i (j - i)) :: acc in - loop acc (j + 1) - in - loop [] 0 - -let accept_deflate (req : _ S.Request.t) = - match S.Request.get_header req "Accept-Encoding" with - | Some s -> List.mem "deflate" @@ split_on_char ~f:String.trim ',' s +let accept_deflate (req : _ Request.t) = + match Request.get_header req "Accept-Encoding" with + | Some s -> + List.mem "deflate" @@ List.rev_map String.trim @@ String.split_on_char ',' s | None -> false let has_deflate s = try Scanf.sscanf s "deflate, %s" (fun _ -> true) with _ -> false (* decompress [req]'s body if needed *) -let decompress_req_stream_ ~buf_size (req : BS.t S.Request.t) : _ S.Request.t = - match S.Request.get_header ~f:String.trim req "Transfer-Encoding" with +let decompress_req_stream_ ~buf_size (req : IO.Input.t Request.t) : _ Request.t + = + match Request.get_header ~f:String.trim req "Transfer-Encoding" with (* TODO | Some "gzip" -> let req' = S.Request.set_header req "Transfer-Encoding" "chunked" in Some (req', decode_gzip_stream_) *) | Some "deflate" -> - let body' = S.Request.body req |> decode_deflate_stream_ ~buf_size in - req - |> S.Request.remove_header "Transfer-Encoding" - |> S.Request.set_body body' + let body' = Request.body req |> decode_deflate_stream_ ~buf_size in + req |> Request.remove_header "Transfer-Encoding" |> Request.set_body body' | Some s when has_deflate s -> (match Scanf.sscanf s "deflate, %s" (fun s -> s) with | tr' -> - let body' = S.Request.body req |> decode_deflate_stream_ ~buf_size in + let body' = Request.body req |> decode_deflate_stream_ ~buf_size in req - |> S.Request.set_header "Transfer-Encoding" tr' - |> S.Request.set_body body' + |> Request.set_header "Transfer-Encoding" tr' + |> Request.set_body body' | exception _ -> req) | _ -> req -let compress_resp_stream_ ~compress_above ~buf_size (req : _ S.Request.t) - (resp : S.Response.t) : S.Response.t = +let compress_resp_stream_ ~compress_above ~buf_size (req : _ Request.t) + (resp : Response.t) : Response.t = (* headers for compressed stream *) let update_headers h = h - |> S.Headers.remove "Content-Length" - |> S.Headers.set "Content-Encoding" "deflate" + |> Headers.remove "Content-Length" + |> Headers.set "Content-Encoding" "deflate" in if accept_deflate req then ( @@ -181,25 +65,25 @@ let compress_resp_stream_ ~compress_above ~buf_size (req : _ S.Request.t) (String.length s) compress_above); let body = encode_deflate_writer_ ~buf_size @@ W.of_string s in resp - |> S.Response.update_headers update_headers - |> S.Response.set_body (`Writer body) - | `Stream str -> + |> Response.update_headers update_headers + |> Response.set_body (`Writer body) + | `Stream ic -> Log.debug (fun k -> k "encode stream response with deflate"); - let w = BS.to_writer str in + let w = IO.Writer.of_input ic in resp - |> S.Response.update_headers update_headers - |> S.Response.set_body (`Writer (encode_deflate_writer_ ~buf_size w)) + |> Response.update_headers update_headers + |> Response.set_body (`Writer (encode_deflate_writer_ ~buf_size w)) | `Writer w -> Log.debug (fun k -> k "encode writer response with deflate"); resp - |> S.Response.update_headers update_headers - |> S.Response.set_body (`Writer (encode_deflate_writer_ ~buf_size w)) + |> Response.update_headers update_headers + |> Response.set_body (`Writer (encode_deflate_writer_ ~buf_size w)) | `String _ | `Void -> resp ) else resp let middleware ?(compress_above = 16 * 1024) ?(buf_size = 16 * 1_024) () : - S.Middleware.t = + Server.Middleware.t = let buf_size = max buf_size 1_024 in fun h req ~resp -> let req = decompress_req_stream_ ~buf_size req in @@ -209,4 +93,4 @@ let middleware ?(compress_above = 16 * 1024) ?(buf_size = 16 * 1_024) () : let setup ?compress_above ?buf_size server = let m = middleware ?compress_above ?buf_size () in Log.info (fun k -> k "setup gzip middleware"); - S.add_middleware ~stage:`Encoding server m + Server.add_middleware ~stage:`Encoding server m diff --git a/src/camlzip/Tiny_httpd_camlzip.mli b/src/camlzip/Tiny_httpd_camlzip.mli index 2fb7f570..f098e6da 100644 --- a/src/camlzip/Tiny_httpd_camlzip.mli +++ b/src/camlzip/Tiny_httpd_camlzip.mli @@ -7,7 +7,7 @@ *) val middleware : - ?compress_above:int -> ?buf_size:int -> unit -> Tiny_httpd_server.Middleware.t + ?compress_above:int -> ?buf_size:int -> unit -> Server.Middleware.t (** Middleware responsible for deflate compression/decompression. @param compress_above threshold, in bytes, above which a response body that has a known content-length is compressed. Stream bodies @@ -15,7 +15,7 @@ val middleware : @param buf_size size of the underlying buffer for compression/decompression @since 0.11 *) -val setup : ?compress_above:int -> ?buf_size:int -> Tiny_httpd_server.t -> unit +val setup : ?compress_above:int -> ?buf_size:int -> Server.t -> unit (** Install middleware for tiny_httpd to be able to encode/decode compressed streams @param compress_above threshold above with string responses are compressed diff --git a/src/camlzip/dune b/src/camlzip/dune index 6ffc109f..d7192304 100644 --- a/src/camlzip/dune +++ b/src/camlzip/dune @@ -2,5 +2,5 @@ (name tiny_httpd_camlzip) (public_name tiny_httpd_camlzip) (synopsis "A wrapper around camlzip to bring compression to Tiny_httpd") - (flags :standard -safe-string -warn-error -a+8) - (libraries tiny_httpd camlzip)) + (flags :standard -open Tiny_httpd_core -safe-string -warn-error -a+8) + (libraries tiny_httpd.core iostream-camlzip camlzip)) diff --git a/src/core/IO.ml b/src/core/IO.ml index 5130d14f..afcdc8df 100644 --- a/src/core/IO.ml +++ b/src/core/IO.ml @@ -109,7 +109,7 @@ module Input = struct (fd : Unix.file_descr) : t = let eof = ref false in object - inherit Iostream.In_buf.t_from_refill ~buf () + inherit Iostream.In_buf.t_from_refill ~bytes:buf.bytes () method private refill (slice : Slice.t) = if not !eof then ( @@ -137,7 +137,7 @@ module Input = struct if slice.len = 0 then eof := true ) - method! close () = + method close () = if not !closed then ( closed := true; eof := true; @@ -150,13 +150,13 @@ module Input = struct let of_slice (slice : Slice.t) : t = object - inherit Iostream.In_buf.t_from_refill ~buf:slice () + inherit Iostream.In_buf.t_from_refill ~bytes:slice.bytes () method private refill (slice : Slice.t) = slice.off <- 0; slice.len <- 0 - method! close () = () + method close () = () end (** Read into the given slice. @@ -198,7 +198,7 @@ module Input = struct slice.off <- 0; input_rec slice - method! close () = + method close () = close i1; close i2 end @@ -225,10 +225,10 @@ module Input = struct Stdlib.output oc slice.bytes slice.off slice.len) self - let to_chan' (oc : #Iostream.Out_buf.t) (self : #t) : unit = + let to_chan' (oc : #Iostream.Out.t) (self : #t) : unit = iter_slice (fun (slice : Slice.t) -> - Iostream.Out_buf.output oc slice.bytes slice.off slice.len) + Iostream.Out.output oc slice.bytes slice.off slice.len) self let read_all_using ~buf (self : #t) : string = @@ -299,11 +299,10 @@ module Input = struct @param close_rec if true, closing this will also close the input stream *) let limit_size_to ~close_rec ~max_size ~(bytes : bytes) (arg : t) : t = let remaining_size = ref max_size in - let slice = Slice.of_bytes bytes in object - inherit Iostream.In_buf.t_from_refill ~buf:slice () - method! close () = if close_rec then close arg + inherit Iostream.In_buf.t_from_refill ~bytes () + method close () = if close_rec then close arg method private refill slice = if slice.len = 0 then @@ -324,12 +323,11 @@ module Input = struct @param close_rec if true, closing this will also close the input stream *) let reading_exactly ~close_rec ~size ~(bytes : bytes) (arg : t) : t = let remaining_size = ref size in - let slice = Slice.of_bytes bytes in object - inherit Iostream.In_buf.t_from_refill ~buf:slice () + inherit Iostream.In_buf.t_from_refill ~bytes () - method! close () = + method close () = if !remaining_size > 0 then skip arg !remaining_size; if close_rec then close arg @@ -346,9 +344,11 @@ module Input = struct ) end - let read_chunked ~(buf : Slice.t) ~fail (bs : #t) : t = + let read_chunked ~(bytes : bytes) ~fail (bs : #t) : t = let first = ref true in - let line_buf = Buf.create ~size:128 () in + + (* small buffer to read the chunk sizes *) + let line_buf = Buf.create ~size:32 () in let read_next_chunk_len () : int = if !first then first := false @@ -377,7 +377,7 @@ module Input = struct let chunk_size = ref 0 in object - inherit t_from_refill ~buf () + inherit t_from_refill ~bytes () method private refill (slice : Slice.t) : unit = if !chunk_size = 0 && not !eof then chunk_size := read_next_chunk_len (); @@ -395,10 +395,7 @@ module Input = struct (* stream is finished *) eof := true - method! close () = - (* do not close underlying stream *) - eof := true; - () + method close () = eof := true (* do not close underlying stream *) end (** Output a stream using chunked encoding *) @@ -435,14 +432,15 @@ module Writer = struct let[@inline] make ~write () : t = { write } (** Write into the channel. *) - let[@inline] write (oc : Output.t) (self : t) : unit = self.write oc + let[@inline] write (oc : #Output.t) (self : t) : unit = + self.write (oc :> Output.t) (** Empty writer, will output 0 bytes. *) let empty : t = { write = ignore } (** A writer that just emits the bytes from the given string. *) let[@inline] of_string (str : string) : t = - let write oc = Output.output_string oc str in + let write oc = Iostream.Out.output_string oc str in { write } let[@inline] of_input (ic : #Input.t) : t = diff --git a/src/core/buf.ml b/src/core/buf.ml index fcc89933..5824946f 100644 --- a/src/core/buf.ml +++ b/src/core/buf.ml @@ -4,6 +4,7 @@ let create ?(size = 4_096) () : t = let bytes = Bytes.make size ' ' in { bytes; i = 0; original = bytes } +let of_bytes bytes : t = { bytes; i = 0; original = bytes } let[@inline] size self = self.i let[@inline] bytes_slice self = self.bytes diff --git a/src/core/buf.mli b/src/core/buf.mli index e5ca90c1..dbbbb7ca 100644 --- a/src/core/buf.mli +++ b/src/core/buf.mli @@ -11,6 +11,7 @@ type t val size : t -> int val clear : t -> unit val create : ?size:int -> unit -> t +val of_bytes : bytes -> t val contents : t -> string val clear_and_zero : t -> unit diff --git a/src/core/request.ml b/src/core/request.ml index 1a5c19b7..df0cd1e0 100644 --- a/src/core/request.ml +++ b/src/core/request.ml @@ -24,6 +24,9 @@ let start_time self = self.start_time let query self = self.query let get_header ?f self h = Headers.get ?f h self.headers let remove_header k self = { self with headers = Headers.remove k self.headers } +let add_meta self k v = self.meta <- Hmap.add k v self.meta +let get_meta self k = Hmap.find k self.meta +let get_meta_exn self k = Hmap.get k self.meta let get_header_int self h = match get_header self h with @@ -64,9 +67,9 @@ let pp out self : unit = self.body pp_comp_ self.path_components pp_query self.query (* decode a "chunked" stream into a normal stream *) -let read_stream_chunked_ ~buf (bs : #IO.Input.t) : IO.Input.t = +let read_stream_chunked_ ~bytes (bs : #IO.Input.t) : IO.Input.t = Log.debug (fun k -> k "body: start reading chunked stream..."); - IO.Input.read_chunked ~buf ~fail:(fun s -> Bad_req (400, s)) bs + IO.Input.read_chunked ~bytes ~fail:(fun s -> Bad_req (400, s)) bs let limit_body_size_ ~max_size ~bytes (bs : #IO.Input.t) : IO.Input.t = Log.debug (fun k -> k "limit size of body to max-size=%d" max_size); @@ -146,8 +149,8 @@ let parse_req_start ~client_addr ~get_time_s ~buf (bs : IO.Input.t) : (* parse body, given the headers. @param tr_stream a transformation of the input stream. *) -let parse_body_ ~tr_stream ~buf (req : IO.Input.t t) : IO.Input.t t resp_result - = +let parse_body_ ~tr_stream ~bytes (req : IO.Input.t t) : + IO.Input.t t resp_result = try let size = match Headers.get_exn "Content-Length" req.headers |> int_of_string with @@ -162,7 +165,9 @@ let parse_body_ ~tr_stream ~buf (req : IO.Input.t t) : IO.Input.t t resp_result read_exactly ~size ~bytes @@ tr_stream req.body | Some "chunked" -> (* body sent by chunks *) - let bs : IO.Input.t = read_stream_chunked_ ~buf @@ tr_stream req.body in + let bs : IO.Input.t = + read_stream_chunked_ ~bytes @@ tr_stream req.body + in if size > 0 then ( let bytes = Bytes.create 4096 in limit_body_size_ ~max_size:size ~bytes bs @@ -176,11 +181,11 @@ let parse_body_ ~tr_stream ~buf (req : IO.Input.t t) : IO.Input.t t resp_result | Bad_req (c, s) -> Error (c, s) | e -> Error (400, Printexc.to_string e) -let read_body_full ?buf ?buf_size (self : IO.Input.t t) : string t = +let read_body_full ?bytes ?buf_size (self : IO.Input.t t) : string t = try let buf = - match buf with - | Some b -> b + match bytes with + | Some b -> Buf.of_bytes b | None -> Buf.create ?size:buf_size () in let body = IO.Input.read_all_using ~buf self.body in @@ -196,8 +201,8 @@ module Private_ = struct let parse_req_start_exn ?(buf = Buf.create ()) ~client_addr ~get_time_s bs = parse_req_start ~client_addr ~get_time_s ~buf bs |> unwrap_resp_result - let parse_body ?(buf = IO.Slice.create 4096) req bs : _ t = - parse_body_ ~tr_stream:(fun s -> s) ~buf { req with body = bs } + let parse_body ?(bytes = Bytes.create 4096) req bs : _ t = + parse_body_ ~tr_stream:(fun s -> s) ~bytes { req with body = bs } |> unwrap_resp_result let[@inline] set_body body self = { self with body } diff --git a/src/core/request.mli b/src/core/request.mli index cf4f8368..731cfe69 100644 --- a/src/core/request.mli +++ b/src/core/request.mli @@ -36,6 +36,19 @@ type 'body t = private { @since 0.11 the field [start_time] was added *) +val add_meta : _ t -> 'a Hmap.key -> 'a -> unit +(** Add metadata + @since NEXT_RELEASE *) + +val get_meta : _ t -> 'a Hmap.key -> 'a option +(** Get metadata + @since NEXT_RELEASE *) + +val get_meta_exn : _ t -> 'a Hmap.key -> 'a +(** Like {!get_meta} but can fail + @raise Invalid_argument if not present + @since NEXT_RELEASE *) + val pp : Format.formatter -> string t -> unit (** Pretty print the request and its body. The exact format of this printing is not specified. *) @@ -102,11 +115,11 @@ val limit_body_size : @since 0.3 *) -val read_body_full : ?buf:Buf.t -> ?buf_size:int -> IO.Input.t t -> string t +val read_body_full : ?bytes:bytes -> ?buf_size:int -> IO.Input.t t -> string t (** Read the whole body into a string. Potentially blocking. @param buf_size initial size of underlying buffer (since 0.11) - @param buf the initial buffer (since 0.14) + @param bytes the initial buffer (since 0.14) *) (**/**) @@ -128,7 +141,7 @@ module Private_ : sig unit t option val close_after_req : _ t -> bool - val parse_body : ?buf:IO.Slice.t -> unit t -> IO.Input.t -> IO.Input.t t + val parse_body : ?bytes:bytes -> unit t -> IO.Input.t -> IO.Input.t t val set_body : 'a -> _ t -> 'a t end diff --git a/src/core/response.ml b/src/core/response.ml index 1bd7af66..3e2d4347 100644 --- a/src/core/response.ml +++ b/src/core/response.ml @@ -76,12 +76,12 @@ let pp out self : unit = Format.fprintf out "{@[code=%d;@ headers=[@[%a@]];@ body=%a@]}" self.code Headers.pp self.headers pp_body self.body -let output_ ~buf (oc : IO.Output.t) (self : t) : unit = +let output_ ~bytes (oc : IO.Output.t) (self : t) : unit = (* double indirection: - print into [buffer] using [bprintf] - transfer to [buf_] so we can output from there *) let tmp_buffer = Buffer.create 32 in - Buf.clear buf; + let buf = Buf.of_bytes bytes in (* write start of reply *) Printf.bprintf tmp_buffer "HTTP/1.1 %d %s\r\n" self.code diff --git a/src/core/response.mli b/src/core/response.mli index 610faed5..6ddf08b8 100644 --- a/src/core/response.mli +++ b/src/core/response.mli @@ -112,7 +112,7 @@ val pp : Format.formatter -> t -> unit module Private_ : sig val make_void_force_ : ?headers:Headers.t -> code:int -> unit -> t - val output_ : buf:Buf.t -> IO.Output.t -> t -> unit + val output_ : bytes:Bytes.t -> IO.Output.t -> t -> unit end (**/**) diff --git a/src/core/server.ml b/src/core/server.ml index d36173ae..e24bed89 100644 --- a/src/core/server.ml +++ b/src/core/server.ml @@ -72,7 +72,6 @@ let unwrap_handler_result req = function type t = { backend: (module IO_BACKEND); mutable tcp_server: IO.TCP_server.t option; - buf_size: int; mutable handler: IO.Input.t Request.t -> Response.t; (** toplevel handler, if any *) mutable middlewares: (int * Middleware.t) list; (** Global middlewares *) @@ -80,7 +79,7 @@ type t = { (** sorted version of {!middlewares} *) mutable path_handlers: (unit Request.t -> handler_result option) list; (** path handlers *) - buf_pool: Buf.t Pool.t; + bytes_pool: bytes Pool.t; } let addr (self : t) = @@ -169,8 +168,8 @@ let add_route_handler (type a) ?accept ?middlewares ?meth self (route : (a, _) Route.t) (f : _) : unit = let tr_req _oc req ~resp f = let req = - Pool.with_resource self.buf_pool @@ fun buf -> - Request.read_body_full ~buf req + Pool.with_resource self.bytes_pool @@ fun bytes -> + Request.read_body_full ~bytes req in resp (f req) in @@ -190,8 +189,8 @@ exception Exit_SSE let add_route_server_sent_handler ?accept self route f = let tr_req (oc : IO.Output.t) req ~resp f = let req = - Pool.with_resource self.buf_pool @@ fun buf -> - Request.read_body_full ~buf req + Pool.with_resource self.bytes_pool @@ fun bytes -> + Request.read_body_full ~bytes req in let headers = ref Headers.(empty |> set "content-type" "text/event-stream") @@ -256,6 +255,8 @@ let add_upgrade_handler ?(accept = fun _ -> Ok ()) (self : t) route f : unit = in self.path_handlers <- ph :: self.path_handlers +let clear_bytes_ bs = Bytes.fill bs 0 (Bytes.length bs) '\x00' + let create_from ?(buf_size = 16 * 1_024) ?(middlewares = []) ~backend () : t = let handler _req = Response.fail ~code:404 "no top handler" in let self = @@ -263,13 +264,12 @@ let create_from ?(buf_size = 16 * 1_024) ?(middlewares = []) ~backend () : t = backend; tcp_server = None; handler; - buf_size; path_handlers = []; middlewares = []; middlewares_sorted = lazy []; - buf_pool = - Pool.create ~clear:Buf.clear_and_zero - ~mk_item:(fun () -> Buf.create ~size:buf_size ()) + bytes_pool = + Pool.create ~clear:clear_bytes_ + ~mk_item:(fun () -> Bytes.create buf_size) (); } in @@ -302,8 +302,8 @@ let string_as_list_contains_ (s : string) (sub : string) : bool = (* handle client on [ic] and [oc] *) let client_handle_for (self : t) ~client_addr ic oc : unit = - Pool.with_resource self.buf_pool @@ fun buf -> - Pool.with_resource self.buf_pool @@ fun buf_res -> + Pool.with_resource self.bytes_pool @@ fun bytes_req -> + Pool.with_resource self.bytes_pool @@ fun bytes_res -> let (module B) = self.backend in (* how to log the response to this query *) @@ -336,7 +336,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = let msg = Printexc.to_string e in let resp = Response.fail ~code:500 "server error: %s" msg in if not Log.dummy then log_exn msg bt; - Response.Private_.output_ ~buf:buf_res oc resp + Response.Private_.output_ ~bytes:bytes_res oc resp in let handle_bad_req req e bt = @@ -346,7 +346,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = log_exn msg bt; log_response req resp ); - Response.Private_.output_ ~buf:buf_res oc resp + Response.Private_.output_ ~bytes:bytes_res oc resp in let handle_upgrade req (module UP : UPGRADE_HANDLER) : unit = @@ -368,7 +368,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = Log.error (fun k -> k "upgrade failed: %s" msg); let resp = Response.make_raw ~code:429 "upgrade required" in log_response req resp; - Response.Private_.output_ ~buf:buf_res oc resp + Response.Private_.output_ ~bytes:bytes_res oc resp | Ok (headers, handshake_st) -> (* send the upgrade reply *) let headers = @@ -376,7 +376,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = in let resp = Response.make_string ~code:101 ~headers (Ok "") in log_response req resp; - Response.Private_.output_ ~buf:buf_res oc resp; + Response.Private_.output_ ~bytes:bytes_res oc resp; UP.handle_connection client_addr handshake_st ic oc with e -> @@ -388,6 +388,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = let handle_one_req () = match + let buf = Buf.of_bytes bytes_req in Request.Private_.parse_req_start ~client_addr ~get_time_s:B.get_time_s ~buf ic with @@ -395,7 +396,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = | Error (c, s) -> (* connection error, close *) let res = Response.make_raw ~code:c s in - (try Response.Private_.output_ ~buf:buf_res oc res + (try Response.Private_.output_ ~bytes:bytes_res oc res with Sys_error _ -> ()); continue := false | Ok (Some req) -> @@ -418,7 +419,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = (match Request.get_header ~f:String.trim req "Expect" with | Some "100-continue" -> Log.debug (fun k -> k "send back: 100 CONTINUE"); - Response.Private_.output_ ~buf:buf_res oc + Response.Private_.output_ ~bytes:bytes_res oc (Response.make_raw ~code:100 "") | Some s -> bad_reqf 417 "unknown expectation %s" s | None -> ()); @@ -432,11 +433,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = in (* now actually read request's body into a stream *) - let req = - Request.Private_.parse_body - ~buf:(IO.Slice.of_bytes (Buf.bytes_slice buf)) - req ic - in + let req = Request.Private_.parse_body ~bytes:bytes_req req ic in (* how to reply *) let resp r = @@ -444,7 +441,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = if Headers.get "connection" r.Response.headers = Some "close" then continue := false; log_response req r; - Response.Private_.output_ ~buf:buf_res oc r + Response.Private_.output_ ~bytes:bytes_res oc r with Sys_error e -> Log.debug (fun k -> k "error when writing response: %s@.connection broken" e); @@ -466,7 +463,7 @@ let client_handle_for (self : t) ~client_addr ic oc : unit = continue := false; let resp = Response.make_raw ~code s in log_response req resp; - Response.Private_.output_ ~buf:buf_res oc resp + Response.Private_.output_ ~bytes:bytes_res oc resp | Upgrade _ as e -> raise e | e -> let bt = Printexc.get_raw_backtrace () in diff --git a/src/html/Tiny_httpd_html.ml b/src/html/Tiny_httpd_html.ml index 8ed797f6..0b1ac3f9 100644 --- a/src/html/Tiny_httpd_html.ml +++ b/src/html/Tiny_httpd_html.ml @@ -14,12 +14,11 @@ include Html_ be a "html" tag. @since 0.14 *) -let to_output ?(top = false) (self : elt) (out : IO.Output.t) : unit = +let to_output ?(top = false) (self : elt) (out : #IO.Output.t) : unit = let out = Out.create_of_out out in if top then Out.add_string out "\n"; self out; - Out.add_format_nl out; - Out.flush out + Out.add_format_nl out (** Convert a HTML element to a string. @param top if true, add DOCTYPE at the beginning. The top element should then @@ -54,7 +53,7 @@ let to_out_channel_top = to_output ~top:true @param top if true, add a DOCTYPE. See {!to_out_channel}. @since 0.14 *) let to_writer ?top (self : elt) : IO.Writer.t = - let write oc = to_output ?top self oc in + let write (oc : #IO.Output.t) = to_output ?top self oc in IO.Writer.make ~write () (** Convert a HTML element to a stream. This might just convert diff --git a/src/ws/tiny_httpd_ws.ml b/src/ws/tiny_httpd_ws.ml index 94c0decb..3917ef0e 100644 --- a/src/ws/tiny_httpd_ws.ml +++ b/src/ws/tiny_httpd_ws.ml @@ -380,13 +380,13 @@ let upgrade ic oc : _ * _ = let reader = Reader.create ~ic ~writer () in let ws_ic : IO.Input.t = object - inherit IO.Input.t_from_refill () + inherit IO.Input.t_from_refill ~bytes:(Bytes.create 4_096) () method private refill (slice : IO.Slice.t) = slice.off <- 0; slice.len <- Reader.read reader slice.bytes 0 (Bytes.length slice.bytes) - method! close () = Reader.close reader + method close () = Reader.close reader end in let ws_oc : IO.Output.t = diff --git a/vendor/iostream b/vendor/iostream index bb03b78f..668a7c22 160000 --- a/vendor/iostream +++ b/vendor/iostream @@ -1 +1 @@ -Subproject commit bb03b78fab03e9eb7186c45d33d23735650351ec +Subproject commit 668a7c22c09d21293c9ce3fd8bc66b3080c525d2