Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lists in cowboy_req:set_resp_headers #1669

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions doc/src/manual/cowboy_req.set_resp_headers.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cowboy_req:set_resp_headers - Set several response headers
set_resp_headers(Headers, Req :: cowboy_req:req())
-> Req
Headers :: cowboy:http_headers()
Headers :: cowboy:http_headers() | [{binary(), iodata()}]
----

Set several headers to be sent with the response.
Expand All @@ -32,8 +32,16 @@ instead of this function to set cookies.

Headers::

Headers as a map with keys being lowercase binary strings,
and values as binary strings.
Headers as a map with names being lowercase binary strings,
and values as iodata; or as a list with the same requirements
for names and values.
+
When a list is given it is converted to its equivalent map,
with duplicate headers concatenated with a comma inserted
in-between. Support for lists is meant to simplify using
data from clients or other applications.
+
The set-cookie header must not be set using this function.

Req::

Expand All @@ -48,6 +56,7 @@ otherwise the headers will not be sent in the response.

== Changelog

* *2.13*: The function now accepts a list of headers.
* *2.0*: Function introduced.

== Examples
Expand Down
17 changes: 16 additions & 1 deletion src/cowboy_req.erl
Original file line number Diff line number Diff line change
Expand Up @@ -726,8 +726,10 @@ set_resp_header(Name, Value, Req=#{resp_headers := RespHeaders}) ->
set_resp_header(Name,Value, Req) ->
Req#{resp_headers => #{Name => Value}}.

-spec set_resp_headers(cowboy:http_headers(), Req)
-spec set_resp_headers(cowboy:http_headers() | [{binary(), iodata()}], Req)
-> Req when Req::req().
set_resp_headers(Headers, Req) when is_list(Headers) ->
set_resp_headers_list(Headers, Req, #{});
set_resp_headers(#{<<"set-cookie">> := _}, _) ->
exit({response_error, invalid_header,
'Response cookies must be set using cowboy_req:set_resp_cookie/3,4.'});
Expand All @@ -736,6 +738,19 @@ set_resp_headers(Headers, Req=#{resp_headers := RespHeaders}) ->
set_resp_headers(Headers, Req) ->
Req#{resp_headers => Headers}.

set_resp_headers_list([], Req, Acc) ->
set_resp_headers(Acc, Req);
set_resp_headers_list([{<<"set-cookie">>, _}|_], _, _) ->
exit({response_error, invalid_header,
'Response cookies must be set using cowboy_req:set_resp_cookie/3,4.'});
set_resp_headers_list([{Name, Value}|Tail], Req, Acc) ->
case Acc of
#{Name := ValueAcc} ->
set_resp_headers_list(Tail, Req, Acc#{Name => [ValueAcc, <<", ">>, Value]});
_ ->
set_resp_headers_list(Tail, Req, Acc#{Name => Value})
end.

-spec resp_header(binary(), req()) -> binary() | undefined.
resp_header(Name, Req) ->
resp_header(Name, Req, undefined).
Expand Down
13 changes: 13 additions & 0 deletions test/handlers/resp_h.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,25 @@ do(<<"set_resp_headers">>, Req0, Opts) ->
<<"content-encoding">> => <<"compress">>
}, Req0),
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
do(<<"set_resp_headers_list">>, Req0, Opts) ->
Req = cowboy_req:set_resp_headers([
{<<"content-type">>, <<"text/plain">>},
{<<"content-encoding">>, <<"compress">>}
], Req0),
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
do(<<"set_resp_headers_cookie">>, Req0, Opts) ->
ct_helper:ignore(cowboy_req, set_resp_headers, 2),
Req = cowboy_req:set_resp_headers(#{
<<"set-cookie">> => <<"name=value">>
}, Req0),
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
do(<<"set_resp_headers_list_cookie">>, Req0, Opts) ->
ct_helper:ignore(cowboy_req, set_resp_headers_list, 3),
Req = cowboy_req:set_resp_headers([
{<<"set-cookie">>, <<"name=value">>},
{<<"set-cookie">>, <<"name2=value2">>}
], Req0),
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
do(<<"set_resp_headers_http11">>, Req0, Opts) ->
Req = cowboy_req:set_resp_headers(#{
<<"connection">> => <<"custom-header, close">>,
Expand Down
10 changes: 7 additions & 3 deletions test/req_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -858,11 +858,15 @@ set_resp_header(Config) ->

set_resp_headers(Config) ->
doc("Response using set_resp_headers."),
{200, Headers, <<"OK">>} = do_get("/resp/set_resp_headers", Config),
true = lists:keymember(<<"content-type">>, 1, Headers),
true = lists:keymember(<<"content-encoding">>, 1, Headers),
{200, Headers1, <<"OK">>} = do_get("/resp/set_resp_headers", Config),
true = lists:keymember(<<"content-type">>, 1, Headers1),
true = lists:keymember(<<"content-encoding">>, 1, Headers1),
{200, Headers2, <<"OK">>} = do_get("/resp/set_resp_headers_list", Config),
true = lists:keymember(<<"content-type">>, 1, Headers2),
true = lists:keymember(<<"content-encoding">>, 1, Headers2),
%% The set-cookie header is special. set_resp_cookie must be used.
{500, _, _} = do_maybe_h3_error3(do_get("/resp/set_resp_headers_cookie", Config)),
{500, _, _} = do_maybe_h3_error3(do_get("/resp/set_resp_headers_list_cookie", Config)),
ok.

resp_header(Config) ->
Expand Down
Loading