Skip to content

Commit c7ff329

Browse files
committed
Add support for lists in cowboy_req:set_resp_headers
This is meant to be used with clients such as Gun to simplify proxying and similar operations. The set-cookie header must not be set this way so there is still some extra processing to be done to fully translate a Gun response into a Cowboy response.
1 parent f316a65 commit c7ff329

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

doc/src/manual/cowboy_req.set_resp_headers.asciidoc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ cowboy_req:set_resp_headers - Set several response headers
1111
set_resp_headers(Headers, Req :: cowboy_req:req())
1212
-> Req
1313
14-
Headers :: cowboy:http_headers()
14+
Headers :: cowboy:http_headers() | [{binary(), iodata()}]
1515
----
1616

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

3333
Headers::
3434

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

3846
Req::
3947

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

4957
== Changelog
5058

59+
* *2.13*: The function now accepts a list of headers.
5160
* *2.0*: Function introduced.
5261

5362
== Examples

src/cowboy_req.erl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,8 +726,10 @@ set_resp_header(Name, Value, Req=#{resp_headers := RespHeaders}) ->
726726
set_resp_header(Name,Value, Req) ->
727727
Req#{resp_headers => #{Name => Value}}.
728728

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

741+
set_resp_headers_list([], Req, Acc) ->
742+
set_resp_headers(Acc, Req);
743+
set_resp_headers_list([{<<"set-cookie">>, _}|_], _, _) ->
744+
exit({response_error, invalid_header,
745+
'Response cookies must be set using cowboy_req:set_resp_cookie/3,4.'});
746+
set_resp_headers_list([{Name, Value}|Tail], Req, Acc) ->
747+
case Acc of
748+
#{Name := ValueAcc} ->
749+
set_resp_headers_list(Tail, Req, Acc#{Name => [ValueAcc, <<", ">>, Value]});
750+
_ ->
751+
set_resp_headers_list(Tail, Req, Acc#{Name => Value})
752+
end.
753+
739754
-spec resp_header(binary(), req()) -> binary() | undefined.
740755
resp_header(Name, Req) ->
741756
resp_header(Name, Req, undefined).

test/handlers/resp_h.erl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,25 @@ do(<<"set_resp_headers">>, Req0, Opts) ->
4343
<<"content-encoding">> => <<"compress">>
4444
}, Req0),
4545
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
46+
do(<<"set_resp_headers_list">>, Req0, Opts) ->
47+
Req = cowboy_req:set_resp_headers([
48+
{<<"content-type">>, <<"text/plain">>},
49+
{<<"content-encoding">>, <<"compress">>}
50+
], Req0),
51+
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
4652
do(<<"set_resp_headers_cookie">>, Req0, Opts) ->
4753
ct_helper:ignore(cowboy_req, set_resp_headers, 2),
4854
Req = cowboy_req:set_resp_headers(#{
4955
<<"set-cookie">> => <<"name=value">>
5056
}, Req0),
5157
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
58+
do(<<"set_resp_headers_list_cookie">>, Req0, Opts) ->
59+
ct_helper:ignore(cowboy_req, set_resp_headers_list, 3),
60+
Req = cowboy_req:set_resp_headers([
61+
{<<"set-cookie">>, <<"name=value">>},
62+
{<<"set-cookie">>, <<"name2=value2">>}
63+
], Req0),
64+
{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
5265
do(<<"set_resp_headers_http11">>, Req0, Opts) ->
5366
Req = cowboy_req:set_resp_headers(#{
5467
<<"connection">> => <<"custom-header, close">>,

test/req_SUITE.erl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -858,11 +858,14 @@ set_resp_header(Config) ->
858858

859859
set_resp_headers(Config) ->
860860
doc("Response using set_resp_headers."),
861-
{200, Headers, <<"OK">>} = do_get("/resp/set_resp_headers", Config),
862-
true = lists:keymember(<<"content-type">>, 1, Headers),
863-
true = lists:keymember(<<"content-encoding">>, 1, Headers),
861+
{200, Headers1, <<"OK">>} = do_get("/resp/set_resp_headers", Config),
862+
true = lists:keymember(<<"content-type">>, 1, Headers1),
863+
true = lists:keymember(<<"content-encoding">>, 1, Headers1),
864+
{200, Headers2, <<"OK">>} = do_get("/resp/set_resp_headers_list", Config),
865+
true = lists:sort(Headers1) =:= lists:sort(Headers2),
864866
%% The set-cookie header is special. set_resp_cookie must be used.
865867
{500, _, _} = do_maybe_h3_error3(do_get("/resp/set_resp_headers_cookie", Config)),
868+
{500, _, _} = do_maybe_h3_error3(do_get("/resp/set_resp_headers_list_cookie", Config)),
866869
ok.
867870

868871
resp_header(Config) ->

0 commit comments

Comments
 (0)