aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/test/httpd_poll.erl
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2010-01-13 16:18:24 +0000
committerErlang/OTP <[email protected]>2010-01-13 16:18:24 +0000
commit6153ba7599f2ce1ab22959a40b6ca33b4238f0d0 (patch)
treea81d50b08c7828d3662dd50e48bcf55b72f507b2 /lib/inets/test/httpd_poll.erl
parent68c2f188c3446f53fad03d0f652207a9a8bb1946 (diff)
downloadotp-6153ba7599f2ce1ab22959a40b6ca33b4238f0d0.tar.gz
otp-6153ba7599f2ce1ab22959a40b6ca33b4238f0d0.tar.bz2
otp-6153ba7599f2ce1ab22959a40b6ca33b4238f0d0.zip
OTP-8016, OTP-8056, OTP-8103, OTP-8106, OTP-8312, OTP-8315, OTP-8327, OTP-8349,
OTP-8351, OTP-8359 & OTP-8371.
Diffstat (limited to 'lib/inets/test/httpd_poll.erl')
-rw-r--r--lib/inets/test/httpd_poll.erl496
1 files changed, 496 insertions, 0 deletions
diff --git a/lib/inets/test/httpd_poll.erl b/lib/inets/test/httpd_poll.erl
new file mode 100644
index 0000000000..1cc10365a7
--- /dev/null
+++ b/lib/inets/test/httpd_poll.erl
@@ -0,0 +1,496 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(httpd_poll).
+-behaviour(gen_server).
+
+
+%% External API
+-export([start/0, start_appup/2, start/3,stop/0,verbosity/1,poll_time/1]).
+
+%% gen_server exports
+-export([init/1,
+ handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
+
+
+-define(default_verbosity,error).
+-define(default_poll_time,60000). %% 60 seconds
+
+
+-record(state,{host = "", port = -1, ptime = -1, tref = none, uris = []}).
+
+
+%% start/0
+%%
+%% Description: Start polling HTTPD with default values
+%%
+start() ->
+ Options = default_options(otp),
+ start("gandalf", 8000, Options).
+
+start_appup(Host, Port) ->
+ Options = default_options(top),
+ start(Host, Port, Options).
+
+%% start/3
+%%
+%% Description: Start polling HTTPD
+%%
+%% Parameters:
+%% Host = string()
+%% Host name of HTTPD
+%% Port = integer()
+%% Port number of HTTPD
+%% Options = [Option]
+%% Option = {poll_time,integer()} | {verbosity,verbosity()} |
+%% {log_file,string()} | {uris,[uri()]}
+%% verbosity() = silence | error | log | debug | trace
+%% uri() = {string(),string}
+%% First part is a descriptive string and the second
+%% part is the actual URI.
+%%
+start(Host,Port,Options) ->
+ gen_server:start({local,httpd_tester},?MODULE,[Host,Port,Options],[]).
+
+stop() ->
+ gen_server:call(httpd_tester,stop).
+
+
+default_options(UriDesc) ->
+ Verbosity = {verbosity,?default_verbosity},
+ Uris = {uris,uris(UriDesc)},
+ PollTime = {poll_time,?default_poll_time},
+ Logging = {log_file,"httpd_poll.log"},
+ [Verbosity, Uris, PollTime, Logging].
+
+
+options(Options) ->
+ options(Options, default_options(otp), []).
+
+options([], Defaults, Options) ->
+ Options ++ Defaults;
+options([{Key,Val} = Opt|Opts], Defaults, Options) ->
+ options(Opts, lists:keydelete(Key, 1, Defaults), [Opt|Options]).
+
+
+verbosity(silence) ->
+ set_verbosity(silence);
+verbosity(error) ->
+ set_verbosity(error);
+verbosity(log) ->
+ set_verbosity(log);
+verbosity(debug) ->
+ set_verbosity(debug);
+verbosity(trace) ->
+ set_verbosity(trace).
+
+set_verbosity(Verbosity) ->
+ gen_server:cast(httpd_tester,{verbosity,Verbosity}).
+
+poll_time(NewTime) ->
+ gen_server:call(httpd_tester,{poll_time,NewTime}).
+
+
+%% ----------------------------------------------------------------------
+
+
+init([Host, Port, Options0]) ->
+ process_flag(trap_exit,true),
+ Options = options(Options0),
+ put(verbosity,get_verbosity(Options)),
+ log_open(get_log_file(Options)),
+ tstart(),
+ PollTime = get_poll_time(Options),
+ Ref = tcreate(PollTime),
+ log("created"),
+ {ok,#state{host = Host,
+ port = Port,
+ ptime = PollTime,
+ tref = Ref,
+ uris = get_uris(Options)}}.
+
+uris(top) ->
+ [uri_top_index()];
+
+uris(otp) ->
+ [
+ uri_top_index(),
+ uri_internal_product1(),
+ uri_internal_product2(),
+ uri_p7a_test_results(),
+ uri_bjorn1(),
+ uri_bjorn2(),
+ uri_top_ronja()
+ ].
+
+uri_top_index() ->
+ {"top page","/"}.
+
+uri_internal_product1() ->
+ {"product internal page (1)","/product/internal/"}.
+
+uri_internal_product2() ->
+ {"product internal page (2)","/product/internal"}.
+
+uri_p7a_test_results() ->
+ {"test summery index page",
+ "/product/internal/test/test_results/progress_P7A/index.html"}.
+
+uri_bjorn1() ->
+ {"bjorns home page (1)","/~bjorn/"}.
+
+uri_bjorn2() ->
+ {"bjorns home page (2)","/~bjorn"}.
+
+uri_top_ronja() ->
+ {"ronja top page","/ronja/"}.
+
+
+handle_call(stop, _From, State) ->
+ vlog("stop request"),
+ {stop, normal, ok, State};
+
+handle_call({poll_time,NewTime}, _From, State) ->
+ vlog("set new poll time: ~p",[NewTime]),
+ OldTime = State#state.ptime,
+ {stop, normal, OldTime, State#state{ptime = NewTime}};
+
+handle_call(Request, _From, State) ->
+ vlog("unexpected request(call): ~p",[Request]),
+ {reply, ok, State}.
+
+
+handle_cast({verbosity,Verbosity}, State) ->
+ vlog("set (new) verbosity to: ~p",[Verbosity]),
+ put(verbosity,Verbosity),
+ {noreply, State};
+
+handle_cast(Message, State) ->
+ vlog("unexpected message(call): ~p",[Message]),
+ {noreply, State}.
+
+
+handle_info(poll_time,State) ->
+ {{Description,Uri},Uris} = get_uri(State#state.uris),
+ vlog("poll time for ~s",[Description]),
+ do_poll(State#state.host,State#state.port,Uri),
+ Ref = tcreate(State#state.ptime),
+ {noreply, State#state{tref = Ref, uris = Uris}};
+
+handle_info(Info, State) ->
+ vlog("unexpected message(info): ~p",[Info]),
+ {noreply, State}.
+
+
+terminate(Reason,State) ->
+ tcancel(State#state.tref),
+ log_close(get(log_file)),
+ ok.
+
+
+get_uri([Uri|Uris]) ->
+ {Uri,Uris++[Uri]}.
+
+
+do_poll(Host,Port,Uri) ->
+ (catch poll(create(Host,Port),Uri,"200")).
+
+poll({ok,Socket},Uri,ExpStatus) ->
+ vtrace("poll -> entry with Socket: ~p",[Socket]),
+ put(latest_requested_uri,Uri),
+ Req = "GET " ++ Uri ++ " HTTP/1.0\r\n\r\n",
+ await_poll_response(send(Socket,Req),Socket,ExpStatus);
+poll({error,Reason},_Req,_ExpStatus) ->
+ verror("failed creating socket: ~p",[Reason]),
+ log("failed creating socket: ~p",[Reason]),
+ exit({error,Reason});
+poll(O,_Req,_ExpStatus) ->
+ verror("unexpected result from socket create: ~p",[O]),
+ log("unexpected result from socket create: ~p",[O]),
+ exit({unexpected_result,O}).
+
+await_poll_response(ok,Socket,ExpStatusCode) ->
+ vtrace("await_poll_response -> awaiting response with status ~s",
+ [ExpStatusCode]),
+ receive
+ {tcp_closed,Socket} ->
+ verror("connection closed when awaiting poll response"),
+ log("connection closed when awaiting reply to GET of '~s'",
+ [get(latest_requested_uri)]),
+ exit(connection_closed);
+ {tcp,Socket,Response} ->
+ vdebug("received response"),
+ validate(ExpStatusCode,Socket,Response)
+ after 10000 ->
+ verror("connection timeout waiting for poll response",[]),
+ log("connection timeout waiting for reply to GET of '~s'",
+ [get(latest_requested_uri)]),
+ exit(connection_timed_out)
+ end;
+await_poll_response(Error,_Socket,_ExpStatusCode) ->
+ verror("failed sending GET request for '~s' for reason: ~p",
+ [get(latest_requested_uri),Error]),
+ log("failed sending GET request for '~s' for reason: ~p",
+ [get(latest_requested_uri),Error]),
+ exit(Error).
+
+
+validate(ExpStatusCode,Socket,Response) ->
+ Sz = sz(Response),
+ vtrace("validate -> Entry with ~p bytes response",[Sz]),
+ Size = trash_the_rest(Socket,Sz),
+ close(Socket),
+ case inets_regexp:split(Response," ") of
+ {ok,["HTTP/1.0",ExpStatusCode|_]} ->
+ vlog("response (~p bytes) was ok",[Size]),
+ ok;
+ {ok,["HTTP/1.0",StatusCode|_]} ->
+ verror("unexpected response status received: ~s => ~s",
+ [StatusCode,status_to_message(StatusCode)]),
+ log("unexpected result to GET of '~s': ~s => ~s",
+ [get(latest_requested_uri),StatusCode,
+ status_to_message(StatusCode)]),
+ exit({unexpected_response_code,StatusCode,ExpStatusCode})
+ end.
+
+
+%% ------------------------------------------------------------------
+
+trash_the_rest(Socket,N) ->
+ receive
+ {tcp, Socket, Trash} ->
+ vtrace("trash_the_rest -> trash ~p bytes",[sz(Trash)]),
+ trash_the_rest(Socket,add(N,sz(Trash)));
+ {tcp_closed, Socket} ->
+ vdebug("socket closed after receiving ~p bytes",[N]),
+ N
+ after 10000 ->
+ verror("connection timeout waiting for message"),
+ exit(connection_timed_out)
+ end.
+
+
+add(N1,N2) when integer(N1),integer(N2) ->
+ N1 + N2;
+add(N1,N2) when integer(N1) ->
+ N1;
+add(N1,N2) when integer(N2) ->
+ N2.
+
+sz(L) when list(L) ->
+ length(lists:flatten(L));
+sz(B) when binary(B) ->
+ size(B);
+sz(O) ->
+ {unknown_size,O}.
+
+
+%% --------------------------------------------------------------
+%%
+%% Status code to printable string
+%%
+
+status_to_message(L) when list(L) ->
+ case (catch list_to_integer(L)) of
+ I when integer(I) ->
+ status_to_message(I);
+ _ ->
+ io_lib:format("UNKNOWN STATUS CODE: '~p'",[L])
+ end;
+status_to_message(100) -> "Section 10.1.1: Continue";
+status_to_message(101) -> "Section 10.1.2: Switching Protocols";
+status_to_message(200) -> "Section 10.2.1: OK";
+status_to_message(201) -> "Section 10.2.2: Created";
+status_to_message(202) -> "Section 10.2.3: Accepted";
+status_to_message(203) -> "Section 10.2.4: Non-Authoritative Information";
+status_to_message(204) -> "Section 10.2.5: No Content";
+status_to_message(205) -> "Section 10.2.6: Reset Content";
+status_to_message(206) -> "Section 10.2.7: Partial Content";
+status_to_message(300) -> "Section 10.3.1: Multiple Choices";
+status_to_message(301) -> "Section 10.3.2: Moved Permanently";
+status_to_message(302) -> "Section 10.3.3: Found";
+status_to_message(303) -> "Section 10.3.4: See Other";
+status_to_message(304) -> "Section 10.3.5: Not Modified";
+status_to_message(305) -> "Section 10.3.6: Use Proxy";
+status_to_message(307) -> "Section 10.3.8: Temporary Redirect";
+status_to_message(400) -> "Section 10.4.1: Bad Request";
+status_to_message(401) -> "Section 10.4.2: Unauthorized";
+status_to_message(402) -> "Section 10.4.3: Peyment Required";
+status_to_message(403) -> "Section 10.4.4: Forbidden";
+status_to_message(404) -> "Section 10.4.5: Not Found";
+status_to_message(405) -> "Section 10.4.6: Method Not Allowed";
+status_to_message(406) -> "Section 10.4.7: Not Acceptable";
+status_to_message(407) -> "Section 10.4.8: Proxy Authentication Required";
+status_to_message(408) -> "Section 10.4.9: Request Time-Out";
+status_to_message(409) -> "Section 10.4.10: Conflict";
+status_to_message(410) -> "Section 10.4.11: Gone";
+status_to_message(411) -> "Section 10.4.12: Length Required";
+status_to_message(412) -> "Section 10.4.13: Precondition Failed";
+status_to_message(413) -> "Section 10.4.14: Request Entity Too Large";
+status_to_message(414) -> "Section 10.4.15: Request-URI Too Large";
+status_to_message(415) -> "Section 10.4.16: Unsupported Media Type";
+status_to_message(416) -> "Section 10.4.17: Requested range not satisfiable";
+status_to_message(417) -> "Section 10.4.18: Expectation Failed";
+status_to_message(500) -> "Section 10.5.1: Internal Server Error";
+status_to_message(501) -> "Section 10.5.2: Not Implemented";
+status_to_message(502) -> "Section 10.5.3: Bad Gatteway";
+status_to_message(503) -> "Section 10.5.4: Service Unavailable";
+status_to_message(504) -> "Section 10.5.5: Gateway Time-out";
+status_to_message(505) -> "Section 10.5.6: HTTP Version not supported";
+status_to_message(Code) -> io_lib:format("Unknown status code: ~p",[Code]).
+
+
+%% ----------------------------------------------------------------
+
+create(Host,Port) ->
+ vtrace("create -> ~n\tHost: ~s~n\tPort: ~p",[Host,Port]),
+ case gen_tcp:connect(Host,Port,[{packet,0},{reuseaddr,true}]) of
+ {ok,Socket} ->
+ {ok,Socket};
+ {error,{enfile,_}} ->
+ {error,enfile};
+ Error ->
+ Error
+ end.
+
+close(Socket) ->
+ gen_tcp:close(Socket).
+
+
+send(Socket,Data) ->
+ vtrace("send -> send ~p bytes of data",[length(Data)]),
+ gen_tcp:send(Socket,Data).
+
+
+%% ----------------------------------------------------------------
+
+tstart() ->
+ timer:start().
+
+tcreate(Time) ->
+ {ok,Ref} = timer:send_after(Time,poll_time),
+ Ref.
+
+tcancel(Ref) ->
+ timer:cancel(Ref).
+
+%% ----------------------------------------------------------------
+
+log_open(undefined) ->
+ ok;
+log_open(FileName) ->
+ put(log_file,fopen(FileName)).
+
+log_close(undefined) ->
+ ok;
+log_close(Fd) ->
+ fclose(Fd).
+
+log(F) ->
+ log(F,[]).
+
+log(F,A) ->
+ {{Year,Month,Day},{Hour,Min,Sec}} = local_time(),
+ fwrite(get(log_file),
+ "~w.~w.~w ~w.~w.~w " ++ F ++ "~n",
+ [Year,Month,Day,Hour,Min,Sec] ++ A).
+
+%% ----------------------------------------------------------------
+
+fopen(Name) ->
+ {ok,Fd} = file:open(Name,[write]),
+ Fd.
+
+fclose(Fd) ->
+ file:close(Fd).
+
+fwrite(undefined,_F,_A) ->
+ ok;
+fwrite(Fd,F,A) ->
+ io:format(Fd,F,A).
+
+
+%% ----------------------------------------------------------------
+
+get_poll_time(Opts) ->
+ get_option(poll_time,Opts,?default_poll_time).
+
+get_log_file(Opts) ->
+ get_option(log_file,Opts).
+
+get_uris(Opts) ->
+ get_option(uris,Opts,[]).
+
+get_verbosity(Opts) ->
+ get_option(verbosity,Opts,?default_verbosity).
+
+get_option(Opt,Opts) ->
+ get_option(Opt,Opts,undefined).
+
+get_option(Opt,Opts,Default) ->
+ case lists:keysearch(Opt,1,Opts) of
+ {value,{Opt,Value}} ->
+ Value;
+ false ->
+ Default
+ end.
+
+%% ----------------------------------------------------------------
+
+%% sleep(T) -> receive after T -> ok end.
+
+%% ----------------------------------------------------------------
+
+%% vtrace(F) -> vprint(get(verbosity),trace,F,[]).
+vtrace(F,A) -> vprint(get(verbosity),trace,F,A).
+
+vdebug(F) -> vprint(get(verbosity),debug,F,[]).
+vdebug(F,A) -> vprint(get(verbosity),debug,F,A).
+
+vlog(F) -> vprint(get(verbosity),log,F,[]).
+vlog(F,A) -> vprint(get(verbosity),log,F,A).
+
+verror(F) -> vprint(get(verbosity),error,F,[]).
+verror(F,A) -> vprint(get(verbosity),error,F,A).
+
+vprint(trace,Severity,F,A) -> vprint(Severity,F,A);
+vprint(debug,trace,F,A) -> ok;
+vprint(debug,Severity,F,A) -> vprint(Severity,F,A);
+vprint(log,log,F,A) -> vprint(log,F,A);
+vprint(log,error,F,A) -> vprint(log,F,A);
+vprint(error,error,F,A) -> vprint(error,F,A);
+vprint(_Verbosity,_Severity,_F,_A) -> ok.
+
+vprint(Severity,F,A) ->
+ {{Year,Month,Day},{Hour,Min,Sec}} = local_time(),
+ io:format("~w.~w.~w ~w.~w.~w " ++ image_of(Severity) ++ F ++ "~n",
+ [Year,Month,Day,Hour,Min,Sec] ++ A).
+
+image_of(error) -> "ERR: ";
+image_of(log) -> "LOG: ";
+image_of(debug) -> "DBG: ";
+image_of(trace) -> "TRC: ".
+
+local_time() -> calendar:local_time().
+
+
+
+
+