refactor(zip): compression is now a middleware

This commit is contained in:
Simon Cruanes 2021-12-11 10:58:40 -05:00
parent 9e26576740
commit 1a88ea7d74
No known key found for this signature in database
GPG key ID: 4AC01D0849AA62B6
2 changed files with 51 additions and 33 deletions

View file

@ -2,7 +2,7 @@
module S = Tiny_httpd module S = Tiny_httpd
module BS = Tiny_httpd.Byte_stream module BS = Tiny_httpd.Byte_stream
let decode_deflate_stream_ ~buf_size () (is:S.byte_stream) : S.byte_stream = let decode_deflate_stream_ ~buf_size (is:S.byte_stream) : S.byte_stream =
S._debug (fun k->k "wrap stream with deflate.decode"); S._debug (fun k->k "wrap stream with deflate.decode");
let buf = Bytes.make buf_size ' ' in let buf = Bytes.make buf_size ' ' in
let buf_len = ref 0 in let buf_len = ref 0 in
@ -145,7 +145,8 @@ let has_deflate s =
try Scanf.sscanf s "deflate, %s" (fun _ -> true) try Scanf.sscanf s "deflate, %s" (fun _ -> true)
with _ -> false with _ -> false
let cb_decode_compressed_stream ~buf_size (req:unit S.Request.t) : _ option = (* 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 match S.Request.get_header ~f:String.trim req "Transfer-Encoding" with
(* TODO (* TODO
| Some "gzip" -> | Some "gzip" ->
@ -155,51 +156,63 @@ let cb_decode_compressed_stream ~buf_size (req:unit S.Request.t) : _ option =
| Some s when has_deflate s -> | Some s when has_deflate s ->
begin match Scanf.sscanf s "deflate, %s" (fun s -> s) with begin match Scanf.sscanf s "deflate, %s" (fun s -> s) with
| tr' -> | tr' ->
let req' = S.Request.set_header req "Transfer-Encoding" tr' in let body' = S.Request.body req |> decode_deflate_stream_ ~buf_size in
Some (req', decode_deflate_stream_ ~buf_size ()) req
| exception _ -> None |> S.Request.set_header "Transfer-Encoding" tr'
|> S.Request.set_body body'
| exception _ -> req
end end
| _ -> None | _ -> req
let cb_encode_compressed_stream let compress_resp_stream_
~compress_above ~compress_above
~buf_size (req:_ S.Request.t) (resp:S.Response.t) : _ option = ~buf_size
if accept_deflate req then ( (req:_ S.Request.t) (resp:S.Response.t) : S.Response.t =
let set_headers h =
(* headers for compressed stream *)
let update_headers h =
h h
|> S.Headers.remove "Content-Length" |> S.Headers.remove "Content-Length"
|> S.Headers.set "Content-Encoding" "deflate" |> S.Headers.set "Content-Encoding" "deflate"
in in
if accept_deflate req then (
match resp.body with match resp.body with
| `String s when String.length s > compress_above -> | `String s when String.length s > compress_above ->
(* big string, we compress *)
S._debug S._debug
(fun k->k "encode str response with deflate (size %d, threshold %d)" (fun k->k "encode str response with deflate (size %d, threshold %d)"
(String.length s) compress_above); (String.length s) compress_above);
let body = let body =
encode_deflate_stream_ ~buf_size @@ S.Byte_stream.of_string s encode_deflate_stream_ ~buf_size @@ S.Byte_stream.of_string s
in in
Some { resp
resp with |> S.Response.update_headers update_headers
headers=set_headers resp.headers; body=`Stream body; |> S.Response.set_body (`Stream body)
}
| `Stream str -> | `Stream str ->
S._debug (fun k->k "encode stream response with deflate"); S._debug (fun k->k "encode stream response with deflate");
Some { resp
resp with |> S.Response.update_headers update_headers
headers= set_headers resp.headers; |> S.Response.set_body (`Stream (encode_deflate_stream_ ~buf_size str))
body=`Stream (encode_deflate_stream_ ~buf_size str);
}
| `String _ | `Void -> None
) else None
(* TODO: as middleware *) | `String _ | `Void -> resp
) else resp
let middleware
?(compress_above=16 * 1024)
?(buf_size=16 * 1_024)
() : S.Middleware.t =
let buf_size = max buf_size 1_024 in
fun h req ~resp ->
let req = decompress_req_stream_ ~buf_size req in
h req
~resp:(fun response ->
resp @@ compress_resp_stream_ ~buf_size ~compress_above req response)
let setup let setup
?(compress_above=500*1024) ?compress_above ?buf_size server =
?(buf_size=16 * 1_024) (server:S.t) : unit = let m = middleware ?compress_above ?buf_size () in
let buf_size = max buf_size 1_024 in S._debug (fun k->k "setup gzip support");
S._debug (fun k->k "setup gzip support (buf-size %d)" buf_size); S.add_middleware ~stage:`Encoding server m
S.add_decode_request_cb server (cb_decode_compressed_stream ~buf_size);
S.add_encode_response_cb server (cb_encode_compressed_stream ~compress_above ~buf_size);
()

View file

@ -1,8 +1,13 @@
val middleware :
?compress_above:int ->
?buf_size:int -> unit ->
Tiny_httpd.Middleware.t
val setup : val setup :
?compress_above:int -> ?compress_above:int ->
?buf_size:int -> Tiny_httpd.t -> unit ?buf_size:int -> Tiny_httpd.t -> unit
(** Install callbacks for tiny_httpd to be able to encode/decode (** Install middleware for tiny_httpd to be able to encode/decode
compressed streams compressed streams
@param compress_above threshold above with string responses are compressed @param compress_above threshold above with string responses are compressed
@param buf_size size of the underlying buffer for compression/decompression *) @param buf_size size of the underlying buffer for compression/decompression *)