aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src/http_server/httpd_manager.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/inets/src/http_server/httpd_manager.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/inets/src/http_server/httpd_manager.erl')
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl890
1 files changed, 890 insertions, 0 deletions
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
new file mode 100644
index 0000000000..f2e8763907
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -0,0 +1,890 @@
+%%
+%% %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_manager).
+
+-include("httpd.hrl").
+
+-behaviour(gen_server).
+
+%% Application internal API
+-export([start/2, start_link/2, start_link/3, start_link/4, stop/1, reload/2]).
+-export([new_connection/1, done_connection/1]).
+-export([config_lookup/2, config_lookup/3,
+ config_multi_lookup/2, config_multi_lookup/3,
+ config_match/2, config_match/3]).
+
+%% gen_server exports
+-export([init/1,
+ handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+
+%% Management exports
+-export([block/2, block/3, unblock/1]).
+-export([get_admin_state/1, get_usage_state/1]).
+-export([is_busy/1,is_busy/2,is_busy_or_blocked/1,is_blocked/1]). %% ???????
+-export([get_status/1, get_status/2]).
+
+-export([c/1]).
+
+-record(state,{socket_type = ip_comm,
+ config_file,
+ config_db = null,
+ connections, %% Current request handlers
+ admin_state = unblocked,
+ blocker_ref = undefined,
+ blocking_tmr = undefined,
+ status = []}).
+
+
+%%TODO: Clean up this module!
+
+c(Port) ->
+ Ref = httpd_util:make_name("httpd",undefined,Port),
+ call(Ref, fake_close).
+
+%%
+%% External API
+%%
+%% Deprecated
+start(ConfigFile, ConfigList) ->
+ Port = proplists:get_value(port,ConfigList,80),
+ Addr = proplists:get_value(bind_address, ConfigList),
+ Name = make_name(Addr,Port),
+ gen_server:start({local,Name},?MODULE,
+ [ConfigFile, ConfigList, 15000, Addr, Port],[]).
+
+%% Deprecated
+start_link(ConfigFile, ConfigList) ->
+ start_link(ConfigFile, ConfigList, 15000).
+
+start_link(ConfigFile, ConfigList, AcceptTimeout) ->
+ Port = proplists:get_value(port, ConfigList, 80),
+ Addr = proplists:get_value(bind_address, ConfigList),
+ Name = make_name(Addr, Port),
+
+ gen_server:start_link({local, Name},?MODULE,
+ [ConfigFile, ConfigList, AcceptTimeout, Addr, Port],[]).
+
+start_link(ConfigFile, ConfigList, AcceptTimeout, ListenSocket) ->
+ Port = proplists:get_value(port, ConfigList, 80),
+ Addr = proplists:get_value(bind_address, ConfigList),
+ Name = make_name(Addr, Port),
+
+ gen_server:start_link({local, Name},?MODULE,
+ [ConfigFile, ConfigList, AcceptTimeout, Addr,
+ Port, ListenSocket],[]).
+
+stop(ServerRef) ->
+ call(ServerRef, stop).
+
+reload(ServerRef, Conf) ->
+ call(ServerRef, {reload, Conf}).
+
+
+%%%----------------------------------------------------------------
+
+block(ServerRef, disturbing) ->
+ call(ServerRef,block);
+
+block(ServerRef, non_disturbing) ->
+ do_block(ServerRef, non_disturbing, infinity).
+
+block(ServerRef, Method, Timeout) ->
+ do_block(ServerRef, Method, Timeout).
+
+
+%% The reason for not using call here, is that the manager cannot
+%% _wait_ for completion of the requests. It must be able to do
+%% do other things at the same time as the blocking goes on.
+do_block(ServerRef, Method, infinity) ->
+ Ref = make_ref(),
+ cast(ServerRef, {block, Method, infinity, self(), Ref}),
+ receive
+ {block_reply, Reply, Ref} ->
+ Reply
+ end;
+do_block(ServerRef,Method,Timeout) when Timeout > 0 ->
+ Ref = make_ref(),
+ cast(ServerRef,{block,Method,Timeout,self(),Ref}),
+ receive
+ {block_reply,Reply,Ref} ->
+ Reply
+ end.
+
+
+%%%----------------------------------------------------------------
+
+%% unblock
+
+unblock(ServerRef) ->
+ call(ServerRef,unblock).
+
+%% get admin/usage state
+
+get_admin_state(ServerRef) ->
+ call(ServerRef,get_admin_state).
+
+get_usage_state(ServerRef) ->
+ call(ServerRef,get_usage_state).
+
+
+%% get_status
+
+get_status(ServerRef) ->
+ gen_server:call(ServerRef,get_status).
+
+get_status(ServerRef,Timeout) ->
+ gen_server:call(ServerRef,get_status,Timeout).
+
+%%
+%% Internal API
+%%
+
+
+%% new_connection
+
+new_connection(Manager) ->
+ gen_server:call(Manager, {new_connection, self()}, infinity).
+
+%% done
+
+done_connection(Manager) ->
+ gen_server:cast(Manager, {done_connection, self()}).
+
+
+%% is_busy(ServerRef) -> true | false
+%%
+%% Tests if the server is (in usage state) busy,
+%% i.e. has rached the heavy load limit.
+%%
+
+is_busy(ServerRef) ->
+ gen_server:call(ServerRef,is_busy).
+
+is_busy(ServerRef,Timeout) ->
+ gen_server:call(ServerRef,is_busy,Timeout).
+
+
+%% is_busy_or_blocked(ServerRef) -> busy | blocked | false
+%%
+%% Tests if the server is busy (usage state), i.e. has rached,
+%% the heavy load limit, or blocked (admin state) .
+%%
+
+is_busy_or_blocked(ServerRef) ->
+ gen_server:call(ServerRef,is_busy_or_blocked).
+
+
+%% is_blocked(ServerRef) -> true | false
+%%
+%% Tests if the server is blocked (admin state) .
+%%
+
+is_blocked(ServerRef) ->
+ gen_server:call(ServerRef,is_blocked).
+
+
+%%
+%% Module API. Theese functions are intended for use from modules only.
+%%
+
+config_lookup(Port, Query) ->
+ config_lookup(undefined, Port, Query).
+config_lookup(Addr, Port, Query) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_lookup, Query}).
+
+config_multi_lookup(Port, Query) ->
+ config_multi_lookup(undefined,Port,Query).
+config_multi_lookup(Addr,Port, Query) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_multi_lookup, Query}).
+
+config_match(Port, Pattern) ->
+ config_match(undefined,Port,Pattern).
+config_match(Addr, Port, Pattern) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_match, Pattern}).
+
+
+%%
+%% Server call-back functions
+%%
+
+%% init
+
+init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port]) ->
+ process_flag(trap_exit, true),
+ case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port)) of
+ {error, Reason} ->
+ String = lists:flatten(
+ io_lib:format("Failed initiating "
+ "web server: ~n~p~n~p~n",
+ [ConfigFile,Reason])),
+ error_logger:error_report(String),
+ {stop, {error, Reason}};
+ {ok, State} ->
+ {ok, State}
+ end;
+init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port,
+ ListenInfo]) ->
+ process_flag(trap_exit, true),
+ case (catch do_init(ConfigFile, ConfigList, AcceptTimeout,
+ Addr, Port, ListenInfo)) of
+ {error, Reason} ->
+ String = lists:flatten(
+ io_lib:format("Failed initiating "
+ "web server: ~n~p~n~p~n",
+ [ConfigFile,Reason])),
+ error_logger:error_report(String),
+ {stop, {error, Reason}};
+ {ok, State} ->
+ {ok, State}
+ end.
+
+do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
+ NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
+ ConfigDB = do_initial_store(ConfigList),
+ SocketType = httpd_conf:config(ConfigDB),
+ case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
+ Port, ConfigDB, AcceptTimeout) of
+ {ok, _Pid} ->
+ Status = [{max_conn,0}, {last_heavy_load,never},
+ {last_connection,never}],
+ State = #state{socket_type = SocketType,
+ config_file = NewConfigFile,
+ config_db = ConfigDB,
+ connections = [],
+ status = Status},
+ {ok, State};
+ Else ->
+ Else
+ end.
+
+do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) ->
+ NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
+ ConfigDB = do_initial_store(ConfigList),
+ SocketType = httpd_conf:config(ConfigDB),
+ case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
+ Port, ConfigDB,
+ AcceptTimeout, ListenInfo) of
+ {ok, _Pid} ->
+ Status = [{max_conn,0}, {last_heavy_load,never},
+ {last_connection,never}],
+ State = #state{socket_type = SocketType,
+ config_file = NewConfigFile,
+ config_db = ConfigDB,
+ connections = [],
+ status = Status},
+ {ok, State};
+ Else ->
+ Else
+ end.
+
+do_initial_store(ConfigList) ->
+ case httpd_conf:store(ConfigList) of
+ {ok, ConfigDB} ->
+ ConfigDB;
+ {error, Reason} ->
+ throw({error, Reason})
+ end.
+
+
+
+%% handle_call
+
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+
+handle_call({config_lookup, Query}, _From, State) ->
+ Res = httpd_util:lookup(State#state.config_db, Query),
+ {reply, Res, State};
+
+handle_call({config_multi_lookup, Query}, _From, State) ->
+ Res = httpd_util:multi_lookup(State#state.config_db, Query),
+ {reply, Res, State};
+
+handle_call({config_match, Query}, _From, State) ->
+ Res = ets:match_object(State#state.config_db, Query),
+ {reply, Res, State};
+
+handle_call(get_status, _From, State) ->
+ ManagerStatus = manager_status(self()),
+ S1 = [{current_conn,length(State#state.connections)}|State#state.status]++
+ [ManagerStatus],
+ {reply,S1,State};
+
+handle_call(is_busy, _From, State) ->
+ Reply = case get_ustate(State) of
+ busy ->
+ true;
+ _ ->
+ false
+ end,
+ {reply,Reply,State};
+
+handle_call(is_busy_or_blocked, _From, State) ->
+ Reply =
+ case get_astate(State) of
+ unblocked ->
+ case get_ustate(State) of
+ busy ->
+ busy;
+ _ ->
+ false
+ end;
+ _ ->
+ blocked
+ end,
+ {reply,Reply,State};
+
+handle_call(is_blocked, _From, State) ->
+ Reply =
+ case get_astate(State) of
+ unblocked ->
+ false;
+ _ ->
+ true
+ end,
+ {reply,Reply,State};
+
+handle_call(get_admin_state, _From, State) ->
+ Reply = get_astate(State),
+ {reply,Reply,State};
+
+handle_call(get_usage_state, _From, State) ->
+ Reply = get_ustate(State),
+ {reply,Reply,State};
+
+handle_call({reload, Conf}, _From, State)
+ when State#state.admin_state =:= blocked ->
+ case handle_reload(Conf, State) of
+ {stop, Reply,S1} ->
+ {stop, Reply, S1};
+ {_, Reply, S1} ->
+ {reply,Reply,S1}
+ end;
+
+handle_call({reload, _}, _From, State) ->
+ {reply,{error,{invalid_admin_state,State#state.admin_state}},State};
+
+handle_call(block, _From, State) ->
+ {Reply,S1} = handle_block(State),
+ {reply,Reply,S1};
+
+handle_call(unblock, {From,_Tag}, State) ->
+ {Reply,S1} = handle_unblock(State,From),
+ {reply, Reply, S1};
+
+handle_call({new_connection, Pid}, _From, State) ->
+ {Status, NewState} = handle_new_connection(State, Pid),
+ {reply, Status, NewState};
+
+handle_call(Request, From, State) ->
+ String =
+ lists:flatten(
+ io_lib:format("Unknown request "
+ "~n ~p"
+ "~nto manager (~p)"
+ "~nfrom ~p",
+ [Request, self(), From])),
+ report_error(State,String),
+ {reply, ok, State}.
+
+
+%% handle_cast
+
+handle_cast({done_connection, Pid}, State) ->
+ S1 = handle_done_connection(State, Pid),
+ {noreply, S1};
+
+handle_cast({block, disturbing, Timeout, From, Ref}, State) ->
+ S1 = handle_block(State, Timeout, From, Ref),
+ {noreply,S1};
+
+handle_cast({block, non_disturbing, Timeout, From, Ref}, State) ->
+ S1 = handle_nd_block(State, Timeout, From, Ref),
+ {noreply,S1};
+
+handle_cast(Message, State) ->
+ String =
+ lists:flatten(
+ io_lib:format("Unknown message "
+ "~n ~p"
+ "~nto manager (~p)",
+ [Message, self()])),
+ report_error(State, String),
+ {noreply, State}.
+
+%% handle_info
+
+handle_info({block_timeout, Method}, State) ->
+ S1 = handle_block_timeout(State,Method),
+ {noreply, S1};
+
+handle_info({'DOWN', Ref, process, _Object, _Info}, State) ->
+ S1 =
+ case State#state.blocker_ref of
+ Ref ->
+ handle_blocker_exit(State);
+ _ ->
+ %% Not our blocker, so ignore
+ State
+ end,
+ {noreply, S1};
+
+handle_info({'EXIT', _, normal}, State) ->
+ {noreply, State};
+
+handle_info({'EXIT', _, blocked}, S) ->
+ {noreply, S};
+
+handle_info({'EXIT', Pid, Reason}, State) ->
+ S1 = check_connections(State, Pid, Reason),
+ {noreply, S1};
+
+handle_info(Info, State) ->
+ String =
+ lists:flatten(
+ io_lib:format("Unknown info "
+ "~n ~p"
+ "~nto manager (~p)",
+ [Info, self()])),
+ report_error(State, String),
+ {noreply, State}.
+
+
+%% terminate
+
+terminate(_, #state{config_db = Db}) ->
+ httpd_conf:remove_all(Db),
+ ok.
+
+
+%% code_change({down,ToVsn}, State, Extra)
+%%
+
+code_change({down,_ToVsn}, State, _Extra) ->
+ {ok,State};
+
+%% code_change(FromVsn, State, Extra)
+%%
+code_change(_FromVsn, State, _Extra) ->
+ {ok,State}.
+
+
+
+%% -------------------------------------------------------------------------
+%% check_connection
+%%
+%%
+%%
+%%
+
+check_connections(#state{connections = []} = State, _Pid, _Reason) ->
+ State;
+check_connections(#state{admin_state = shutting_down,
+ connections = Connections} = State, Pid, Reason) ->
+ %% Could be a crashing request handler
+ case lists:delete(Pid, Connections) of
+ [] -> % Crashing request handler => block complete
+ String =
+ lists:flatten(
+ io_lib:format("request handler (~p) crashed:"
+ "~n ~p", [Pid, Reason])),
+ report_error(State, String),
+ demonitor_blocker(State#state.blocker_ref),
+ {Tmr,From,Ref} = State#state.blocking_tmr,
+ stop_block_tmr(Tmr),
+ From ! {block_reply,ok,Ref},
+ State#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined};
+ Connections1 ->
+ State#state{connections = Connections1}
+ end;
+check_connections(#state{connections = Connections} = State, Pid, Reason) ->
+ case lists:delete(Pid, Connections) of
+ Connections -> % Not a request handler, so ignore
+ State;
+ NewConnections ->
+ String =
+ lists:flatten(
+ io_lib:format("request handler (~p) crashed:"
+ "~n ~p", [Pid, Reason])),
+ report_error(State, String),
+ State#state{connections = NewConnections}
+ end.
+
+
+%% -------------------------------------------------------------------------
+%% handle_[new | done]_connection
+%%
+%%
+%%
+%%
+
+handle_new_connection(State, Handler) ->
+ UsageState = get_ustate(State),
+ AdminState = get_astate(State),
+ handle_new_connection(UsageState, AdminState, State, Handler).
+
+handle_new_connection(busy, unblocked, State, _Handler) ->
+ Status = update_heavy_load_status(State#state.status),
+ {{reject, busy},
+ State#state{status = Status}};
+
+handle_new_connection(_UsageState, unblocked, State, Handler) ->
+ Connections = State#state.connections,
+ Status = update_connection_status(State#state.status,
+ length(Connections)+1),
+ link(Handler),
+ {{ok, accept},
+ State#state{connections = [Handler|Connections], status = Status}};
+
+handle_new_connection(_UsageState, _AdminState, State, _Handler) ->
+ {{reject, blocked},
+ State}.
+
+handle_done_connection(#state{admin_state = shutting_down,
+ connections = Connections} = State, Handler) ->
+ unlink(Handler),
+ case lists:delete(Handler, Connections) of
+ [] -> % Ok, block complete
+ demonitor_blocker(State#state.blocker_ref),
+ {Tmr,From,Ref} = State#state.blocking_tmr,
+ stop_block_tmr(Tmr),
+ From ! {block_reply,ok,Ref},
+ State#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined};
+ Connections1 ->
+ State#state{connections = Connections1}
+ end;
+
+handle_done_connection(#state{connections = Connections} = State, Handler) ->
+ State#state{connections = lists:delete(Handler, Connections)}.
+
+
+%% -------------------------------------------------------------------------
+%% handle_block
+%%
+%%
+%%
+%%
+handle_block(#state{admin_state = AdminState} = S) ->
+ handle_block(S, AdminState).
+
+handle_block(S,unblocked) ->
+ %% Kill all connections
+ [kill_handler(Pid) || Pid <- S#state.connections],
+ {ok,S#state{connections = [], admin_state = blocked}};
+handle_block(S,blocked) ->
+ {ok,S};
+handle_block(S,shutting_down) ->
+ {{error,shutting_down},S}.
+
+
+kill_handler(Pid) ->
+ exit(Pid, blocked).
+
+handle_block(S,Timeout,From,Ref) when Timeout >= 0 ->
+ do_block(S,Timeout,From,Ref);
+
+handle_block(S,Timeout,From,Ref) ->
+ Reply = {error,{invalid_block_request,Timeout}},
+ From ! {block_reply,Reply,Ref},
+ S.
+
+do_block(S,Timeout,From,Ref) ->
+ case S#state.connections of
+ [] ->
+ %% Already in idle usage state => go directly to blocked
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked};
+ _ ->
+ %% Active or Busy usage state => go to shutting_down
+ %% Make sure we get to know if blocker dies...
+ MonitorRef = monitor_blocker(From),
+ Tmr = {start_block_tmr(Timeout,disturbing),From,Ref},
+ S#state{admin_state = shutting_down,
+ blocker_ref = MonitorRef, blocking_tmr = Tmr}
+ end.
+
+handle_nd_block(S,infinity,From,Ref) ->
+ do_nd_block(S,infinity,From,Ref);
+
+handle_nd_block(S,Timeout,From,Ref) when Timeout >= 0 ->
+ do_nd_block(S,Timeout,From,Ref);
+
+handle_nd_block(S,Timeout,From,Ref) ->
+ Reply = {error,{invalid_block_request,Timeout}},
+ From ! {block_reply,Reply,Ref},
+ S.
+
+do_nd_block(S,Timeout,From,Ref) ->
+ case S#state.connections of
+ [] ->
+ %% Already in idle usage state => go directly to blocked
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked};
+ _ ->
+ %% Active or Busy usage state => go to shutting_down
+ %% Make sure we get to know if blocker dies...
+ MonitorRef = monitor_blocker(From),
+ Tmr = {start_block_tmr(Timeout,non_disturbing),From,Ref},
+ S#state{admin_state = shutting_down,
+ blocker_ref = MonitorRef, blocking_tmr = Tmr}
+ end.
+
+handle_block_timeout(S,Method) ->
+ %% Time to take this to the road...
+ demonitor_blocker(S#state.blocker_ref),
+ handle_block_timeout1(S,Method,S#state.blocking_tmr).
+
+handle_block_timeout1(S,non_disturbing,{_,From,Ref}) ->
+ From ! {block_reply,{error,timeout},Ref},
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S,disturbing,{_,From,Ref}) ->
+ [exit(Pid,blocked) || Pid <- S#state.connections],
+
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S,Method,{_,From,Ref}) ->
+ From ! {block_reply,{error,{unknown_block_method,Method}},Ref},
+ S#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S, _Method, _TmrInfo) ->
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined}.
+
+handle_unblock(S, FromA) ->
+ handle_unblock(S, FromA, S#state.admin_state).
+
+handle_unblock(S, _FromA, unblocked) ->
+ {ok,S};
+handle_unblock(S, FromA, _AdminState) ->
+ stop_block_tmr(S#state.blocking_tmr),
+ case S#state.blocking_tmr of
+ {_Tmr,FromB,Ref} ->
+ %% Another process is trying to unblock
+ %% Inform the blocker
+ FromB ! {block_reply, {error,{unblocked,FromA}},Ref};
+ _ ->
+ ok
+ end,
+ {ok,S#state{admin_state = unblocked, blocking_tmr = undefined}}.
+
+%% The blocker died so we give up on the block.
+handle_blocker_exit(S) ->
+ {Tmr,_From,_Ref} = S#state.blocking_tmr,
+ stop_block_tmr(Tmr),
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined}.
+
+
+
+%% -------------------------------------------------------------------------
+%% handle_reload
+%%
+%%
+%%
+%%
+handle_reload(undefined, #state{config_file = undefined} = State) ->
+ {continue, {error, undefined_config_file}, State};
+handle_reload(undefined, #state{config_file = ConfigFile} = State) ->
+ case load_config(ConfigFile) of
+ {ok, Config} ->
+ do_reload(Config, State);
+ {error, Reason} ->
+ error_logger:error_msg("Bad config file: ~p~n", [Reason]),
+ {continue, {error, Reason}, State}
+ end;
+handle_reload(Config, State) ->
+ do_reload(Config, State).
+
+load_config(ConfigFile) ->
+ case httpd_conf:load(ConfigFile) of
+ {ok, Config} ->
+ httpd_conf:validate_properties(Config);
+ Error ->
+ Error
+ end.
+
+do_reload(Config, #state{config_db = Db} = State) ->
+ case (catch check_constant_values(Db, Config)) of
+ ok ->
+ %% If something goes wrong between the remove
+ %% and the store where fu-ed
+ httpd_conf:remove_all(Db),
+ case httpd_conf:store(Config) of
+ {ok, NewConfigDB} ->
+ {continue, ok, State#state{config_db = NewConfigDB}};
+ Error ->
+ {stop, Error, State}
+ end;
+ Error ->
+ {continue, Error, State}
+ end.
+
+check_constant_values(Db, Config) ->
+ %% Check port number
+ Port = httpd_util:lookup(Db,port),
+ case proplists:get_value(port,Config) of %% MUST be equal
+ Port ->
+ ok;
+ OtherPort ->
+ throw({error,{port_number_changed,Port,OtherPort}})
+ end,
+
+ %% Check bind address
+ Addr = httpd_util:lookup(Db,bind_address),
+ case proplists:get_value(bind_address, Config) of %% MUST be equal
+ Addr ->
+ ok;
+ OtherAddr ->
+ throw({error,{addr_changed,Addr,OtherAddr}})
+ end,
+
+ %% Check socket type
+ SockType = httpd_util:lookup(Db, socket_type),
+ case proplists:get_value(socket_type, Config) of %% MUST be equal
+ SockType ->
+ ok;
+ OtherSockType ->
+ throw({error,{sock_type_changed,SockType,OtherSockType}})
+ end,
+ ok.
+
+
+%% get_ustate(State) -> idle | active | busy
+%%
+%% Retrieve the usage state of the HTTP server:
+%% 0 active connection -> idle
+%% max_clients active connections -> busy
+%% Otherwise -> active
+%%
+get_ustate(State) ->
+ get_ustate(length(State#state.connections),State).
+
+get_ustate(0,_State) ->
+ idle;
+get_ustate(ConnectionCnt,State) ->
+ ConfigDB = State#state.config_db,
+ case httpd_util:lookup(ConfigDB, max_clients, 150) of
+ ConnectionCnt ->
+ busy;
+ _ ->
+ active
+ end.
+
+
+get_astate(S) -> S#state.admin_state.
+
+
+%% Timer handling functions
+start_block_tmr(infinity,_) ->
+ undefined;
+start_block_tmr(T,M) ->
+ erlang:send_after(T,self(),{block_timeout,M}).
+
+stop_block_tmr(undefined) ->
+ ok;
+stop_block_tmr(Ref) ->
+ erlang:cancel_timer(Ref).
+
+
+%% Monitor blocker functions
+monitor_blocker(Pid) when is_pid(Pid) ->
+ case (catch erlang:monitor(process,Pid)) of
+ {'EXIT', _Reason} ->
+ undefined;
+ MonitorRef ->
+ MonitorRef
+ end;
+monitor_blocker(_) ->
+ undefined.
+
+demonitor_blocker(undefined) ->
+ ok;
+demonitor_blocker(Ref) ->
+ (catch erlang:demonitor(Ref)).
+
+
+%% Some status utility functions
+
+update_heavy_load_status(Status) ->
+ update_status_with_time(Status,last_heavy_load).
+
+update_connection_status(Status,ConnCount) ->
+ S1 = case lists:keysearch(max_conn,1,Status) of
+ {value, {max_conn, C1}} when ConnCount > C1 ->
+ lists:keyreplace(max_conn,1,Status,{max_conn,ConnCount});
+ {value, {max_conn, _C2}} ->
+ Status;
+ false ->
+ [{max_conn, ConnCount} | Status]
+ end,
+ update_status_with_time(S1,last_connection).
+
+update_status_with_time(Status,Key) ->
+ lists:keyreplace(Key,1,Status,{Key,universal_time()}).
+
+universal_time() -> calendar:universal_time().
+
+manager_status(P) ->
+ Items = [status, message_queue_len, reductions,
+ heap_size, stack_size],
+ {manager_status, process_status(P,Items,[])}.
+
+
+process_status(P,[],L) ->
+ [{pid,P}|lists:reverse(L)];
+process_status(P,[H|T],L) ->
+ case (catch process_info(P,H)) of
+ {H, Value} ->
+ process_status(P,T,[{H,Value}|L]);
+ _ ->
+ process_status(P,T,[{H,undefined}|L])
+ end.
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd",Addr,Port).
+
+
+report_error(State,String) ->
+ Cdb = State#state.config_db,
+ error_logger:error_report(String),
+ mod_log:report_error(Cdb,String),
+ mod_disk_log:report_error(Cdb,String).
+
+%%
+call(ServerRef,Request) ->
+ gen_server:call(ServerRef,Request).
+
+cast(ServerRef,Message) ->
+ gen_server:cast(ServerRef,Message).
+