mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Rework and improve the decompress stream handler
The read buffer was changed into an iovec to avoid doing too many binary concatenations and allocations. Decompression happens transparently: when decoding gzip, the content-encoding header is removed (we only decode when "gzip" is the only encoding so nothing remains). We always add a content_decoded key to the Req object. This key contains a list of codings that were decoded, in the reverse order in which they were. Currently it can only be empty or contain <<"gzip">> but future improvements or user handlers may see it contain more values. The option to disable decompression was renamed to decompress_enabled and defaults to true. It is no longer possible to enable/disable decompression in the middle of reading the body: this ensures that the data we pass forward is always valid. Various smaller improvements were made to the code, tests and manual pages.
This commit is contained in:
parent
3ed1b24dd6
commit
fd9711d949
6 changed files with 361 additions and 203 deletions
|
@ -5,10 +5,10 @@
|
|||
|
||||
-export([init/2]).
|
||||
|
||||
init(Req0, State=[]) ->
|
||||
init(Req0, State=echo) ->
|
||||
case cowboy_req:binding(what, Req0) of
|
||||
<<"decompress_ignore">> ->
|
||||
cowboy_req:cast({set_options, #{decompress_ignore => true}}, Req0);
|
||||
<<"decompress_disable">> ->
|
||||
cowboy_req:cast({set_options, #{decompress_enabled => false}}, Req0);
|
||||
<<"decompress_ratio_limit">> ->
|
||||
cowboy_req:cast({set_options, #{decompress_ratio_limit => 0.5}}, Req0);
|
||||
<<"normal">> -> ok
|
||||
|
@ -16,47 +16,63 @@ init(Req0, State=[]) ->
|
|||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{}, Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=header_command) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req2 = cowboy_req:stream_reply(200, #{}, Req1),
|
||||
Req = cowboy_req:stream_body(Body, fin, Req2),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=accept_identity) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<"identity">>}, Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=invalid_header) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<";">>}, Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=reject_explicit_header) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<"identity, gzip;q=0">>},
|
||||
Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=reject_implicit_header) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<"identity, *;q=0">>},
|
||||
Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=accept_explicit_header) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<"identity, gzip;q=0.5">>},
|
||||
Body, Req1),
|
||||
{ok, Req, State};
|
||||
|
||||
init(Req0, State=accept_implicit_header) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:reply(200, #{<<"accept-encoding">> => <<"identity, *;q=0.5">>},
|
||||
Body, Req1),
|
||||
init(Req0, State=test) ->
|
||||
Req = test(Req0, cowboy_req:binding(what, Req0)),
|
||||
{ok, Req, State}.
|
||||
|
||||
test(Req, <<"content-encoding">>) ->
|
||||
cowboy_req:reply(200, #{},
|
||||
cowboy_req:header(<<"content-encoding">>, Req, <<"undefined">>),
|
||||
Req);
|
||||
test(Req, <<"content-decoded">>) ->
|
||||
cowboy_req:reply(200, #{},
|
||||
io_lib:format("~0p", [maps:get(content_decoded, Req, undefined)]),
|
||||
Req);
|
||||
test(Req0, <<"disable-in-the-middle">>) ->
|
||||
{Status, Data, Req1} = cowboy_req:read_body(Req0, #{length => 1000}),
|
||||
cowboy_req:cast({set_options, #{decompress_enabled => false}}, Req1),
|
||||
{ok, Body, Req} = do_read_body(Status, Req1, Data),
|
||||
cowboy_req:reply(200, #{}, Body, Req);
|
||||
test(Req0, <<"enable-in-the-middle">>) ->
|
||||
{Status, Data, Req1} = cowboy_req:read_body(Req0, #{length => 1000}),
|
||||
cowboy_req:cast({set_options, #{decompress_enabled => true}}, Req1),
|
||||
{ok, Body, Req} = do_read_body(Status, Req1, Data),
|
||||
cowboy_req:reply(200, #{}, Body, Req);
|
||||
test(Req0, <<"header-command">>) ->
|
||||
{ok, Body, Req1} = read_body(Req0),
|
||||
Req = cowboy_req:stream_reply(200, #{}, Req1),
|
||||
cowboy_req:stream_body(Body, fin, Req);
|
||||
test(Req0, <<"accept-identity">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<"identity">>},
|
||||
Body, Req);
|
||||
test(Req0, <<"invalid-header">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<";">>},
|
||||
Body, Req);
|
||||
test(Req0, <<"reject-explicit-header">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<"identity, gzip;q=0">>},
|
||||
Body, Req);
|
||||
test(Req0, <<"reject-implicit-header">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<"identity, *;q=0">>},
|
||||
Body, Req);
|
||||
test(Req0, <<"accept-explicit-header">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<"identity, gzip;q=0.5">>},
|
||||
Body, Req);
|
||||
test(Req0, <<"accept-implicit-header">>) ->
|
||||
{ok, Body, Req} = read_body(Req0),
|
||||
cowboy_req:reply(200,
|
||||
#{<<"accept-encoding">> => <<"identity, *;q=0.5">>},
|
||||
Body, Req).
|
||||
|
||||
read_body(Req0) ->
|
||||
{Status, Data, Req} = cowboy_req:read_body(Req0, #{length => 1000}),
|
||||
do_read_body(Status, Req, Data).
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue