Skip to content

Commit

Permalink
Use a single header for Access-Control-Expose-Headers
Browse files Browse the repository at this point in the history
This fixes the case where more than one header is exposed.
Previously, only one header would be listed due to the way cowboy
handles multiple response headers with the same name.

If we had more control over the reply, we could perhaps add multiple
headers (due to how cowboy_req:reply/4 works).  However, this approach
is much simpler and appears to be more consistent with its use in the
wild.
  • Loading branch information
danielwhite committed Nov 2, 2013
1 parent 428293d commit 09c1a24
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
15 changes: 12 additions & 3 deletions src/cowboy_cors.erl
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ exposed_headers(Req, State) ->

set_exposed_headers(Req, []) ->
Req;
set_exposed_headers(Req, [Header|Tail]) ->
Req1 = cowboy_req:set_resp_header(<<"access-control-expose-headers">>, Header, Req),
set_exposed_headers(Req1, Tail).
set_exposed_headers(Req, Headers) ->
Bin = header_list(Headers),
cowboy_req:set_resp_header(<<"access-control-expose-headers">>, Bin, Req).

%% allow_credentials/1 should return true or false.
allow_credentials(Req, State) ->
Expand Down Expand Up @@ -195,3 +195,12 @@ terminate(Req, #state{env = Env}) ->
-spec error_terminate(cowboy_req:req(), #state{}) -> no_return().
error_terminate(_Req, _State) ->
erlang:throw({?MODULE, error}).

%% create a comma-separated list for a header value
header_list(Values) ->
header_list(Values, <<>>).

header_list([Value], Acc) ->
<<Acc/binary, Value/binary>>;
header_list([Value | Rest], Acc) ->
header_list(Rest, <<Acc/binary, Value/binary, ",">>).
17 changes: 17 additions & 0 deletions test/cors_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
-export([standard_options/1]).
-export([simple_allowed_get/1]).
-export([simple_allowed_credentials_get/1]).
-export([simple_exposed_headers/1]).
-export([actual_options/1]).
-export([preflight_method/1]).
-export([preflight_allowed_method/1]).
Expand Down Expand Up @@ -46,6 +47,7 @@ groups() ->
standard_options,
simple_allowed_get,
simple_allowed_credentials_get,
simple_exposed_headers,
actual_options,
preflight_method,
preflight_allowed_method,
Expand Down Expand Up @@ -168,6 +170,21 @@ simple_allowed_credentials_get(Config) ->
{_, Origin} = lists:keyfind(<<"access-control-allow-origin">>, 1, Headers),
{_, <<"true">>} = lists:keyfind(<<"access-control-allow-credentials">>, 1, Headers).

simple_exposed_headers(Config) ->
Origin = <<"http://example.com">>,
Exposed = [<<"x-first">>, <<"x-second">>],
{ok, 204, Headers, _} =
request(<<"GET">>,
[{<<"Origin">>, Origin}],
[{allowed_origins, Origin},
{allowed_methods, <<"GET">>},
{exposed_headers, Exposed}],
Config),
{_, Origin} = lists:keyfind(<<"access-control-allow-origin">>, 1, Headers),
{_, ExposedList} = lists:keyfind(<<"access-control-expose-headers">>, 1, Headers),
Exposed = cowboy_http:nonempty_list(ExposedList, fun cowboy_http:token/2),
false = lists:keyfind(<<"access-control-allow-credentials">>, 1, Headers).

actual_options(Config) ->
%% OPTIONS request without Access-Control-Request-Method is not a pre-flight request.
Origin = <<"http://example.com">>,
Expand Down

0 comments on commit 09c1a24

Please sign in to comment.