diff options
-rw-r--r-- | doc/src/manual/cowboy_rest.asciidoc | 9 | ||||
-rw-r--r-- | src/cowboy_rest.erl | 7 | ||||
-rw-r--r-- | test/handlers/last_modified_h.erl | 2 | ||||
-rw-r--r-- | test/rest_handler_SUITE.erl | 10 |
4 files changed, 26 insertions, 2 deletions
diff --git a/doc/src/manual/cowboy_rest.asciidoc b/doc/src/manual/cowboy_rest.asciidoc index fcef799..4fbffd2 100644 --- a/doc/src/manual/cowboy_rest.asciidoc +++ b/doc/src/manual/cowboy_rest.asciidoc @@ -483,7 +483,7 @@ req() :: #{ ---- last_modified(Req, State) -> {Result, Req, State} -Result :: calendar:datetime() +Result :: calendar:datetime() | undefined Default - no last modified value ---- @@ -493,6 +493,10 @@ This date will be used to test against the if-modified-since and if-unmodified-since headers, and sent as the last-modified header in the response to GET and HEAD requests. +When `undefined` is returned, no last-modified header is +added to response. Can be useful if you save timestamp on store +action in memory and lose it after restart. + === malformed_request [source,erlang] @@ -856,6 +860,9 @@ listed here, like the authorization header. == Changelog +* *2.14*: The `last_modified` callback is now type correct + when returning `undefined` to avoid responding + a last-modified header. * *2.11*: The `ranges_provided`, `range_satisfiable` and the `RangeCallback` callbacks have been added. * *2.11*: The `generate_etag` callback can now return diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl index 9f30fcf..7629b3e 100644 --- a/src/cowboy_rest.erl +++ b/src/cowboy_rest.erl @@ -130,7 +130,7 @@ -optional_callbacks([languages_provided/2]). -callback last_modified(Req, State) - -> {calendar:datetime(), Req, State} + -> {calendar:datetime() | undefined, Req, State} when Req::cowboy_req:req(), State::any(). -optional_callbacks([last_modified/2]). @@ -1531,6 +1531,11 @@ last_modified(Req, State=#state{last_modified=undefined}) -> case unsafe_call(Req, State, last_modified) of no_call -> {undefined, Req, State#state{last_modified=no_call}}; + %% We allow the callback to return 'undefined', + %% in which case the generated header would be missing + %% as if the callback was not called. + {undefined, Req2, State2} -> + {undefined, Req2, State2#state{last_modified=no_call}}; {LastModified, Req2, State2} -> {LastModified, Req2, State2#state{last_modified=LastModified}} end; diff --git a/test/handlers/last_modified_h.erl b/test/handlers/last_modified_h.erl index 82893b3..1b109e3 100644 --- a/test/handlers/last_modified_h.erl +++ b/test/handlers/last_modified_h.erl @@ -19,6 +19,8 @@ get_text_plain(Req, State) -> last_modified(Req=#{qs := <<"tuple">>}, State) -> {{{2012, 9, 21}, {22, 36, 14}}, Req, State}; +last_modified(Req=#{qs := <<"undefined">>}, State) -> + {undefined, Req, State}; %% Simulate the callback being missing in other cases. last_modified(#{qs := <<"missing">>}, _) -> no_call. diff --git a/test/rest_handler_SUITE.erl b/test/rest_handler_SUITE.erl index a3d9533..7a9566f 100644 --- a/test/rest_handler_SUITE.erl +++ b/test/rest_handler_SUITE.erl @@ -796,6 +796,16 @@ last_modified_missing(Config) -> false = lists:keyfind(<<"last-modified">>, 1, Headers), ok. +last_modified_undefined(Config) -> + doc("The last-modified header must not be sent when the callback returns undefined."), + ConnPid = gun_open(Config), + Ref = gun:get(ConnPid, "/last_modified?undefined", [ + {<<"accept-encoding">>, <<"gzip">>} + ]), + {response, _, 200, Headers} = gun:await(ConnPid, Ref), + false = lists:keyfind(<<"last-modified">>, 1, Headers), + ok. + options_missing(Config) -> doc("A successful OPTIONS request to a simple handler results in " "a 200 OK response with the allow header set. (RFC7231 4.3.7)"), |