zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cowboy_clock.erl (8173B)


      1 %% Copyright (c) 2011-2017, Loïc Hoguin <essen@ninenines.eu>
      2 %%
      3 %% Permission to use, copy, modify, and/or distribute this software for any
      4 %% purpose with or without fee is hereby granted, provided that the above
      5 %% copyright notice and this permission notice appear in all copies.
      6 %%
      7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     14 
     15 %% While a gen_server process runs in the background to update
     16 %% the cache of formatted dates every second, all API calls are
     17 %% local and directly read from the ETS cache table, providing
     18 %% fast time and date computations.
     19 -module(cowboy_clock).
     20 -behaviour(gen_server).
     21 
     22 %% API.
     23 -export([start_link/0]).
     24 -export([stop/0]).
     25 -export([rfc1123/0]).
     26 -export([rfc1123/1]).
     27 
     28 %% gen_server.
     29 -export([init/1]).
     30 -export([handle_call/3]).
     31 -export([handle_cast/2]).
     32 -export([handle_info/2]).
     33 -export([terminate/2]).
     34 -export([code_change/3]).
     35 
     36 -record(state, {
     37 	universaltime = undefined :: undefined | calendar:datetime(),
     38 	rfc1123 = <<>> :: binary(),
     39 	tref = undefined :: undefined | reference()
     40 }).
     41 
     42 %% API.
     43 
     44 -spec start_link() -> {ok, pid()}.
     45 start_link() ->
     46 	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
     47 
     48 -spec stop() -> stopped.
     49 stop() ->
     50 	gen_server:call(?MODULE, stop).
     51 
     52 %% When the ets table doesn't exist, either because of a bug
     53 %% or because Cowboy is being restarted, we perform in a
     54 %% slightly degraded state and build a new timestamp for
     55 %% every request.
     56 -spec rfc1123() -> binary().
     57 rfc1123() ->
     58 	try
     59 		ets:lookup_element(?MODULE, rfc1123, 2)
     60 	catch error:badarg ->
     61 		rfc1123(erlang:universaltime())
     62 	end.
     63 
     64 -spec rfc1123(calendar:datetime()) -> binary().
     65 rfc1123(DateTime) ->
     66 	update_rfc1123(<<>>, undefined, DateTime).
     67 
     68 %% gen_server.
     69 
     70 -spec init([]) -> {ok, #state{}}.
     71 init([]) ->
     72 	?MODULE = ets:new(?MODULE, [set, protected,
     73 		named_table, {read_concurrency, true}]),
     74 	T = erlang:universaltime(),
     75 	B = update_rfc1123(<<>>, undefined, T),
     76 	TRef = erlang:send_after(1000, self(), update),
     77 	ets:insert(?MODULE, {rfc1123, B}),
     78 	{ok, #state{universaltime=T, rfc1123=B, tref=TRef}}.
     79 
     80 -type from() :: {pid(), term()}.
     81 -spec handle_call
     82 	(stop, from(), State) -> {stop, normal, stopped, State}
     83 	when State::#state{}.
     84 handle_call(stop, _From, State) ->
     85 	{stop, normal, stopped, State};
     86 handle_call(_Request, _From, State) ->
     87 	{reply, ignored, State}.
     88 
     89 -spec handle_cast(_, State) -> {noreply, State} when State::#state{}.
     90 handle_cast(_Msg, State) ->
     91 	{noreply, State}.
     92 
     93 -spec handle_info(any(), State) -> {noreply, State} when State::#state{}.
     94 handle_info(update, #state{universaltime=Prev, rfc1123=B1, tref=TRef0}) ->
     95 	%% Cancel the timer in case an external process sent an update message.
     96 	_ = erlang:cancel_timer(TRef0),
     97 	T = erlang:universaltime(),
     98 	B2 = update_rfc1123(B1, Prev, T),
     99 	ets:insert(?MODULE, {rfc1123, B2}),
    100 	TRef = erlang:send_after(1000, self(), update),
    101 	{noreply, #state{universaltime=T, rfc1123=B2, tref=TRef}};
    102 handle_info(_Info, State) ->
    103 	{noreply, State}.
    104 
    105 -spec terminate(_, _) -> ok.
    106 terminate(_Reason, _State) ->
    107 	ok.
    108 
    109 -spec code_change(_, State, _) -> {ok, State} when State::#state{}.
    110 code_change(_OldVsn, State, _Extra) ->
    111 	{ok, State}.
    112 
    113 %% Internal.
    114 
    115 -spec update_rfc1123(binary(), undefined | calendar:datetime(),
    116 	calendar:datetime()) -> binary().
    117 update_rfc1123(Bin, Now, Now) ->
    118 	Bin;
    119 update_rfc1123(<< Keep:23/binary, _/bits >>,
    120 		{Date, {H, M, _}}, {Date, {H, M, S}}) ->
    121 	<< Keep/binary, (pad_int(S))/binary, " GMT" >>;
    122 update_rfc1123(<< Keep:20/binary, _/bits >>,
    123 		{Date, {H, _, _}}, {Date, {H, M, S}}) ->
    124 	<< Keep/binary, (pad_int(M))/binary, $:, (pad_int(S))/binary, " GMT" >>;
    125 update_rfc1123(<< Keep:17/binary, _/bits >>, {Date, _}, {Date, {H, M, S}}) ->
    126 	<< Keep/binary, (pad_int(H))/binary, $:, (pad_int(M))/binary,
    127 		$:, (pad_int(S))/binary, " GMT" >>;
    128 update_rfc1123(<< _:7/binary, Keep:10/binary, _/bits >>,
    129 		{{Y, Mo, _}, _}, {Date = {Y, Mo, D}, {H, M, S}}) ->
    130 	Wday = calendar:day_of_the_week(Date),
    131 	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, Keep/binary,
    132 		(pad_int(H))/binary, $:, (pad_int(M))/binary,
    133 		$:, (pad_int(S))/binary, " GMT" >>;
    134 update_rfc1123(<< _:11/binary, Keep:6/binary, _/bits >>,
    135 		{{Y, _, _}, _}, {Date = {Y, Mo, D}, {H, M, S}}) ->
    136 	Wday = calendar:day_of_the_week(Date),
    137 	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, " ",
    138 		(month(Mo))/binary, Keep/binary,
    139 		(pad_int(H))/binary, $:, (pad_int(M))/binary,
    140 		$:, (pad_int(S))/binary, " GMT" >>;
    141 update_rfc1123(_, _, {Date = {Y, Mo, D}, {H, M, S}}) ->
    142 	Wday = calendar:day_of_the_week(Date),
    143 	<< (weekday(Wday))/binary, ", ", (pad_int(D))/binary, " ",
    144 		(month(Mo))/binary, " ", (integer_to_binary(Y))/binary,
    145 		" ", (pad_int(H))/binary, $:, (pad_int(M))/binary,
    146 		$:, (pad_int(S))/binary, " GMT" >>.
    147 
    148 %% Following suggestion by MononcQc on #erlounge.
    149 -spec pad_int(0..59) -> binary().
    150 pad_int(X) when X < 10 ->
    151 	<< $0, ($0 + X) >>;
    152 pad_int(X) ->
    153 	integer_to_binary(X).
    154 
    155 -spec weekday(1..7) -> <<_:24>>.
    156 weekday(1) -> <<"Mon">>;
    157 weekday(2) -> <<"Tue">>;
    158 weekday(3) -> <<"Wed">>;
    159 weekday(4) -> <<"Thu">>;
    160 weekday(5) -> <<"Fri">>;
    161 weekday(6) -> <<"Sat">>;
    162 weekday(7) -> <<"Sun">>.
    163 
    164 -spec month(1..12) -> <<_:24>>.
    165 month( 1) -> <<"Jan">>;
    166 month( 2) -> <<"Feb">>;
    167 month( 3) -> <<"Mar">>;
    168 month( 4) -> <<"Apr">>;
    169 month( 5) -> <<"May">>;
    170 month( 6) -> <<"Jun">>;
    171 month( 7) -> <<"Jul">>;
    172 month( 8) -> <<"Aug">>;
    173 month( 9) -> <<"Sep">>;
    174 month(10) -> <<"Oct">>;
    175 month(11) -> <<"Nov">>;
    176 month(12) -> <<"Dec">>.
    177 
    178 %% Tests.
    179 
    180 -ifdef(TEST).
    181 update_rfc1123_test_() ->
    182 	Tests = [
    183 		{<<"Sat, 14 May 2011 14:25:33 GMT">>, undefined,
    184 			{{2011, 5, 14}, {14, 25, 33}}, <<>>},
    185 		{<<"Sat, 14 May 2011 14:25:33 GMT">>, {{2011, 5, 14}, {14, 25, 33}},
    186 			{{2011, 5, 14}, {14, 25, 33}}, <<"Sat, 14 May 2011 14:25:33 GMT">>},
    187 		{<<"Sat, 14 May 2011 14:25:34 GMT">>, {{2011, 5, 14}, {14, 25, 33}},
    188 			{{2011, 5, 14}, {14, 25, 34}}, <<"Sat, 14 May 2011 14:25:33 GMT">>},
    189 		{<<"Sat, 14 May 2011 14:26:00 GMT">>, {{2011, 5, 14}, {14, 25, 59}},
    190 			{{2011, 5, 14}, {14, 26,  0}}, <<"Sat, 14 May 2011 14:25:59 GMT">>},
    191 		{<<"Sat, 14 May 2011 15:00:00 GMT">>, {{2011, 5, 14}, {14, 59, 59}},
    192 			{{2011, 5, 14}, {15,  0,  0}}, <<"Sat, 14 May 2011 14:59:59 GMT">>},
    193 		{<<"Sun, 15 May 2011 00:00:00 GMT">>, {{2011, 5, 14}, {23, 59, 59}},
    194 			{{2011, 5, 15}, { 0,  0,  0}}, <<"Sat, 14 May 2011 23:59:59 GMT">>},
    195 		{<<"Wed, 01 Jun 2011 00:00:00 GMT">>, {{2011, 5, 31}, {23, 59, 59}},
    196 			{{2011, 6,  1}, { 0,  0,  0}}, <<"Tue, 31 May 2011 23:59:59 GMT">>},
    197 		{<<"Sun, 01 Jan 2012 00:00:00 GMT">>, {{2011, 5, 31}, {23, 59, 59}},
    198 			{{2012, 1,  1}, { 0,  0,  0}}, <<"Sat, 31 Dec 2011 23:59:59 GMT">>}
    199 	],
    200 	[{R, fun() -> R = update_rfc1123(B, P, N) end} || {R, P, N, B} <- Tests].
    201 
    202 pad_int_test_() ->
    203 	Tests = [
    204 		{ 0, <<"00">>}, { 1, <<"01">>}, { 2, <<"02">>}, { 3, <<"03">>},
    205 		{ 4, <<"04">>}, { 5, <<"05">>}, { 6, <<"06">>}, { 7, <<"07">>},
    206 		{ 8, <<"08">>}, { 9, <<"09">>}, {10, <<"10">>}, {11, <<"11">>},
    207 		{12, <<"12">>}, {13, <<"13">>}, {14, <<"14">>}, {15, <<"15">>},
    208 		{16, <<"16">>}, {17, <<"17">>}, {18, <<"18">>}, {19, <<"19">>},
    209 		{20, <<"20">>}, {21, <<"21">>}, {22, <<"22">>}, {23, <<"23">>},
    210 		{24, <<"24">>}, {25, <<"25">>}, {26, <<"26">>}, {27, <<"27">>},
    211 		{28, <<"28">>}, {29, <<"29">>}, {30, <<"30">>}, {31, <<"31">>},
    212 		{32, <<"32">>}, {33, <<"33">>}, {34, <<"34">>}, {35, <<"35">>},
    213 		{36, <<"36">>}, {37, <<"37">>}, {38, <<"38">>}, {39, <<"39">>},
    214 		{40, <<"40">>}, {41, <<"41">>}, {42, <<"42">>}, {43, <<"43">>},
    215 		{44, <<"44">>}, {45, <<"45">>}, {46, <<"46">>}, {47, <<"47">>},
    216 		{48, <<"48">>}, {49, <<"49">>}, {50, <<"50">>}, {51, <<"51">>},
    217 		{52, <<"52">>}, {53, <<"53">>}, {54, <<"54">>}, {55, <<"55">>},
    218 		{56, <<"56">>}, {57, <<"57">>}, {58, <<"58">>}, {59, <<"59">>}
    219 	],
    220 	[{I, fun() -> O = pad_int(I) end} || {I, O} <- Tests].
    221 -endif.