mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Fix cowboy_http:cookie_to_iodata/3
No more trying to quote, this is still completely broken everywhere.
This commit is contained in:
parent
1851032482
commit
f077c711a8
2 changed files with 47 additions and 22 deletions
|
@ -815,9 +815,20 @@ ce_identity(Data) ->
|
|||
-spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts())
|
||||
-> iodata().
|
||||
cookie_to_iodata(Name, Value, Opts) ->
|
||||
case binary:match(Name, [<<$=>>, <<$,>>, <<$;>>,
|
||||
<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
|
||||
nomatch -> ok
|
||||
end,
|
||||
case binary:match(Value, [<<$,>>, <<$;>>,
|
||||
<<$\s>>, <<$\t>>, <<$\r>>, <<$\n>>, <<$\013>>, <<$\014>>]) of
|
||||
nomatch -> ok
|
||||
end,
|
||||
MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
|
||||
false -> <<>>;
|
||||
{_, MaxAge} when is_integer(MaxAge), MaxAge >= 0 ->
|
||||
{_, 0} ->
|
||||
%% MSIE requires an Expires date in the past to delete a cookie.
|
||||
<<"; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0">>;
|
||||
{_, MaxAge} when is_integer(MaxAge), MaxAge > 0 ->
|
||||
UTC = calendar:universal_time(),
|
||||
Secs = calendar:datetime_to_gregorian_seconds(UTC),
|
||||
Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
|
||||
|
@ -826,11 +837,11 @@ cookie_to_iodata(Name, Value, Opts) ->
|
|||
end,
|
||||
DomainBin = case lists:keyfind(domain, 1, Opts) of
|
||||
false -> <<>>;
|
||||
{_, Domain} -> [<<"; Domain=">>, quote(Domain)]
|
||||
{_, Domain} -> [<<"; Domain=">>, Domain]
|
||||
end,
|
||||
PathBin = case lists:keyfind(path, 1, Opts) of
|
||||
false -> <<>>;
|
||||
{_, Path} -> [<<"; Path=">>, quote(Path)]
|
||||
{_, Path} -> [<<"; Path=">>, Path]
|
||||
end,
|
||||
SecureBin = case lists:keyfind(secure, 1, Opts) of
|
||||
false -> <<>>;
|
||||
|
@ -840,21 +851,9 @@ cookie_to_iodata(Name, Value, Opts) ->
|
|||
false -> <<>>;
|
||||
{_, true} -> <<"; HttpOnly">>
|
||||
end,
|
||||
[Name, <<"=">>, quote(Value), <<"; Version=1">>,
|
||||
[Name, <<"=">>, Value, <<"; Version=1">>,
|
||||
MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
|
||||
|
||||
-spec quote(binary()) -> binary().
|
||||
quote(Bin) ->
|
||||
quote(Bin, << $" >>).
|
||||
|
||||
-spec quote(binary(), binary()) -> binary().
|
||||
quote(<<>>, Acc) ->
|
||||
<< Acc/binary, $" >>;
|
||||
quote(<< $", Rest/bits >>, Acc) ->
|
||||
quote(Rest, << Acc/binary, $\\, $" >>);
|
||||
quote(<< C, Rest/bits >>, Acc) ->
|
||||
quote(Rest, << Acc/binary, C >>).
|
||||
|
||||
%% @doc Convert an HTTP version tuple to its binary form.
|
||||
-spec version_to_binary(version()) -> binary().
|
||||
version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
|
||||
|
@ -1160,14 +1159,14 @@ cookie_to_iodata_test_() ->
|
|||
Tests = [
|
||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||
[{http_only, true}, {domain, <<"acme.com">>}],
|
||||
<<"Customer=\"WILE_E_COYOTE\"; Version=1; "
|
||||
"Domain=\"acme.com\"; HttpOnly">>},
|
||||
<<"Customer=WILE_E_COYOTE; Version=1; "
|
||||
"Domain=acme.com; HttpOnly">>},
|
||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||
[{path, <<"/acme">>}],
|
||||
<<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>},
|
||||
<<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
|
||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||
[{path, <<"/acme">>}, {badoption, <<"negatory">>}],
|
||||
<<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>}
|
||||
<<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>}
|
||||
],
|
||||
[{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end}
|
||||
|| {N, V, O, R} <- Tests].
|
||||
|
@ -1177,7 +1176,7 @@ cookie_to_iodata_max_age_test() ->
|
|||
binary:split(iolist_to_binary(
|
||||
cookie_to_iodata(N, V, O)), <<";">>, [global])
|
||||
end,
|
||||
[<<"Customer=\"WILE_E_COYOTE\"">>,
|
||||
[<<"Customer=WILE_E_COYOTE">>,
|
||||
<<" Version=1">>,
|
||||
<<" Expires=", _/binary>>,
|
||||
<<" Max-Age=111">>,
|
||||
|
@ -1186,13 +1185,33 @@ cookie_to_iodata_max_age_test() ->
|
|||
case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
|
||||
{'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
|
||||
end,
|
||||
[<<"Customer=\"WILE_E_COYOTE\"">>,
|
||||
[<<"Customer=WILE_E_COYOTE">>,
|
||||
<<" Version=1">>,
|
||||
<<" Expires=", _/binary>>,
|
||||
<<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||
[{max_age, 86417}]),
|
||||
ok.
|
||||
|
||||
cookie_to_iodata_failures_test_() ->
|
||||
F = fun(N, V) ->
|
||||
try cookie_to_iodata(N, V, []) of
|
||||
_ ->
|
||||
false
|
||||
catch _:_ ->
|
||||
true
|
||||
end
|
||||
end,
|
||||
Tests = [
|
||||
{<<"Na=me">>, <<"Value">>},
|
||||
{<<"Name;">>, <<"Value">>},
|
||||
{<<"\r\name">>, <<"Value">>},
|
||||
{<<"Name">>, <<"Value;">>},
|
||||
{<<"Name">>, <<"\value">>}
|
||||
],
|
||||
[{iolist_to_binary(io_lib:format("{~p, ~p} failure", [N, V])),
|
||||
fun() -> true = F(N, V) end}
|
||||
|| {N, V} <- Tests].
|
||||
|
||||
x_www_form_urlencoded_test_() ->
|
||||
%% {Qs, Result}
|
||||
Tests = [
|
||||
|
|
|
@ -800,6 +800,12 @@ multipart_skip(Req) ->
|
|||
%% Response API.
|
||||
|
||||
%% @doc Add a cookie header to the response.
|
||||
%%
|
||||
%% The cookie name cannot contain any of the following characters:
|
||||
%% =,;\s\t\r\n\013\014
|
||||
%%
|
||||
%% The cookie value cannot contain any of the following characters:
|
||||
%% ,; \t\r\n\013\014
|
||||
-spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req)
|
||||
-> Req when Req::req().
|
||||
set_resp_cookie(Name, Value, Opts, Req) ->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue