aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/manual/cowboy_rest.asciidoc9
-rw-r--r--src/cowboy_rest.erl7
-rw-r--r--test/handlers/last_modified_h.erl2
-rw-r--r--test/rest_handler_SUITE.erl10
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)"),