Packages
hackney
1.17.1
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_date.erl
%%% -*- erlang -*-
%%%
%%% This file is part of hackney_lib released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%% Copyright (c) 2012-2015 Benoît Chesneau <benoitc@e-engura.org>
%%%
-module(hackney_date).
-export([date_to_rfc2109/1]).
-export([parse_http_date/1,
rfc2109_to_date/1,
rfc1123_to_date/1,
rfc850_to_date/1,
asctime_to_date/1]).
%% @doc Return the date formatted according to RFC2109.
-spec date_to_rfc2109(calendar:datetime()) -> binary().
date_to_rfc2109({Date = {Y, Mo, D}, {H, Mi, S}}) ->
Wday = calendar:day_of_the_week(Date),
<<(weekday_int(Wday))/binary, ", ",
(pad_int(D))/binary, "-",
(month_int(Mo))/binary, "-",
(year_int(Y))/binary, " ",
(pad_int(H))/binary, ":",
(pad_int(Mi))/binary, ":",
(pad_int(S))/binary, " GMT">>.
%% @doc Parse an HTTP date (RFC1123, RFC850 or asctime date).
%% @end
%%
%% While this may not be the most efficient date parsing we can do,
%% it should work fine for our purposes because all HTTP dates should
%% be sent as RFC1123 dates in HTTP/1.1.
-spec parse_http_date(binary()) -> any().
parse_http_date(Data) ->
case rfc1123_to_date(Data) of
{error, badarg} ->
case rfc850_to_date(Data) of
{error, badarg} ->
case asctime_to_date(Data) of
{error, badarg} ->
case rfc2109_to_date(Data) of
{error, badarg} ->
{error, badarg};
HTTPDate ->
HTTPDate
end;
HTTPDate ->
HTTPDate
end;
HTTPDate ->
HTTPDate
end;
HTTPDate ->
HTTPDate
end.
%% @doc Parse an RFC2109 date.
-spec rfc2109_to_date(binary()) -> any().
rfc2109_to_date(Data) ->
wkday(Data,
fun (<< ", ", Rest/binary >>, _WkDay) ->
date4(Rest,
fun (<< " ", Rest2/binary >>, Date) ->
time(Rest2,
fun (<< " GMT", Rest3/binary >>, Time) ->
http_date_ret(Rest3, {Date, Time});
(_Any, _Time) ->
{error, badarg}
end);
(_Any, _Date) ->
{error, badarg}
end);
(_Any, _WkDay) ->
{error, badarg}
end).
%% @doc Parse an RFC1123 date.
-spec rfc1123_to_date(binary()) -> any().
rfc1123_to_date(Data) ->
wkday(Data,
fun (<< ", ", Rest/binary >>, _WkDay) ->
date1(Rest,
fun (<< " ", Rest2/binary >>, Date) ->
time(Rest2,
fun (<< " GMT", Rest3/binary >>, Time) ->
http_date_ret(Rest3, {Date, Time});
(_Any, _Time) ->
{error, badarg}
end);
(_Any, _Date) ->
{error, badarg}
end);
(_Any, _WkDay) ->
{error, badarg}
end).
%% @doc Parse an RFC850 date.
-spec rfc850_to_date(binary()) -> any().
%% From the RFC:
%% HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
%% which appears to be more than 50 years in the future is in fact
%% in the past (this helps solve the "year 2000" problem).
rfc850_to_date(Data) ->
weekday(Data,
fun (<< ", ", Rest/binary >>, _WeekDay) ->
date2(Rest,
fun (<< " ", Rest2/binary >>, Date) ->
time(Rest2,
fun (<< " GMT", Rest3/binary >>, Time) ->
http_date_ret(Rest3, {Date, Time});
(_Any, _Time) ->
{error, badarg}
end);
(_Any, _Date) ->
{error, badarg}
end);
(_Any, _WeekDay) ->
{error, badarg}
end).
%% @doc Parse an asctime date.
-spec asctime_to_date(binary()) -> any().
asctime_to_date(Data) ->
wkday(Data,
fun (<< " ", Rest/binary >>, _WkDay) ->
date3(Rest,
fun (<< " ", Rest2/binary >>, PartialDate) ->
time(Rest2,
fun (<< " ", Rest3/binary >>, Time) ->
asctime_year(Rest3,
PartialDate, Time);
(_Any, _Time) ->
{error, badarg}
end);
(_Any, _PartialDate) ->
{error, badarg}
end);
(_Any, _WkDay) ->
{error, badarg}
end).
-spec asctime_year(binary(), tuple(), tuple()) -> any().
asctime_year(<< Y1, Y2, Y3, Y4, Rest/binary >>, {Month, Day}, Time)
when Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
Year = (Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
http_date_ret(Rest, {{Year, Month, Day}, Time}).
-spec http_date_ret(binary(), tuple()) -> any().
http_date_ret(Data, DateTime = {Date, _Time}) ->
hackney_bstr:whitespace(Data,
fun (<<>>) ->
case calendar:valid_date(Date) of
true -> DateTime;
false -> {error, badarg}
end;
(_Any) ->
{error, badarg}
end).
%% We never use it, pretty much just checks the wkday is right.
-spec wkday(binary(), fun()) -> any().
wkday(<< WkDay:3/binary, Rest/binary >>, Fun)
when WkDay =:= <<"Mon">>; WkDay =:= <<"Tue">>; WkDay =:= <<"Wed">>;
WkDay =:= <<"Thu">>; WkDay =:= <<"Fri">>; WkDay =:= <<"Sat">>;
WkDay =:= <<"Sun">> ->
Fun(Rest, WkDay);
wkday(_Any, _Fun) ->
{error, badarg}.
%% We never use it, pretty much just checks the weekday is right.
-spec weekday(binary(), fun()) -> any().
weekday(<< "Monday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Monday">>);
weekday(<< "Tuesday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Tuesday">>);
weekday(<< "Wednesday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Wednesday">>);
weekday(<< "Thursday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Thursday">>);
weekday(<< "Friday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Friday">>);
weekday(<< "Saturday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Saturday">>);
weekday(<< "Sunday", Rest/binary >>, Fun) ->
Fun(Rest, <<"Sunday">>);
weekday(_Any, _Fun) ->
{error, badarg}.
-spec date1(binary(), fun()) -> any().
date1(<< D1, D2, " ", M:3/binary, " ", Y1, Y2, Y3, Y4, Rest/binary >>, Fun)
when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
case month_to_int(M) of
{error, badarg} ->
{error, badarg};
Month ->
Fun(Rest, {
(Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
Month,
(D1 - $0) * 10 + (D2 - $0)
})
end;
date1(_Data, _Fun) ->
{error, badarg}.
-spec date2(binary(), fun()) -> any().
date2(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Rest/binary >>, Fun)
when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9 ->
case month_to_int(M) of
{error, badarg} ->
{error, badarg};
Month ->
Year = (Y1 - $0) * 10 + (Y2 - $0),
Year2 = case Year > 50 of
true -> Year + 1900;
false -> Year + 2000
end,
Fun(Rest, {
Year2,
Month,
(D1 - $0) * 10 + (D2 - $0)
})
end;
date2(_Data, _Fun) ->
{error, badarg}.
-spec date3(binary(), fun()) -> any().
date3(<< M:3/binary, " ", D1, D2, Rest/binary >>, Fun)
when (D1 >= $0 andalso D1 =< $3) orelse D1 =:= $\s,
D2 >= $0, D2 =< $9 ->
case month_to_int(M) of
{error, badarg} ->
{error, badarg};
Month ->
Day = case D1 of
$\s -> D2 - $0;
D1 -> (D1 - $0) * 10 + (D2 - $0)
end,
Fun(Rest, {Month, Day})
end;
date3(_Data, _Fun) ->
{error, badarg}.
date4(<< D1, D2, "-", M:3/binary, "-", Y1, Y2, Y3, Y4, Rest/binary >>, Fun)
when D1 >= $0, D1 =< $9, D2 >= $0, D2 =< $9,
Y1 >= $0, Y1 =< $9, Y2 >= $0, Y2 =< $9,
Y3 >= $0, Y3 =< $9, Y4 >= $0, Y4 =< $9 ->
case month_to_int(M) of
{error, badarg} ->
{error, badarg};
Month ->
Fun(Rest, {
(Y1 - $0) * 1000 + (Y2 - $0) * 100 + (Y3 - $0) * 10 + (Y4 - $0),
Month,
(D1 - $0) * 10 + (D2 - $0)
})
end;
date4(_Data, _Fun) ->
{error, badarg}.
-spec month_to_int(<< _:24 >>) -> 1..12 | {error, badarg}.
month_to_int(<<"Jan">>) -> 1;
month_to_int(<<"Feb">>) -> 2;
month_to_int(<<"Mar">>) -> 3;
month_to_int(<<"Apr">>) -> 4;
month_to_int(<<"May">>) -> 5;
month_to_int(<<"Jun">>) -> 6;
month_to_int(<<"Jul">>) -> 7;
month_to_int(<<"Aug">>) -> 8;
month_to_int(<<"Sep">>) -> 9;
month_to_int(<<"Oct">>) -> 10;
month_to_int(<<"Nov">>) -> 11;
month_to_int(<<"Dec">>) -> 12;
month_to_int(_Any) -> {error, badarg}.
-spec time(binary(), fun()) -> any().
time(<< H1, H2, ":", M1, M2, ":", S1, S2, Rest/binary >>, Fun)
when H1 >= $0, H1 =< $2, H2 >= $0, H2 =< $9,
M1 >= $0, M1 =< $5, M2 >= $0, M2 =< $9,
S1 >= $0, S1 =< $5, S2 >= $0, S2 =< $9 ->
Hour = (H1 - $0) * 10 + (H2 - $0),
case Hour < 24 of
true ->
Time = {
Hour,
(M1 - $0) * 10 + (M2 - $0),
(S1 - $0) * 10 + (S2 - $0)
},
Fun(Rest, Time);
false ->
{error, badarg}
end.
-spec pad_int(0..59) -> <<_:16>>.
pad_int( 0) -> <<"00">>;
pad_int( 1) -> <<"01">>;
pad_int( 2) -> <<"02">>;
pad_int( 3) -> <<"03">>;
pad_int( 4) -> <<"04">>;
pad_int( 5) -> <<"05">>;
pad_int( 6) -> <<"06">>;
pad_int( 7) -> <<"07">>;
pad_int( 8) -> <<"08">>;
pad_int( 9) -> <<"09">>;
pad_int(10) -> <<"10">>;
pad_int(11) -> <<"11">>;
pad_int(12) -> <<"12">>;
pad_int(13) -> <<"13">>;
pad_int(14) -> <<"14">>;
pad_int(15) -> <<"15">>;
pad_int(16) -> <<"16">>;
pad_int(17) -> <<"17">>;
pad_int(18) -> <<"18">>;
pad_int(19) -> <<"19">>;
pad_int(20) -> <<"20">>;
pad_int(21) -> <<"21">>;
pad_int(22) -> <<"22">>;
pad_int(23) -> <<"23">>;
pad_int(24) -> <<"24">>;
pad_int(25) -> <<"25">>;
pad_int(26) -> <<"26">>;
pad_int(27) -> <<"27">>;
pad_int(28) -> <<"28">>;
pad_int(29) -> <<"29">>;
pad_int(30) -> <<"30">>;
pad_int(31) -> <<"31">>;
pad_int(32) -> <<"32">>;
pad_int(33) -> <<"33">>;
pad_int(34) -> <<"34">>;
pad_int(35) -> <<"35">>;
pad_int(36) -> <<"36">>;
pad_int(37) -> <<"37">>;
pad_int(38) -> <<"38">>;
pad_int(39) -> <<"39">>;
pad_int(40) -> <<"40">>;
pad_int(41) -> <<"41">>;
pad_int(42) -> <<"42">>;
pad_int(43) -> <<"43">>;
pad_int(44) -> <<"44">>;
pad_int(45) -> <<"45">>;
pad_int(46) -> <<"46">>;
pad_int(47) -> <<"47">>;
pad_int(48) -> <<"48">>;
pad_int(49) -> <<"49">>;
pad_int(50) -> <<"50">>;
pad_int(51) -> <<"51">>;
pad_int(52) -> <<"52">>;
pad_int(53) -> <<"53">>;
pad_int(54) -> <<"54">>;
pad_int(55) -> <<"55">>;
pad_int(56) -> <<"56">>;
pad_int(57) -> <<"57">>;
pad_int(58) -> <<"58">>;
pad_int(59) -> <<"59">>.
-spec weekday_int(1..7) -> <<_:24>>.
weekday_int(1) -> <<"Mon">>;
weekday_int(2) -> <<"Tue">>;
weekday_int(3) -> <<"Wed">>;
weekday_int(4) -> <<"Thu">>;
weekday_int(5) -> <<"Fri">>;
weekday_int(6) -> <<"Sat">>;
weekday_int(7) -> <<"Sun">>.
-spec month_int(1..12) -> <<_:24>>.
month_int( 1) -> <<"Jan">>;
month_int( 2) -> <<"Feb">>;
month_int( 3) -> <<"Mar">>;
month_int( 4) -> <<"Apr">>;
month_int( 5) -> <<"May">>;
month_int( 6) -> <<"Jun">>;
month_int( 7) -> <<"Jul">>;
month_int( 8) -> <<"Aug">>;
month_int( 9) -> <<"Sep">>;
month_int(10) -> <<"Oct">>;
month_int(11) -> <<"Nov">>;
month_int(12) -> <<"Dec">>.
-spec year_int(pos_integer()) -> <<_:32>>.
year_int(1970) -> <<"1970">>;
year_int(1971) -> <<"1971">>;
year_int(1972) -> <<"1972">>;
year_int(1973) -> <<"1973">>;
year_int(1974) -> <<"1974">>;
year_int(1975) -> <<"1975">>;
year_int(1976) -> <<"1976">>;
year_int(1977) -> <<"1977">>;
year_int(1978) -> <<"1978">>;
year_int(1979) -> <<"1979">>;
year_int(1980) -> <<"1980">>;
year_int(1981) -> <<"1981">>;
year_int(1982) -> <<"1982">>;
year_int(1983) -> <<"1983">>;
year_int(1984) -> <<"1984">>;
year_int(1985) -> <<"1985">>;
year_int(1986) -> <<"1986">>;
year_int(1987) -> <<"1987">>;
year_int(1988) -> <<"1988">>;
year_int(1989) -> <<"1989">>;
year_int(1990) -> <<"1990">>;
year_int(1991) -> <<"1991">>;
year_int(1992) -> <<"1992">>;
year_int(1993) -> <<"1993">>;
year_int(1994) -> <<"1994">>;
year_int(1995) -> <<"1995">>;
year_int(1996) -> <<"1996">>;
year_int(1997) -> <<"1997">>;
year_int(1998) -> <<"1998">>;
year_int(1999) -> <<"1999">>;
year_int(2000) -> <<"2000">>;
year_int(2001) -> <<"2001">>;
year_int(2002) -> <<"2002">>;
year_int(2003) -> <<"2003">>;
year_int(2004) -> <<"2004">>;
year_int(2005) -> <<"2005">>;
year_int(2006) -> <<"2006">>;
year_int(2007) -> <<"2007">>;
year_int(2008) -> <<"2008">>;
year_int(2009) -> <<"2009">>;
year_int(2010) -> <<"2010">>;
year_int(2011) -> <<"2011">>;
year_int(2012) -> <<"2012">>;
year_int(2013) -> <<"2013">>;
year_int(2014) -> <<"2014">>;
year_int(2015) -> <<"2015">>;
year_int(2016) -> <<"2016">>;
year_int(2017) -> <<"2017">>;
year_int(2018) -> <<"2018">>;
year_int(2019) -> <<"2019">>;
year_int(2020) -> <<"2020">>;
year_int(2021) -> <<"2021">>;
year_int(2022) -> <<"2022">>;
year_int(2023) -> <<"2023">>;
year_int(2024) -> <<"2024">>;
year_int(2025) -> <<"2025">>;
year_int(2026) -> <<"2026">>;
year_int(2027) -> <<"2027">>;
year_int(2028) -> <<"2028">>;
year_int(2029) -> <<"2029">>;
year_int(Year) -> list_to_binary(integer_to_list(Year)).