0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Don't automatically compress when response has etag

In the cowboy_compress_h stream handler.

Otherwise this could cause issues with caching, with the
etag being the same for compressed/uncompressed content.

Users that wish to send etags AND compress will have to
do it manually for the time being.
This commit is contained in:
Loïc Hoguin 2024-01-05 15:53:42 +01:00
parent 67df6fedae
commit 5b2f600036
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
4 changed files with 31 additions and 1 deletions

View file

@ -9,7 +9,7 @@ cowboy_compress_h - Compress stream handler
The module `cowboy_compress_h` compresses response bodies The module `cowboy_compress_h` compresses response bodies
automatically when the client supports it. It will not automatically when the client supports it. It will not
try to compress responses that already have a content try to compress responses that already have a content
encoding. encoding or that have an etag header defined.
Normal responses will only be compressed when their Normal responses will only be compressed when their
size is lower than the configured threshold. Streamed size is lower than the configured threshold. Streamed
@ -55,6 +55,8 @@ The compress stream handler does not produce any event.
== Changelog == Changelog
* *2.11*: Compression is now disabled when the etag
header is in the response headers.
* *2.6*: The options `compress_buffering` and * *2.6*: The options `compress_buffering` and
`compress_threshold` were added. `compress_threshold` were added.
* *2.0*: Module introduced. * *2.0*: Module introduced.

View file

@ -96,6 +96,9 @@ check_req(Req) ->
%% Do not compress responses that contain the content-encoding header. %% Do not compress responses that contain the content-encoding header.
check_resp_headers(#{<<"content-encoding">> := _}, State) -> check_resp_headers(#{<<"content-encoding">> := _}, State) ->
State#state{compress=undefined}; State#state{compress=undefined};
%% Do not compress responses that contain the etag header.
check_resp_headers(#{<<"etag">> := _}, State) ->
State#state{compress=undefined};
check_resp_headers(_, State) -> check_resp_headers(_, State) ->
State. State.

View file

@ -109,6 +109,17 @@ gzip_reply_content_encoding(Config) ->
{_, <<"100000">>} = lists:keyfind(<<"content-length">>, 1, Headers), {_, <<"100000">>} = lists:keyfind(<<"content-length">>, 1, Headers),
ok. ok.
gzip_reply_etag(Config) ->
doc("Reply with etag header; get an uncompressed response."),
{200, Headers, _} = do_get("/reply/etag",
[{<<"accept-encoding">>, <<"gzip">>}], Config),
%% We set a strong etag.
{_, <<"\"STRONK\"">>} = lists:keyfind(<<"etag">>, 1, Headers),
%% The reply didn't include a vary header.
false = lists:keyfind(<<"vary">>, 1, Headers),
{_, <<"100000">>} = lists:keyfind(<<"content-length">>, 1, Headers),
ok.
gzip_reply_large_body(Config) -> gzip_reply_large_body(Config) ->
doc("Reply a large body; get a gzipped response."), doc("Reply a large body; get a gzipped response."),
{200, Headers, GzBody} = do_get("/reply/large", {200, Headers, GzBody} = do_get("/reply/large",
@ -174,6 +185,15 @@ gzip_stream_reply_content_encoding(Config) ->
100000 = iolist_size(Body), 100000 = iolist_size(Body),
ok. ok.
gzip_stream_reply_etag(Config) ->
doc("Stream reply with etag header; get an uncompressed response."),
{200, Headers, Body} = do_get("/stream_reply/etag",
[{<<"accept-encoding">>, <<"gzip">>}], Config),
{_, <<"\"STRONK\"">>} = lists:keyfind(<<"etag">>, 1, Headers),
false = lists:keyfind(<<"vary">>, 1, Headers),
100000 = iolist_size(Body),
ok.
opts_compress_buffering_false(Config0) -> opts_compress_buffering_false(Config0) ->
doc("Confirm that the compress_buffering option can be set to false, " doc("Confirm that the compress_buffering option can be set to false, "
"which is the default."), "which is the default."),

View file

@ -19,6 +19,9 @@ init(Req0, State=reply) ->
<<"content-encoding">> -> <<"content-encoding">> ->
cowboy_req:reply(200, #{<<"content-encoding">> => <<"compress">>}, cowboy_req:reply(200, #{<<"content-encoding">> => <<"compress">>},
lists:duplicate(100000, $a), Req0); lists:duplicate(100000, $a), Req0);
<<"etag">> ->
cowboy_req:reply(200, #{<<"etag">> => <<"\"STRONK\"">>},
lists:duplicate(100000, $a), Req0);
<<"sendfile">> -> <<"sendfile">> ->
AppFile = code:where_is_file("cowboy.app"), AppFile = code:where_is_file("cowboy.app"),
Size = filelib:file_size(AppFile), Size = filelib:file_size(AppFile),
@ -34,6 +37,8 @@ init(Req0, State=stream_reply) ->
stream_reply(#{}, Req0); stream_reply(#{}, Req0);
<<"content-encoding">> -> <<"content-encoding">> ->
stream_reply(#{<<"content-encoding">> => <<"compress">>}, Req0); stream_reply(#{<<"content-encoding">> => <<"compress">>}, Req0);
<<"etag">> ->
stream_reply(#{<<"etag">> => <<"\"STRONK\"">>}, Req0);
<<"sendfile">> -> <<"sendfile">> ->
Data = lists:duplicate(10000, $a), Data = lists:duplicate(10000, $a),
AppFile = code:where_is_file("cowboy.app"), AppFile = code:where_is_file("cowboy.app"),