4
%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
22
-behaviour(gen_server).
26
-export([start/0, start_appup/2, start/3,stop/0,verbosity/1,poll_time/1]).
30
handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
33
-define(default_verbosity,error).
34
-define(default_poll_time,60000). %% 60 seconds
37
-record(state,{host = "", port = -1, ptime = -1, tref = none, uris = []}).
42
%% Description: Start polling HTTPD with default values
45
Options = default_options(otp),
46
start("gandalf", 8000, Options).
48
start_appup(Host, Port) ->
49
Options = default_options(top),
50
start(Host, Port, Options).
54
%% Description: Start polling HTTPD
60
%% Port number of HTTPD
62
%% Option = {poll_time,integer()} | {verbosity,verbosity()} |
63
%% {log_file,string()} | {uris,[uri()]}
64
%% verbosity() = silence | error | log | debug | trace
65
%% uri() = {string(),string}
66
%% First part is a descriptive string and the second
67
%% part is the actual URI.
69
start(Host,Port,Options) ->
70
gen_server:start({local,httpd_tester},?MODULE,[Host,Port,Options],[]).
73
gen_server:call(httpd_tester,stop).
76
default_options(UriDesc) ->
77
Verbosity = {verbosity,?default_verbosity},
78
Uris = {uris,uris(UriDesc)},
79
PollTime = {poll_time,?default_poll_time},
80
Logging = {log_file,"httpd_poll.log"},
81
[Verbosity, Uris, PollTime, Logging].
85
options(Options, default_options(otp), []).
87
options([], Defaults, Options) ->
89
options([{Key,Val} = Opt|Opts], Defaults, Options) ->
90
options(Opts, lists:keydelete(Key, 1, Defaults), [Opt|Options]).
94
set_verbosity(silence);
100
set_verbosity(debug);
102
set_verbosity(trace).
104
set_verbosity(Verbosity) ->
105
gen_server:cast(httpd_tester,{verbosity,Verbosity}).
107
poll_time(NewTime) ->
108
gen_server:call(httpd_tester,{poll_time,NewTime}).
111
%% ----------------------------------------------------------------------
114
init([Host, Port, Options0]) ->
115
process_flag(trap_exit,true),
116
Options = options(Options0),
117
put(verbosity,get_verbosity(Options)),
118
log_open(get_log_file(Options)),
120
PollTime = get_poll_time(Options),
121
Ref = tcreate(PollTime),
123
{ok,#state{host = Host,
127
uris = get_uris(Options)}}.
135
uri_internal_product1(),
136
uri_internal_product2(),
137
uri_p7a_test_results(),
146
uri_internal_product1() ->
147
{"product internal page (1)","/product/internal/"}.
149
uri_internal_product2() ->
150
{"product internal page (2)","/product/internal"}.
152
uri_p7a_test_results() ->
153
{"test summery index page",
154
"/product/internal/test/test_results/progress_P7A/index.html"}.
157
{"bjorns home page (1)","/~bjorn/"}.
160
{"bjorns home page (2)","/~bjorn"}.
163
{"ronja top page","/ronja/"}.
166
handle_call(stop, _From, State) ->
167
vlog("stop request"),
168
{stop, normal, ok, State};
170
handle_call({poll_time,NewTime}, _From, State) ->
171
vlog("set new poll time: ~p",[NewTime]),
172
OldTime = State#state.ptime,
173
{stop, normal, OldTime, State#state{ptime = NewTime}};
175
handle_call(Request, _From, State) ->
176
vlog("unexpected request(call): ~p",[Request]),
180
handle_cast({verbosity,Verbosity}, State) ->
181
vlog("set (new) verbosity to: ~p",[Verbosity]),
182
put(verbosity,Verbosity),
185
handle_cast(Message, State) ->
186
vlog("unexpected message(call): ~p",[Message]),
190
handle_info(poll_time,State) ->
191
{{Description,Uri},Uris} = get_uri(State#state.uris),
192
vlog("poll time for ~s",[Description]),
193
do_poll(State#state.host,State#state.port,Uri),
194
Ref = tcreate(State#state.ptime),
195
{noreply, State#state{tref = Ref, uris = Uris}};
197
handle_info(Info, State) ->
198
vlog("unexpected message(info): ~p",[Info]),
202
terminate(Reason,State) ->
203
tcancel(State#state.tref),
204
log_close(get(log_file)),
208
get_uri([Uri|Uris]) ->
212
do_poll(Host,Port,Uri) ->
213
(catch poll(create(Host,Port),Uri,"200")).
215
poll({ok,Socket},Uri,ExpStatus) ->
216
vtrace("poll -> entry with Socket: ~p",[Socket]),
217
put(latest_requested_uri,Uri),
218
Req = "GET " ++ Uri ++ " HTTP/1.0\r\n\r\n",
219
await_poll_response(send(Socket,Req),Socket,ExpStatus);
220
poll({error,Reason},_Req,_ExpStatus) ->
221
verror("failed creating socket: ~p",[Reason]),
222
log("failed creating socket: ~p",[Reason]),
223
exit({error,Reason});
224
poll(O,_Req,_ExpStatus) ->
225
verror("unexpected result from socket create: ~p",[O]),
226
log("unexpected result from socket create: ~p",[O]),
227
exit({unexpected_result,O}).
229
await_poll_response(ok,Socket,ExpStatusCode) ->
230
vtrace("await_poll_response -> awaiting response with status ~s",
233
{tcp_closed,Socket} ->
234
verror("connection closed when awaiting poll response"),
235
log("connection closed when awaiting reply to GET of '~s'",
236
[get(latest_requested_uri)]),
237
exit(connection_closed);
238
{tcp,Socket,Response} ->
239
vdebug("received response"),
240
validate(ExpStatusCode,Socket,Response)
242
verror("connection timeout waiting for poll response",[]),
243
log("connection timeout waiting for reply to GET of '~s'",
244
[get(latest_requested_uri)]),
245
exit(connection_timed_out)
247
await_poll_response(Error,_Socket,_ExpStatusCode) ->
248
verror("failed sending GET request for '~s' for reason: ~p",
249
[get(latest_requested_uri),Error]),
250
log("failed sending GET request for '~s' for reason: ~p",
251
[get(latest_requested_uri),Error]),
255
validate(ExpStatusCode,Socket,Response) ->
257
vtrace("validate -> Entry with ~p bytes response",[Sz]),
258
Size = trash_the_rest(Socket,Sz),
260
case inets_regexp:split(Response," ") of
261
{ok,["HTTP/1.0",ExpStatusCode|_]} ->
262
vlog("response (~p bytes) was ok",[Size]),
264
{ok,["HTTP/1.0",StatusCode|_]} ->
265
verror("unexpected response status received: ~s => ~s",
266
[StatusCode,status_to_message(StatusCode)]),
267
log("unexpected result to GET of '~s': ~s => ~s",
268
[get(latest_requested_uri),StatusCode,
269
status_to_message(StatusCode)]),
270
exit({unexpected_response_code,StatusCode,ExpStatusCode})
274
%% ------------------------------------------------------------------
276
trash_the_rest(Socket,N) ->
278
{tcp, Socket, Trash} ->
279
vtrace("trash_the_rest -> trash ~p bytes",[sz(Trash)]),
280
trash_the_rest(Socket,add(N,sz(Trash)));
281
{tcp_closed, Socket} ->
282
vdebug("socket closed after receiving ~p bytes",[N]),
285
verror("connection timeout waiting for message"),
286
exit(connection_timed_out)
290
add(N1,N2) when integer(N1),integer(N2) ->
292
add(N1,N2) when integer(N1) ->
294
add(N1,N2) when integer(N2) ->
297
sz(L) when list(L) ->
298
length(lists:flatten(L));
299
sz(B) when binary(B) ->
305
%% --------------------------------------------------------------
307
%% Status code to printable string
310
status_to_message(L) when list(L) ->
311
case (catch list_to_integer(L)) of
313
status_to_message(I);
315
io_lib:format("UNKNOWN STATUS CODE: '~p'",[L])
317
status_to_message(100) -> "Section 10.1.1: Continue";
318
status_to_message(101) -> "Section 10.1.2: Switching Protocols";
319
status_to_message(200) -> "Section 10.2.1: OK";
320
status_to_message(201) -> "Section 10.2.2: Created";
321
status_to_message(202) -> "Section 10.2.3: Accepted";
322
status_to_message(203) -> "Section 10.2.4: Non-Authoritative Information";
323
status_to_message(204) -> "Section 10.2.5: No Content";
324
status_to_message(205) -> "Section 10.2.6: Reset Content";
325
status_to_message(206) -> "Section 10.2.7: Partial Content";
326
status_to_message(300) -> "Section 10.3.1: Multiple Choices";
327
status_to_message(301) -> "Section 10.3.2: Moved Permanently";
328
status_to_message(302) -> "Section 10.3.3: Found";
329
status_to_message(303) -> "Section 10.3.4: See Other";
330
status_to_message(304) -> "Section 10.3.5: Not Modified";
331
status_to_message(305) -> "Section 10.3.6: Use Proxy";
332
status_to_message(307) -> "Section 10.3.8: Temporary Redirect";
333
status_to_message(400) -> "Section 10.4.1: Bad Request";
334
status_to_message(401) -> "Section 10.4.2: Unauthorized";
335
status_to_message(402) -> "Section 10.4.3: Peyment Required";
336
status_to_message(403) -> "Section 10.4.4: Forbidden";
337
status_to_message(404) -> "Section 10.4.5: Not Found";
338
status_to_message(405) -> "Section 10.4.6: Method Not Allowed";
339
status_to_message(406) -> "Section 10.4.7: Not Acceptable";
340
status_to_message(407) -> "Section 10.4.8: Proxy Authentication Required";
341
status_to_message(408) -> "Section 10.4.9: Request Time-Out";
342
status_to_message(409) -> "Section 10.4.10: Conflict";
343
status_to_message(410) -> "Section 10.4.11: Gone";
344
status_to_message(411) -> "Section 10.4.12: Length Required";
345
status_to_message(412) -> "Section 10.4.13: Precondition Failed";
346
status_to_message(413) -> "Section 10.4.14: Request Entity Too Large";
347
status_to_message(414) -> "Section 10.4.15: Request-URI Too Large";
348
status_to_message(415) -> "Section 10.4.16: Unsupported Media Type";
349
status_to_message(416) -> "Section 10.4.17: Requested range not satisfiable";
350
status_to_message(417) -> "Section 10.4.18: Expectation Failed";
351
status_to_message(500) -> "Section 10.5.1: Internal Server Error";
352
status_to_message(501) -> "Section 10.5.2: Not Implemented";
353
status_to_message(502) -> "Section 10.5.3: Bad Gatteway";
354
status_to_message(503) -> "Section 10.5.4: Service Unavailable";
355
status_to_message(504) -> "Section 10.5.5: Gateway Time-out";
356
status_to_message(505) -> "Section 10.5.6: HTTP Version not supported";
357
status_to_message(Code) -> io_lib:format("Unknown status code: ~p",[Code]).
360
%% ----------------------------------------------------------------
363
vtrace("create -> ~n\tHost: ~s~n\tPort: ~p",[Host,Port]),
364
case gen_tcp:connect(Host,Port,[{packet,0},{reuseaddr,true}]) of
367
{error,{enfile,_}} ->
374
gen_tcp:close(Socket).
378
vtrace("send -> send ~p bytes of data",[length(Data)]),
379
gen_tcp:send(Socket,Data).
382
%% ----------------------------------------------------------------
388
{ok,Ref} = timer:send_after(Time,poll_time),
394
%% ----------------------------------------------------------------
396
log_open(undefined) ->
398
log_open(FileName) ->
399
put(log_file,fopen(FileName)).
401
log_close(undefined) ->
410
{{Year,Month,Day},{Hour,Min,Sec}} = local_time(),
411
fwrite(get(log_file),
412
"~w.~w.~w ~w.~w.~w " ++ F ++ "~n",
413
[Year,Month,Day,Hour,Min,Sec] ++ A).
415
%% ----------------------------------------------------------------
418
{ok,Fd} = file:open(Name,[write]),
424
fwrite(undefined,_F,_A) ->
430
%% ----------------------------------------------------------------
432
get_poll_time(Opts) ->
433
get_option(poll_time,Opts,?default_poll_time).
435
get_log_file(Opts) ->
436
get_option(log_file,Opts).
439
get_option(uris,Opts,[]).
441
get_verbosity(Opts) ->
442
get_option(verbosity,Opts,?default_verbosity).
444
get_option(Opt,Opts) ->
445
get_option(Opt,Opts,undefined).
447
get_option(Opt,Opts,Default) ->
448
case lists:keysearch(Opt,1,Opts) of
449
{value,{Opt,Value}} ->
455
%% ----------------------------------------------------------------
457
%% sleep(T) -> receive after T -> ok end.
459
%% ----------------------------------------------------------------
461
%% vtrace(F) -> vprint(get(verbosity),trace,F,[]).
462
vtrace(F,A) -> vprint(get(verbosity),trace,F,A).
464
vdebug(F) -> vprint(get(verbosity),debug,F,[]).
465
vdebug(F,A) -> vprint(get(verbosity),debug,F,A).
467
vlog(F) -> vprint(get(verbosity),log,F,[]).
468
vlog(F,A) -> vprint(get(verbosity),log,F,A).
470
verror(F) -> vprint(get(verbosity),error,F,[]).
471
verror(F,A) -> vprint(get(verbosity),error,F,A).
473
vprint(trace,Severity,F,A) -> vprint(Severity,F,A);
474
vprint(debug,trace,F,A) -> ok;
475
vprint(debug,Severity,F,A) -> vprint(Severity,F,A);
476
vprint(log,log,F,A) -> vprint(log,F,A);
477
vprint(log,error,F,A) -> vprint(log,F,A);
478
vprint(error,error,F,A) -> vprint(error,F,A);
479
vprint(_Verbosity,_Severity,_F,_A) -> ok.
481
vprint(Severity,F,A) ->
482
{{Year,Month,Day},{Hour,Min,Sec}} = local_time(),
483
io:format("~w.~w.~w ~w.~w.~w " ++ image_of(Severity) ++ F ++ "~n",
484
[Year,Month,Day,Hour,Min,Sec] ++ A).
486
image_of(error) -> "ERR: ";
487
image_of(log) -> "LOG: ";
488
image_of(debug) -> "DBG: ";
489
image_of(trace) -> "TRC: ".
491
local_time() -> calendar:local_time().