Packages
hackney
4.0.3
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Current section
Files
Jump to
Current section
Files
src/hackney_cookie.erl
%%% -*- erlang -*-
%%%
%%% This file is part of hackney_lib released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright 2007 Mochi Media, Inc.
%%% Copyright 2011 Thomas Burdick <thomas.burdick@gmail.com>
%%% Copyright 2013, Loïc Hoguin <essen@ninenines.eu>
%%% Copyright 2013-2015, Benoit Chesneau
%%%
-module(hackney_cookie).
-export([setcookie/3]).
-export([parse_cookie/1]).
-type cookie_option() :: {max_age, non_neg_integer()}
| {domain, binary()} | {path, binary()}
| {secure, boolean()} | {http_only, boolean()}.
-type cookie_opts() :: [cookie_option()].
-export_type([cookie_opts/0]).
%% @doc Convert a cookie name, value and options to its iodata form.
-spec setcookie(iodata(), iodata(), cookie_opts()) -> binary().
setcookie(Name, Value, Opts) ->
nomatch = binary:match(iolist_to_binary(Name), [<<$=>>, <<$,>>, <<$;>>,
<<$\s>>, <<$\t>>, <<$\r>>,
<<$\n>>, <<$\013>>,
<<$\014>>]),
nomatch = binary:match(iolist_to_binary(Value), [<<$,>>, <<$;>>,
<<$\s>>, <<$\t>>,
<<$\r>>, <<$\n>>,
<<$\013>>, <<$\014>>]),
MaxAgeBin = case lists:keyfind(max_age, 1, Opts) of
false -> <<>>;
{_, 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),
[<<"; Expires=">>, hackney_date:date_to_rfc2109(Expires),
<<"; Max-Age=">>, integer_to_list(MaxAge)]
end,
DomainBin = case lists:keyfind(domain, 1, Opts) of
false -> <<>>;
{_, Domain} ->
%% GHSA-mp55: Name/Value are guarded above; Domain and
%% Path must reject the same line-terminating bytes (and
%% ';', which would start a new attribute) so a
%% request-derived domain/path cannot inject a second
%% Set-Cookie header.
nomatch = binary:match(iolist_to_binary(Domain),
[<<$;>>, <<$\s>>, <<$\t>>, <<$\r>>,
<<$\n>>, <<$\013>>, <<$\014>>, <<0>>]),
[<<"; Domain=">>, Domain]
end,
PathBin = case lists:keyfind(path, 1, Opts) of
false -> <<>>;
{_, Path} ->
nomatch = binary:match(iolist_to_binary(Path),
[<<$;>>, <<$\r>>, <<$\n>>,
<<$\013>>, <<$\014>>, <<0>>]),
[<<"; Path=">>, Path]
end,
SecureBin = case lists:keyfind(secure, 1, Opts) of
false -> <<>>;
{_, true} -> <<"; Secure">>
end,
HttpOnlyBin = case lists:keyfind(http_only, 1, Opts) of
false -> <<>>;
{_, true} -> <<"; HttpOnly">>
end,
iolist_to_binary([Name, <<"=">>, Value, <<"; Version=1">>,
MaxAgeBin, DomainBin, PathBin, SecureBin,
HttpOnlyBin]).
%% @doc Parse a cookie header string and return a list of key/values.
-spec parse_cookie(binary()) -> [{binary(), binary()}] | {error, badarg}.
parse_cookie(Cookie) ->
parse_cookie(Cookie, []).
parse_cookie(<<>>, Acc) ->
lists:reverse(Acc);
parse_cookie(<< $\s, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
parse_cookie(<< $\t, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
parse_cookie(<< $,, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
parse_cookie(<< $;, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
parse_cookie(<< $$, Rest/binary >>, Acc) ->
skip_cookie(Rest, Acc);
parse_cookie(Cookie, Acc) ->
parse_cookie_name(Cookie, Acc, <<>>).
skip_cookie(<<>>, Acc) ->
lists:reverse(Acc);
skip_cookie(<< $,, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
skip_cookie(<< $;, Rest/binary >>, Acc) ->
parse_cookie(Rest, Acc);
skip_cookie(<< _, Rest/binary >>, Acc) ->
skip_cookie(Rest, Acc).
parse_cookie_name(<<>>, Acc, Name) when Name /= <<>> ->
lists:reverse([{Name, true} | Acc]);
parse_cookie_name(<<>>, _, _) ->
{error, badarg};
parse_cookie_name(<< $=, _/binary >>, _, <<>>) ->
{error, badarg};
parse_cookie_name(<< $=, Rest/binary >>, Acc, Name) ->
parse_cookie_value(Rest, Acc, Name, <<>>);
parse_cookie_name(<< $,, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $;, Rest/binary >>, Acc, Name) ->
parse_cookie(Rest, [{Name, true} | Acc]);
parse_cookie_name(<< $\s, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $\t, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $\r, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $\n, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $\013, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< $\014, _/binary >>, _, _) ->
{error, badarg};
parse_cookie_name(<< C, Rest/binary >>, Acc, Name) ->
parse_cookie_name(Rest, Acc, << Name/binary, C >>).
parse_cookie_value(<<>>, Acc, Name, Value) ->
lists:reverse([{Name, parse_cookie_trim(Value)}|Acc]);
parse_cookie_value(<< $;, Rest/binary >>, Acc, Name, Value) ->
parse_cookie(Rest, [{Name, parse_cookie_trim(Value)}|Acc]);
parse_cookie_value(<< $\t, _/binary >>, _, _, _) ->
{error, badarg};
parse_cookie_value(<< $\r, _/binary >>, _, _, _) ->
{error, badarg};
parse_cookie_value(<< $\n, _/binary >>, _, _, _) ->
{error, badarg};
parse_cookie_value(<< $\013, _/binary >>, _, _, _) ->
{error, badarg};
parse_cookie_value(<< $\014, _/binary >>, _, _, _) ->
{error, badarg};
parse_cookie_value(<< C, Rest/binary >>, Acc, Name, Value) ->
parse_cookie_value(Rest, Acc, Name, << Value/binary, C >>).
parse_cookie_trim(Value = <<>>) ->
Value;
parse_cookie_trim(Value) ->
case binary:last(Value) of
$\s ->
Size = byte_size(Value) - 1,
<< Value2:Size/binary, _ >> = Value,
parse_cookie_trim(Value2);
_ ->
Value
end.