Maybe we could make Cowboy a bit more CORS friendly?
By now, to enable CORS we should work with raw headers, copy-pasting code like this from one header to another:
options(Req, State) ->
Req1 = cowboy_req:set_resp_header(<<"access-control-max-age">>, <<"1728000">>, Req0),
Req2 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"HEAD, GET, POST">>, Req1),
Req3 = cowboy_req:set_resp_header(<<"access-control-allow-headers">>, <<"content-type, authorization">>, Req2),
Req4 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<$*>>, Req3),
{ok, Req, State}.
In this way we do not actually care about allowed header's value types. For instance, "access-control-allow-methods" and "access-control-allow-headers" should only accept a list of binaries, while only a numeric value makes sense for the "access-control-max-age" header, "*" origin cannot be used for a resource that supports credentials. Ignoring this information leads to misspelling errors, breaking CORS responses and isn't flexible for working with.
My proposal is to add two new functions (set_cors_headers/2 and set_cors_preflight_headers/2) to the cowboy_req module extending functionality of set_resp_header/3.
-type cors_header()
:: {origin, binary() | list(binary())}.
-type cors_preflight_header()
:: {age, max | non_neg_integer()}
| {methods, list(binary())}
| {headers, list(binary())}
| cors_header().
-spec set_cors_headers(list(cors_header()), Req) -> Req when Req :: cowboy_req:req().
-spec set_cors_preflight_headers(list(cors_preflight_header()), Req) -> Req when Req :: cowboy_req:req().
In my projects, I'm using this simple syntax sugar in REST handlers:
allowed_methods(Req, State) ->
Req2 = cowboy_req:set_cors_headers([{origin, <<$*>>}], Req),
{[<<"HEAD">>, <<"GET">>, <<"POST">>, <<"OPTIONS">>], Req2, State}.
options(Req, State) ->
Headers =
[ {age, max},
{origin, <<$*>>},
{methods, [<<"HEAD">>, <<"GET">>, <<"POST">>]},
{headers, [<<"content-type">>, <<"authorization">>]} ],
{ok, cowboy_req:set_cors_preflight_headers(Headers, Req), State}.
It's also possible to use those functions in a middleware:
execute(Req, Env) ->
case cowboy_req:method(Req) of
<<"OPTIONS">> ->
Headers =
[ {age, max},
{origin, <<$*>>},
{methods, [<<"HEAD">>, <<"GET">>, <<"POST">>]},
{headers, [<<"content-type">>, <<"authorization">>]} ],
{ok, cowboy_req:set_cors_preflight_headers(Headers, Req), Env};
_ ->
{ok, cowboy_req:set_cors_headers([{origin, <<$*>>}], Req), Env}
end.
I believe those functions could make Cowboy yet more friendly and useful tool and would love to make a pull request, if it's ok with Cowboy's philosophy.
Maybe we could make Cowboy a bit more CORS friendly?
By now, to enable CORS we should work with raw headers, copy-pasting code like this from one header to another:
In this way we do not actually care about allowed header's value types. For instance, "access-control-allow-methods" and "access-control-allow-headers" should only accept a list of binaries, while only a numeric value makes sense for the "access-control-max-age" header, "*" origin cannot be used for a resource that supports credentials. Ignoring this information leads to misspelling errors, breaking CORS responses and isn't flexible for working with.
My proposal is to add two new functions (
set_cors_headers/2andset_cors_preflight_headers/2) to thecowboy_reqmodule extending functionality ofset_resp_header/3.In my projects, I'm using this simple syntax sugar in REST handlers:
It's also possible to use those functions in a middleware:
I believe those functions could make Cowboy yet more friendly and useful tool and would love to make a pull request, if it's ok with Cowboy's philosophy.