Packages
hackney
3.1.0
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
Security advisory:
This version has known vulnerabilities.
View advisories
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} -> [<<"; Domain=">>, Domain]
end,
PathBin = case lists:keyfind(path, 1, Opts) of
false -> <<>>;
{_, Path} -> [<<"; 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.