diff options
author | Loïc Hoguin <[email protected]> | 2024-01-16 16:28:52 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2024-01-16 16:28:52 +0100 |
commit | defce46fdf85d16bbe3c0a8de6058334e8a53775 (patch) | |
tree | d26ddce251a9f633b7cd20684e9eb3b8e97d40d4 | |
parent | ec12c2f051f94df6f4c86175caddc0d1108254cd (diff) | |
download | cowboy-defce46fdf85d16bbe3c0a8de6058334e8a53775.tar.gz cowboy-defce46fdf85d16bbe3c0a8de6058334e8a53775.tar.bz2 cowboy-defce46fdf85d16bbe3c0a8de6058334e8a53775.zip |
REST: Allow generate_etag to return undefined
This allows conditionally generating an etag.
-rw-r--r-- | doc/src/manual/cowboy_rest.asciidoc | 9 | ||||
-rw-r--r-- | src/cowboy_rest.erl | 8 | ||||
-rw-r--r-- | test/handlers/generate_etag_h.erl | 3 | ||||
-rw-r--r-- | test/rest_handler_SUITE.erl | 11 |
4 files changed, 29 insertions, 2 deletions
diff --git a/doc/src/manual/cowboy_rest.asciidoc b/doc/src/manual/cowboy_rest.asciidoc index 0bb6d47..ea6dba9 100644 --- a/doc/src/manual/cowboy_rest.asciidoc +++ b/doc/src/manual/cowboy_rest.asciidoc @@ -379,7 +379,7 @@ and that the request shouldn't be repeated. ---- generate_etag(Req, State) -> {Result, Req, State} -Result :: binary() | {weak | strong, binary()} +Result :: binary() | {weak | strong, binary()} | undefined Default - no etag value ---- @@ -389,6 +389,10 @@ When a binary is returned, the value is automatically parsed to a tuple. The binary must be in the same format as the etag header, including quotes. +It is possible to conditionally generate an etag. +When no etag can be generated, `undefined` should +be returned. + === is_authorized [source,erlang] @@ -730,6 +734,9 @@ listed here, like the authorization header. == Changelog +* *2.11*: The `generate_etag` callback can now return + `undefined` to conditionally avoid generating + an etag. * *2.9*: An `AcceptCallback` can now return `{created, URI}` or `{see_other, URI}`. The return value `{true, URI}` is deprecated. diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl index 7d0fe80..003e5f9 100644 --- a/src/cowboy_rest.erl +++ b/src/cowboy_rest.erl @@ -97,7 +97,7 @@ -optional_callbacks([forbidden/2]). -callback generate_etag(Req, State) - -> {binary() | {weak | strong, binary()}, Req, State} + -> {binary() | {weak | strong, binary()} | undefined, Req, State} when Req::cowboy_req:req(), State::any(). -optional_callbacks([generate_etag/2]). @@ -1527,6 +1527,12 @@ generate_etag(Req, State=#state{etag=undefined}) -> case unsafe_call(Req, State, generate_etag) of no_call -> {undefined, Req, State#state{etag=no_call}}; + %% We allow the callback to return 'undefined' + %% to allow conditionally generating etags. We + %% handle 'undefined' the same as if the function + %% was not exported. + {undefined, Req2, State2} -> + {undefined, Req2, State2#state{etag=no_call}}; {Etag, Req2, State2} when is_binary(Etag) -> Etag2 = cow_http_hd:parse_etag(Etag), {Etag2, Req2, State2#state{etag=Etag2}}; diff --git a/test/handlers/generate_etag_h.erl b/test/handlers/generate_etag_h.erl index 97ee82b..b9e1302 100644 --- a/test/handlers/generate_etag_h.erl +++ b/test/handlers/generate_etag_h.erl @@ -34,6 +34,9 @@ generate_etag(Req=#{qs := <<"binary-weak-unquoted">>}, State) -> generate_etag(Req=#{qs := <<"binary-strong-unquoted">>}, State) -> ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1), {<<"etag-header-value">>, Req, State}; +%% Returning 'undefined' to indicate no etag. +generate_etag(Req=#{qs := <<"undefined">>}, State) -> + {undefined, Req, State}; %% Simulate the callback being missing in other cases. generate_etag(#{qs := <<"missing">>}, _) -> no_call. diff --git a/test/rest_handler_SUITE.erl b/test/rest_handler_SUITE.erl index b082810..510098f 100644 --- a/test/rest_handler_SUITE.erl +++ b/test/rest_handler_SUITE.erl @@ -571,6 +571,17 @@ generate_etag_missing(Config) -> false = lists:keyfind(<<"etag">>, 1, Headers), ok. +generate_etag_undefined(Config) -> + doc("The etag header must not be sent when " + "the generate_etag callback returns undefined."), + ConnPid = gun_open(Config), + Ref = gun:get(ConnPid, "/generate_etag?undefined", [ + {<<"accept-encoding">>, <<"gzip">>} + ]), + {response, _, 200, Headers} = gun:await(ConnPid, Ref), + false = lists:keyfind(<<"etag">>, 1, Headers), + ok. + generate_etag_binary_strong(Config) -> doc("The etag header must be sent when the generate_etag " "callback returns a strong binary. (RFC7232 2.3)"), |