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())
|
-spec cookie_to_iodata(iodata(), iodata(), cowboy_req:cookie_opts())
|
||||||
-> iodata().
|
-> iodata().
|
||||||
cookie_to_iodata(Name, Value, Opts) ->
|
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
|
MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
|
||||||
false -> <<>>;
|
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(),
|
UTC = calendar:universal_time(),
|
||||||
Secs = calendar:datetime_to_gregorian_seconds(UTC),
|
Secs = calendar:datetime_to_gregorian_seconds(UTC),
|
||||||
Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
|
Expires = calendar:gregorian_seconds_to_datetime(Secs + MaxAge),
|
||||||
|
@ -826,11 +837,11 @@ cookie_to_iodata(Name, Value, Opts) ->
|
||||||
end,
|
end,
|
||||||
DomainBin = case lists:keyfind(domain, 1, Opts) of
|
DomainBin = case lists:keyfind(domain, 1, Opts) of
|
||||||
false -> <<>>;
|
false -> <<>>;
|
||||||
{_, Domain} -> [<<"; Domain=">>, quote(Domain)]
|
{_, Domain} -> [<<"; Domain=">>, Domain]
|
||||||
end,
|
end,
|
||||||
PathBin = case lists:keyfind(path, 1, Opts) of
|
PathBin = case lists:keyfind(path, 1, Opts) of
|
||||||
false -> <<>>;
|
false -> <<>>;
|
||||||
{_, Path} -> [<<"; Path=">>, quote(Path)]
|
{_, Path} -> [<<"; Path=">>, Path]
|
||||||
end,
|
end,
|
||||||
SecureBin = case lists:keyfind(secure, 1, Opts) of
|
SecureBin = case lists:keyfind(secure, 1, Opts) of
|
||||||
false -> <<>>;
|
false -> <<>>;
|
||||||
|
@ -840,21 +851,9 @@ cookie_to_iodata(Name, Value, Opts) ->
|
||||||
false -> <<>>;
|
false -> <<>>;
|
||||||
{_, true} -> <<"; HttpOnly">>
|
{_, true} -> <<"; HttpOnly">>
|
||||||
end,
|
end,
|
||||||
[Name, <<"=">>, quote(Value), <<"; Version=1">>,
|
[Name, <<"=">>, Value, <<"; Version=1">>,
|
||||||
MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
|
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.
|
%% @doc Convert an HTTP version tuple to its binary form.
|
||||||
-spec version_to_binary(version()) -> binary().
|
-spec version_to_binary(version()) -> binary().
|
||||||
version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
|
version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
|
||||||
|
@ -1160,14 +1159,14 @@ cookie_to_iodata_test_() ->
|
||||||
Tests = [
|
Tests = [
|
||||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||||
[{http_only, true}, {domain, <<"acme.com">>}],
|
[{http_only, true}, {domain, <<"acme.com">>}],
|
||||||
<<"Customer=\"WILE_E_COYOTE\"; Version=1; "
|
<<"Customer=WILE_E_COYOTE; Version=1; "
|
||||||
"Domain=\"acme.com\"; HttpOnly">>},
|
"Domain=acme.com; HttpOnly">>},
|
||||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||||
[{path, <<"/acme">>}],
|
[{path, <<"/acme">>}],
|
||||||
<<"Customer=\"WILE_E_COYOTE\"; Version=1; Path=\"/acme\"">>},
|
<<"Customer=WILE_E_COYOTE; Version=1; Path=/acme">>},
|
||||||
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
{<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||||
[{path, <<"/acme">>}, {badoption, <<"negatory">>}],
|
[{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}
|
[{R, fun() -> R = iolist_to_binary(cookie_to_iodata(N, V, O)) end}
|
||||||
|| {N, V, O, R} <- Tests].
|
|| {N, V, O, R} <- Tests].
|
||||||
|
@ -1177,7 +1176,7 @@ cookie_to_iodata_max_age_test() ->
|
||||||
binary:split(iolist_to_binary(
|
binary:split(iolist_to_binary(
|
||||||
cookie_to_iodata(N, V, O)), <<";">>, [global])
|
cookie_to_iodata(N, V, O)), <<";">>, [global])
|
||||||
end,
|
end,
|
||||||
[<<"Customer=\"WILE_E_COYOTE\"">>,
|
[<<"Customer=WILE_E_COYOTE">>,
|
||||||
<<" Version=1">>,
|
<<" Version=1">>,
|
||||||
<<" Expires=", _/binary>>,
|
<<" Expires=", _/binary>>,
|
||||||
<<" Max-Age=111">>,
|
<<" Max-Age=111">>,
|
||||||
|
@ -1186,13 +1185,33 @@ cookie_to_iodata_max_age_test() ->
|
||||||
case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
|
case catch F(<<"Customer">>, <<"WILE_E_COYOTE">>, [{max_age, -111}]) of
|
||||||
{'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
|
{'EXIT', {{case_clause, {max_age, -111}}, _}} -> ok
|
||||||
end,
|
end,
|
||||||
[<<"Customer=\"WILE_E_COYOTE\"">>,
|
[<<"Customer=WILE_E_COYOTE">>,
|
||||||
<<" Version=1">>,
|
<<" Version=1">>,
|
||||||
<<" Expires=", _/binary>>,
|
<<" Expires=", _/binary>>,
|
||||||
<<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
|
<<" Max-Age=86417">>] = F(<<"Customer">>, <<"WILE_E_COYOTE">>,
|
||||||
[{max_age, 86417}]),
|
[{max_age, 86417}]),
|
||||||
ok.
|
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_() ->
|
x_www_form_urlencoded_test_() ->
|
||||||
%% {Qs, Result}
|
%% {Qs, Result}
|
||||||
Tests = [
|
Tests = [
|
||||||
|
|
|
@ -800,6 +800,12 @@ multipart_skip(Req) ->
|
||||||
%% Response API.
|
%% Response API.
|
||||||
|
|
||||||
%% @doc Add a cookie header to the response.
|
%% @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)
|
-spec set_resp_cookie(iodata(), iodata(), cookie_opts(), Req)
|
||||||
-> Req when Req::req().
|
-> Req when Req::req().
|
||||||
set_resp_cookie(Name, Value, Opts, Req) ->
|
set_resp_cookie(Name, Value, Opts, Req) ->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue