1%% ``Licensed under the Apache License, Version 2.0 (the "License");
2%% you may not use this file except in compliance with the License.
3%% You may obtain a copy of the License at
4%%
5%%     http://www.apache.org/licenses/LICENSE-2.0
6%%
7%% Unless required by applicable law or agreed to in writing, software
8%% distributed under the License is distributed on an "AS IS" BASIS,
9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10%% See the License for the specific language governing permissions and
11%% limitations under the License.
12%%
13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15%% AB. All Rights Reserved.''
16%%
17%%     $Id: httpd.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
18%%
19-module(httpd).
20-export([multi_start/1, multi_start_link/1,
21	 start/0, start/1, start/2,
22	 start_link/0, start_link/1, start_link/2,
23	 start_child/0,start_child/1,
24	 multi_stop/1,
25	 stop/0,stop/1,stop/2,
26	 stop_child/0,stop_child/1,stop_child/2,
27	 multi_restart/1,
28	 restart/0,restart/1,restart/2,
29	 parse_query/1]).
30
31%% Optional start related stuff...
32-export([load/1,        load_mime_types/1,
33	 start2/1,      start2/2,
34	 start_link2/1, start_link2/2,
35	 stop2/1]).
36
37%% Management stuff
38-export([block/0,block/1,block/2,block/3,block/4,
39	 unblock/0,unblock/1,unblock/2]).
40
41%% Debugging and status info stuff...
42-export([verbosity/3,verbosity/4]).
43-export([get_status/1,get_status/2,get_status/3,
44	 get_admin_state/0,get_admin_state/1,get_admin_state/2,
45	 get_usage_state/0,get_usage_state/1,get_usage_state/2]).
46
47-include("httpd.hrl").
48
49-define(D(F, A), io:format("~p:" ++ F ++ "~n", [?MODULE|A])).
50
51
52%% start
53
54start() ->
55    start("/var/tmp/server_root/conf/8888.conf").
56
57start(ConfigFile) ->
58    %% ?D("start(~s) -> entry", [ConfigFile]),
59    start(ConfigFile, []).
60
61start(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) ->
62    httpd_sup:start(ConfigFile, Verbosity).
63
64
65%% start_link
66
67start_link() ->
68    start("/var/tmp/server_root/conf/8888.conf").
69
70start_link(ConfigFile) ->
71    start_link(ConfigFile, []).
72
73start_link(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) ->
74    httpd_sup:start_link(ConfigFile, Verbosity).
75
76
77%% start2 & start_link2
78
79start2(Config) ->
80    start2(Config, []).
81
82start2(Config, Verbosity) when list(Config), list(Verbosity) ->
83    httpd_sup:start2(Config, Verbosity).
84
85start_link2(Config) ->
86    start_link2(Config, []).
87
88start_link2(Config, Verbosity) when list(Config), list(Verbosity) ->
89    httpd_sup:start_link2(Config, Verbosity).
90
91
92%% stop
93
94stop() ->
95  stop(8888).
96
97stop(Port) when integer(Port) ->
98    stop(undefined, Port);
99stop(Pid) when pid(Pid) ->
100    httpd_sup:stop(Pid);
101stop(ConfigFile) when list(ConfigFile) ->
102    %% ?D("stop(~s) -> entry", [ConfigFile]),
103    httpd_sup:stop(ConfigFile).
104
105stop(Addr, Port) when integer(Port) ->
106    httpd_sup:stop(Addr, Port).
107
108stop2(Config) when list(Config) ->
109    httpd_sup:stop2(Config).
110
111%% start_child
112
113start_child() ->
114    start_child("/var/tmp/server_root/conf/8888.conf").
115
116start_child(ConfigFile) ->
117    start_child(ConfigFile, []).
118
119start_child(ConfigFile, Verbosity) ->
120    inets_sup:start_child(ConfigFile, Verbosity).
121
122
123%% stop_child
124
125stop_child() ->
126  stop_child(8888).
127
128stop_child(Port) ->
129    stop_child(undefined,Port).
130
131stop_child(Addr, Port) when integer(Port) ->
132    inets_sup:stop_child(Addr, Port).
133
134
135%% multi_start
136
137multi_start(MultiConfigFile) ->
138    case read_multi_file(MultiConfigFile) of
139	{ok,ConfigFiles} ->
140	    mstart(ConfigFiles);
141	Error ->
142	    Error
143    end.
144
145mstart(ConfigFiles) ->
146    mstart(ConfigFiles,[]).
147mstart([],Results) ->
148    {ok,lists:reverse(Results)};
149mstart([H|T],Results) ->
150    Res = start(H),
151    mstart(T,[Res|Results]).
152
153
154%% multi_start_link
155
156multi_start_link(MultiConfigFile) ->
157    case read_multi_file(MultiConfigFile) of
158	{ok,ConfigFiles} ->
159	    mstart_link(ConfigFiles);
160	Error ->
161	    Error
162    end.
163
164mstart_link(ConfigFiles) ->
165    mstart_link(ConfigFiles,[]).
166mstart_link([],Results) ->
167    {ok,lists:reverse(Results)};
168mstart_link([H|T],Results) ->
169    Res = start_link(H),
170    mstart_link(T,[Res|Results]).
171
172
173%% multi_stop
174
175multi_stop(MultiConfigFile) ->
176    case read_multi_file(MultiConfigFile) of
177	{ok,ConfigFiles} ->
178	    mstop(ConfigFiles);
179	Error ->
180	    Error
181    end.
182
183mstop(ConfigFiles) ->
184    mstop(ConfigFiles,[]).
185mstop([],Results) ->
186    {ok,lists:reverse(Results)};
187mstop([H|T],Results) ->
188    Res = stop(H),
189    mstop(T,[Res|Results]).
190
191
192%% multi_restart
193
194multi_restart(MultiConfigFile) ->
195    case read_multi_file(MultiConfigFile) of
196	{ok,ConfigFiles} ->
197	    mrestart(ConfigFiles);
198	Error ->
199	    Error
200    end.
201
202mrestart(ConfigFiles) ->
203    mrestart(ConfigFiles,[]).
204mrestart([],Results) ->
205    {ok,lists:reverse(Results)};
206mrestart([H|T],Results) ->
207    Res = restart(H),
208    mrestart(T,[Res|Results]).
209
210
211%% restart
212
213restart() -> restart(undefined,8888).
214
215restart(Port) when integer(Port) ->
216    restart(undefined,Port);
217restart(ConfigFile) when list(ConfigFile) ->
218    case get_addr_and_port(ConfigFile) of
219	{ok,Addr,Port} ->
220	    restart(Addr,Port);
221	Error ->
222	    Error
223    end.
224
225
226restart(Addr,Port) when integer(Port) ->
227    do_restart(Addr,Port).
228
229do_restart(Addr,Port) when integer(Port) ->
230    Name = make_name(Addr,Port),
231    case whereis(Name) of
232	Pid when pid(Pid) ->
233	    httpd_manager:restart(Pid);
234	_ ->
235	    {error,not_started}
236    end.
237
238
239%%% =========================================================
240%%% Function:    block/0, block/1, block/2, block/3, block/4
241%%%              block()
242%%%              block(Port)
243%%%              block(ConfigFile)
244%%%              block(Addr,Port)
245%%%              block(Port,Mode)
246%%%              block(ConfigFile,Mode)
247%%%              block(Addr,Port,Mode)
248%%%              block(ConfigFile,Mode,Timeout)
249%%%              block(Addr,Port,Mode,Timeout)
250%%%
251%%% Returns:     ok | {error,Reason}
252%%%
253%%% Description: This function is used to block an HTTP server.
254%%%              The blocking can be done in two ways,
255%%%              disturbing or non-disturbing. Default is disturbing.
256%%%              When a HTTP server is blocked, all requests are rejected
257%%%              (status code 503).
258%%%
259%%%              disturbing:
260%%%              By performing a disturbing block, the server
261%%%              is blocked forcefully and all ongoing requests
262%%%              are terminated. No new connections are accepted.
263%%%              If a timeout time is given then, on-going requests
264%%%              are given this much time to complete before the
265%%%              server is forcefully blocked. In this case no new
266%%%              connections is accepted.
267%%%
268%%%              non-disturbing:
269%%%              A non-disturbing block is more gracefull. No
270%%%              new connections are accepted, but the ongoing
271%%%              requests are allowed to complete.
272%%%              If a timeout time is given, it waits this long before
273%%%              giving up (the block operation is aborted and the
274%%%              server state is once more not-blocked).
275%%%
276%%% Types:       Port       -> integer()
277%%%              Addr       -> {A,B,C,D} | string() | undefined
278%%%              ConfigFile -> string()
279%%%              Mode       -> disturbing | non_disturbing
280%%%              Timeout    -> integer()
281%%%
282block() -> block(undefined,8888,disturbing).
283
284block(Port) when integer(Port) ->
285    block(undefined,Port,disturbing);
286
287block(ConfigFile) when list(ConfigFile) ->
288    case get_addr_and_port(ConfigFile) of
289	{ok,Addr,Port} ->
290	    block(Addr,Port,disturbing);
291	Error ->
292	    Error
293    end.
294
295block(Addr,Port) when integer(Port) ->
296    block(Addr,Port,disturbing);
297
298block(Port,Mode) when integer(Port), atom(Mode) ->
299    block(undefined,Port,Mode);
300
301block(ConfigFile,Mode) when list(ConfigFile), atom(Mode) ->
302    case get_addr_and_port(ConfigFile) of
303	{ok,Addr,Port} ->
304	    block(Addr,Port,Mode);
305	Error ->
306	    Error
307    end.
308
309
310block(Addr,Port,disturbing) when integer(Port) ->
311    do_block(Addr,Port,disturbing);
312block(Addr,Port,non_disturbing) when integer(Port) ->
313    do_block(Addr,Port,non_disturbing);
314
315block(ConfigFile,Mode,Timeout) when list(ConfigFile), atom(Mode), integer(Timeout) ->
316    case get_addr_and_port(ConfigFile) of
317	{ok,Addr,Port} ->
318	    block(Addr,Port,Mode,Timeout);
319	Error ->
320	    Error
321    end.
322
323
324block(Addr,Port,non_disturbing,Timeout) when integer(Port), integer(Timeout) ->
325    do_block(Addr,Port,non_disturbing,Timeout);
326block(Addr,Port,disturbing,Timeout) when integer(Port), integer(Timeout) ->
327    do_block(Addr,Port,disturbing,Timeout).
328
329do_block(Addr,Port,Mode) when integer(Port), atom(Mode) ->
330    Name = make_name(Addr,Port),
331    case whereis(Name) of
332	Pid when pid(Pid) ->
333	    httpd_manager:block(Pid,Mode);
334	_ ->
335	    {error,not_started}
336    end.
337
338
339do_block(Addr,Port,Mode,Timeout) when integer(Port), atom(Mode) ->
340    Name = make_name(Addr,Port),
341    case whereis(Name) of
342	Pid when pid(Pid) ->
343	    httpd_manager:block(Pid,Mode,Timeout);
344	_ ->
345	    {error,not_started}
346    end.
347
348
349%%% =========================================================
350%%% Function:    unblock/0, unblock/1, unblock/2
351%%%              unblock()
352%%%              unblock(Port)
353%%%              unblock(ConfigFile)
354%%%              unblock(Addr,Port)
355%%%
356%%% Description: This function is used to reverse a previous block
357%%%              operation on the HTTP server.
358%%%
359%%% Types:       Port       -> integer()
360%%%              Addr       -> {A,B,C,D} | string() | undefined
361%%%              ConfigFile -> string()
362%%%
363unblock()                        -> unblock(undefined,8888).
364unblock(Port) when integer(Port) -> unblock(undefined,Port);
365
366unblock(ConfigFile) when list(ConfigFile) ->
367    case get_addr_and_port(ConfigFile) of
368	{ok,Addr,Port} ->
369	    unblock(Addr,Port);
370	Error ->
371	    Error
372    end.
373
374unblock(Addr,Port) when integer(Port) ->
375    Name = make_name(Addr,Port),
376    case whereis(Name) of
377	Pid when pid(Pid) ->
378	    httpd_manager:unblock(Pid);
379	_ ->
380	    {error,not_started}
381    end.
382
383
384verbosity(Port,Who,Verbosity) ->
385    verbosity(undefined,Port,Who,Verbosity).
386
387verbosity(Addr,Port,Who,Verbosity) ->
388    Name = make_name(Addr,Port),
389    case whereis(Name) of
390	Pid when pid(Pid) ->
391	    httpd_manager:verbosity(Pid,Who,Verbosity);
392	_ ->
393	    not_started
394    end.
395
396
397%%% =========================================================
398%%% Function:    get_admin_state/0, get_admin_state/1, get_admin_state/2
399%%%              get_admin_state()
400%%%              get_admin_state(Port)
401%%%              get_admin_state(Addr,Port)
402%%%
403%%% Returns:     {ok,State} | {error,Reason}
404%%%
405%%% Description: This function is used to retrieve the administrative
406%%%              state of the HTTP server.
407%%%
408%%% Types:       Port    -> integer()
409%%%              Addr    -> {A,B,C,D} | string() | undefined
410%%%              State   -> unblocked | shutting_down | blocked
411%%%              Reason  -> term()
412%%%
413get_admin_state()                        -> get_admin_state(undefined,8888).
414get_admin_state(Port) when integer(Port) -> get_admin_state(undefined,Port);
415
416get_admin_state(ConfigFile) when list(ConfigFile) ->
417    case get_addr_and_port(ConfigFile) of
418	{ok,Addr,Port} ->
419	    unblock(Addr,Port);
420	Error ->
421	    Error
422    end.
423
424get_admin_state(Addr,Port) when integer(Port) ->
425    Name = make_name(Addr,Port),
426    case whereis(Name) of
427	Pid when pid(Pid) ->
428	    httpd_manager:get_admin_state(Pid);
429	_ ->
430	    {error,not_started}
431    end.
432
433
434
435%%% =========================================================
436%%% Function:    get_usage_state/0, get_usage_state/1, get_usage_state/2
437%%%              get_usage_state()
438%%%              get_usage_state(Port)
439%%%              get_usage_state(Addr,Port)
440%%%
441%%% Returns:     {ok,State} | {error,Reason}
442%%%
443%%% Description: This function is used to retrieve the usage
444%%%              state of the HTTP server.
445%%%
446%%% Types:       Port    -> integer()
447%%%              Addr    -> {A,B,C,D} | string() | undefined
448%%%              State   -> idle | active | busy
449%%%              Reason  -> term()
450%%%
451get_usage_state()                        -> get_usage_state(undefined,8888).
452get_usage_state(Port) when integer(Port) -> get_usage_state(undefined,Port);
453
454get_usage_state(ConfigFile) when list(ConfigFile) ->
455    case get_addr_and_port(ConfigFile) of
456	{ok,Addr,Port} ->
457	    unblock(Addr,Port);
458	Error ->
459	    Error
460    end.
461
462get_usage_state(Addr,Port) when integer(Port) ->
463    Name = make_name(Addr,Port),
464    case whereis(Name) of
465	Pid when pid(Pid) ->
466	    httpd_manager:get_usage_state(Pid);
467	_ ->
468	    {error,not_started}
469    end.
470
471
472
473%%% =========================================================
474%% Function:    get_status(ConfigFile)        -> Status
475%%              get_status(Port)              -> Status
476%%              get_status(Addr,Port)         -> Status
477%%              get_status(Port,Timeout)      -> Status
478%%              get_status(Addr,Port,Timeout) -> Status
479%%
480%% Arguments:   ConfigFile -> string()
481%%                            Configuration file from which Port and
482%%                            BindAddress will be extracted.
483%%              Addr       -> {A,B,C,D} | string()
484%%                            Bind Address of the http server
485%%              Port       -> integer()
486%%                            Port number of the http server
487%%              Timeout    -> integer()
488%%                            Timeout time for the call
489%%
490%% Returns:     Status -> list()
491%%
492%% Description: This function is used when the caller runs in the
493%%              same node as the http server or if calling with a
494%%              program such as erl_call (see erl_interface).
495%%
496
497get_status(ConfigFile) when list(ConfigFile) ->
498    case get_addr_and_port(ConfigFile) of
499	{ok,Addr,Port} ->
500	    get_status(Addr,Port);
501	Error ->
502	    Error
503    end;
504
505get_status(Port) when integer(Port) ->
506    get_status(undefined,Port,5000).
507
508get_status(Port,Timeout) when integer(Port), integer(Timeout) ->
509    get_status(undefined,Port,Timeout);
510
511get_status(Addr,Port) when list(Addr), integer(Port) ->
512    get_status(Addr,Port,5000).
513
514get_status(Addr,Port,Timeout) when integer(Port) ->
515    Name = make_name(Addr,Port),
516    case whereis(Name) of
517	Pid when pid(Pid) ->
518	    httpd_manager:get_status(Pid,Timeout);
519	_ ->
520	    not_started
521    end.
522
523
524%% load config
525
526load(ConfigFile) ->
527    httpd_conf:load(ConfigFile).
528
529load_mime_types(MimeTypesFile) ->
530    httpd_conf:load_mime_types(MimeTypesFile).
531
532
533%% parse_query
534
535parse_query(String) ->
536  {ok, SplitString} = regexp:split(String,"[&;]"),
537  foreach(SplitString).
538
539foreach([]) ->
540  [];
541foreach([KeyValue|Rest]) ->
542  {ok, Plus2Space, _} = regexp:gsub(KeyValue,"[\+]"," "),
543  case regexp:split(Plus2Space,"=") of
544    {ok,[Key|Value]} ->
545      [{httpd_util:decode_hex(Key),
546	httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)];
547    {ok,_} ->
548      foreach(Rest)
549  end.
550
551
552%% get_addr_and_port
553
554get_addr_and_port(ConfigFile) ->
555    case httpd_conf:load(ConfigFile) of
556	{ok,ConfigList} ->
557	    Port = httpd_util:key1search(ConfigList,port,80),
558	    Addr = httpd_util:key1search(ConfigList,bind_address),
559	    {ok,Addr,Port};
560	Error ->
561	    Error
562    end.
563
564
565%% make_name
566
567make_name(Addr,Port) ->
568    httpd_util:make_name("httpd",Addr,Port).
569
570
571%% Multi stuff
572%%
573
574read_multi_file(File) ->
575    read_mfile(file:open(File,[read])).
576
577read_mfile({ok,Fd}) ->
578    read_mfile(read_line(Fd),Fd,[]);
579read_mfile(Error) ->
580    Error.
581
582read_mfile(eof,_Fd,SoFar) ->
583    {ok,lists:reverse(SoFar)};
584read_mfile({error,Reason},_Fd,SoFar) ->
585    {error,Reason};
586read_mfile([$#|Comment],Fd,SoFar) ->
587    read_mfile(read_line(Fd),Fd,SoFar);
588read_mfile([],Fd,SoFar) ->
589    read_mfile(read_line(Fd),Fd,SoFar);
590read_mfile(Line,Fd,SoFar) ->
591    read_mfile(read_line(Fd),Fd,[Line|SoFar]).
592
593read_line(Fd)      -> read_line1(io:get_line(Fd,[])).
594read_line1(eof)    -> eof;
595read_line1(String) -> httpd_conf:clean(String).
596