%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2010. 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). -behaviour(inets_service). -include("httpd.hrl"). -deprecated({start, 0, next_major_release}). -deprecated({start, 1, next_major_release}). -deprecated({start_link, 1, next_major_release}). -deprecated({start_child, 0, next_major_release}). -deprecated({start_child, 1, next_major_release}). -deprecated({stop, 0, next_major_release}). -deprecated({stop, 1, next_major_release}). -deprecated({stop, 2, next_major_release}). -deprecated({stop_child, 0, next_major_release}). -deprecated({stop_child, 1, next_major_release}). -deprecated({stop_child, 2, next_major_release}). -deprecated({restart, 0, next_major_release}). -deprecated({restart, 1, next_major_release}). -deprecated({restart, 2, next_major_release}). -deprecated({block, 0, next_major_release}). -deprecated({block, 1, next_major_release}). -deprecated({block, 2, next_major_release}). -deprecated({block, 3, next_major_release}). -deprecated({block, 4, next_major_release}). -deprecated({unblock, 0, next_major_release}). -deprecated({unblock, 1, next_major_release}). -deprecated({unblock, 2, next_major_release}). %% Behavior callbacks -export([start_standalone/1, start_service/1, stop_service/1, services/0, service_info/1]). %% API -export([parse_query/1, reload_config/2, info/1, info/2, info/3]). %% Deprecated -export([start/0, start/1, start_link/0, start_link/1, start_child/0,start_child/1, stop/0,stop/1,stop/2, stop_child/0,stop_child/1,stop_child/2, restart/0,restart/1,restart/2]). %% Management stuff should be internal functions %% Will be from r13 -export([block/0,block/1,block/2,block/3,block/4, unblock/0,unblock/1,unblock/2]). %% Internal Debugging and status info stuff... %% Keep for now should probably be moved to test catalog -export([get_status/1,get_status/2,get_status/3, get_admin_state/0,get_admin_state/1,get_admin_state/2, get_usage_state/0,get_usage_state/1,get_usage_state/2]). %%%======================================================================== %%% API %%%======================================================================== parse_query(String) -> {ok, SplitString} = inets_regexp:split(String,"[&;]"), foreach(SplitString). reload_config(Config = [Value| _], Mode) when is_tuple(Value) -> do_reload_config(Config, Mode); reload_config(ConfigFile, Mode) -> case httpd_conf:load(ConfigFile) of {ok, ConfigList} -> do_reload_config(ConfigList, Mode); Error -> Error end. info(Pid) when is_pid(Pid) -> info(Pid, []). info(Pid, Properties) when is_pid(Pid) andalso is_list(Properties) -> {ok, ServiceInfo} = service_info(Pid), Address = proplists:get_value(bind_address, ServiceInfo), Port = proplists:get_value(port, ServiceInfo), case Properties of [] -> info(Address, Port); _ -> info(Address, Port, Properties) end; info(Address, Port) when is_integer(Port) -> httpd_conf:get_config(Address, Port). info(Address, Port, Properties) when is_integer(Port) andalso is_list(Properties) -> httpd_conf:get_config(Address, Port, Properties). %%%======================================================================== %%% Behavior callbacks %%%======================================================================== start_standalone(Config) -> httpd_sup:start_link([{httpd, Config}], stand_alone). start_service(Conf) -> httpd_sup:start_child(Conf). stop_service({Address, Port}) -> httpd_sup:stop_child(Address, Port); stop_service(Pid) when is_pid(Pid) -> case service_info(Pid) of {ok, Info} -> Address = proplists:get_value(bind_address, Info), Port = proplists:get_value(port, Info), stop_service({Address, Port}); Error -> Error end. services() -> [{httpd, ChildPid} || {_, ChildPid, _, _} <- supervisor:which_children(httpd_sup)]. service_info(Pid) -> try [{ChildName, ChildPid} || {ChildName, ChildPid, _, _} <- supervisor:which_children(httpd_sup)] of Children -> child_name2info(child_name(Pid, Children)) catch exit:{noproc, _} -> {error, service_not_available} end. %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- child_name(_, []) -> undefined; child_name(Pid, [{Name, Pid} | _]) -> Name; child_name(Pid, [_ | Children]) -> child_name(Pid, Children). child_name2info(undefined) -> {error, no_such_service}; child_name2info({httpd_instance_sup, any, Port}) -> {ok, Host} = inet:gethostname(), Info = info(any, Port, [server_name]), {ok, [{bind_address, any}, {host, Host}, {port, Port} | Info]}; child_name2info({httpd_instance_sup, Address, Port}) -> Info = info(Address, Port, [server_name]), case inet:gethostbyaddr(Address) of {ok, {_, Host, _, _,_, _}} -> {ok, [{bind_address, Address}, {host, Host}, {port, Port} | Info]}; _ -> {ok, [{bind_address, Address}, {port, Port} | Info]} end. reload(Config, Address, Port) -> Name = make_name(Address,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:reload(Pid, Config); _ -> {error,not_started} end. reload(Addr, Port) when is_integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:reload(Pid, undefined); _ -> {error,not_started} end. %%% ========================================================= %%% Function: block/0, block/1, block/2, block/3, block/4 %%% block() %%% block(Port) %%% block(ConfigFile) %%% block(Addr,Port) %%% block(Port,Mode) %%% block(ConfigFile,Mode) %%% block(Addr,Port,Mode) %%% block(ConfigFile,Mode,Timeout) %%% block(Addr,Port,Mode,Timeout) %%% %%% Returns: ok | {error,Reason} %%% %%% Description: This function is used to block an HTTP server. %%% The blocking can be done in two ways, %%% disturbing or non-disturbing. Default is disturbing. %%% When a HTTP server is blocked, all requests are rejected %%% (status code 503). %%% %%% disturbing: %%% By performing a disturbing block, the server %%% is blocked forcefully and all ongoing requests %%% are terminated. No new connections are accepted. %%% If a timeout time is given then, on-going requests %%% are given this much time to complete before the %%% server is forcefully blocked. In this case no new %%% connections is accepted. %%% %%% non-disturbing: %%% A non-disturbing block is more gracefull. No %%% new connections are accepted, but the ongoing %%% requests are allowed to complete. %%% If a timeout time is given, it waits this long before %%% giving up (the block operation is aborted and the %%% server state is once more not-blocked). %%% %%% Types: Port -> integer() %%% Addr -> {A,B,C,D} | string() | undefined %%% ConfigFile -> string() %%% Mode -> disturbing | non_disturbing %%% Timeout -> integer() %%% block() -> block(undefined,8888,disturbing). block(Port) when is_integer(Port) -> block(undefined,Port,disturbing); block(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> block(Addr,Port,disturbing); Error -> Error end. block(Addr,Port) when is_integer(Port) -> block(Addr,Port,disturbing); block(Port,Mode) when is_integer(Port) andalso is_atom(Mode) -> block(undefined,Port,Mode); block(ConfigFile,Mode) when is_list(ConfigFile) andalso is_atom(Mode) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> block(Addr,Port,Mode); Error -> Error end. block(Addr,Port,disturbing) when is_integer(Port) -> do_block(Addr,Port,disturbing); block(Addr,Port,non_disturbing) when is_integer(Port) -> do_block(Addr,Port,non_disturbing); block(ConfigFile,Mode,Timeout) when is_list(ConfigFile) andalso is_atom(Mode) andalso is_integer(Timeout) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> block(Addr,Port,Mode,Timeout); Error -> Error end. block(Addr,Port,non_disturbing,Timeout) when is_integer(Port) andalso is_integer(Timeout) -> do_block(Addr,Port,non_disturbing,Timeout); block(Addr,Port,disturbing,Timeout) when is_integer(Port) andalso is_integer(Timeout) -> do_block(Addr,Port,disturbing,Timeout). do_block(Addr,Port,Mode) when is_integer(Port) andalso is_atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:block(Pid,Mode); _ -> {error,not_started} end. do_block(Addr,Port,Mode,Timeout) when is_integer(Port) andalso is_atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:block(Pid,Mode,Timeout); _ -> {error,not_started} end. %%% ========================================================= %%% Function: unblock/0, unblock/1, unblock/2 %%% unblock() %%% unblock(Port) %%% unblock(ConfigFile) %%% unblock(Addr,Port) %%% %%% Description: This function is used to reverse a previous block %%% operation on the HTTP server. %%% %%% Types: Port -> integer() %%% Addr -> {A,B,C,D} | string() | undefined %%% ConfigFile -> string() %%% unblock() -> unblock(undefined,8888). unblock(Port) when is_integer(Port) -> unblock(undefined,Port); unblock(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. unblock(Addr, Port) when is_integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:unblock(Pid); _ -> {error,not_started} end. foreach([]) -> []; foreach([KeyValue|Rest]) -> {ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "), case inets_regexp:split(Plus2Space,"=") of {ok,[Key|Value]} -> [{httpd_util:decode_hex(Key), httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)]; {ok,_} -> foreach(Rest) end. get_addr_and_port(ConfigFile) -> case httpd_conf:load(ConfigFile) of {ok, ConfigList} -> case (catch httpd_conf:validate_properties(ConfigList)) of {ok, Config} -> Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), {ok, Address, Port}; Error -> Error end; Error -> Error end. make_name(Addr, Port) -> httpd_util:make_name("httpd", Addr, Port). %%%-------------------------------------------------------------- %%% Internal debug functions - Do we want these functions here!? %%%-------------------------------------------------------------------- %%% ========================================================= %%% Function: get_admin_state/0, get_admin_state/1, get_admin_state/2 %%% get_admin_state() %%% get_admin_state(Port) %%% get_admin_state(Addr,Port) %%% %%% Returns: {ok,State} | {error,Reason} %%% %%% Description: This function is used to retrieve the administrative %%% state of the HTTP server. %%% %%% Types: Port -> integer() %%% Addr -> {A,B,C,D} | string() | undefined %%% State -> unblocked | shutting_down | blocked %%% Reason -> term() %%% get_admin_state() -> get_admin_state(undefined,8888). get_admin_state(Port) when is_integer(Port) -> get_admin_state(undefined,Port); get_admin_state(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. get_admin_state(Addr,Port) when is_integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:get_admin_state(Pid); _ -> {error,not_started} end. %%% ========================================================= %%% Function: get_usage_state/0, get_usage_state/1, get_usage_state/2 %%% get_usage_state() %%% get_usage_state(Port) %%% get_usage_state(Addr,Port) %%% %%% Returns: {ok,State} | {error,Reason} %%% %%% Description: This function is used to retrieve the usage %%% state of the HTTP server. %%% %%% Types: Port -> integer() %%% Addr -> {A,B,C,D} | string() | undefined %%% State -> idle | active | busy %%% Reason -> term() %%% get_usage_state() -> get_usage_state(undefined,8888). get_usage_state(Port) when is_integer(Port) -> get_usage_state(undefined,Port); get_usage_state(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. get_usage_state(Addr,Port) when is_integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:get_usage_state(Pid); _ -> {error,not_started} end. %%% ========================================================= %% Function: get_status(ConfigFile) -> Status %% get_status(Port) -> Status %% get_status(Addr,Port) -> Status %% get_status(Port,Timeout) -> Status %% get_status(Addr,Port,Timeout) -> Status %% %% Arguments: ConfigFile -> string() %% Configuration file from which Port and %% BindAddress will be extracted. %% Addr -> {A,B,C,D} | string() %% Bind Address of the http server %% Port -> integer() %% Port number of the http server %% Timeout -> integer() %% Timeout time for the call %% %% Returns: Status -> list() %% %% Description: This function is used when the caller runs in the %% same node as the http server or if calling with a %% program such as erl_call (see erl_interface). %% get_status(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> get_status(Addr,Port); Error -> Error end; get_status(Port) when is_integer(Port) -> get_status(undefined,Port,5000). get_status(Port,Timeout) when is_integer(Port) andalso is_integer(Timeout) -> get_status(undefined,Port,Timeout); get_status(Addr,Port) -> get_status(Addr,Port,5000). get_status(Addr,Port,Timeout) when is_integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> httpd_manager:get_status(Pid,Timeout); _ -> not_started end. do_reload_config(ConfigList, Mode) -> case (catch httpd_conf:validate_properties(ConfigList)) of {ok, Config} -> Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), block(Address, Port, Mode), reload(Config, Address, Port), unblock(Address, Port); Error -> Error end. %%%-------------------------------------------------------------- %%% Deprecated %%%-------------------------------------------------------------- start() -> start("/var/tmp/server_root/conf/8888.conf"). start(ConfigFile) -> {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone), unlink(Pid), {ok, Pid}. start_link() -> start("/var/tmp/server_root/conf/8888.conf"). start_link(ConfigFile) when is_list(ConfigFile) -> inets:start(httpd, ConfigFile, stand_alone). stop() -> stop(8888). stop(Port) when is_integer(Port) -> stop(undefined, Port); stop(Pid) when is_pid(Pid) -> old_stop(Pid); stop(ConfigFile) when is_list(ConfigFile) -> old_stop(ConfigFile). stop(Addr, Port) when is_integer(Port) -> old_stop(Addr, Port). start_child() -> start_child("/var/tmp/server_root/conf/8888.conf"). start_child(ConfigFile) -> httpd_sup:start_child(ConfigFile). stop_child() -> stop_child(8888). stop_child(Port) -> stop_child(undefined, Port). stop_child(Addr, Port) when is_integer(Port) -> httpd_sup:stop_child(Addr, Port). restart() -> reload(undefined, 8888). restart(Port) when is_integer(Port) -> reload(undefined, Port). restart(Addr, Port) -> reload(Addr, Port). old_stop(Pid) when is_pid(Pid) -> do_stop(Pid); old_stop(ConfigFile) when is_list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok, Addr, Port} -> old_stop(Addr, Port); Error -> Error end; old_stop(_StartArgs) -> ok. old_stop(Addr, Port) when is_integer(Port) -> Name = old_make_name(Addr, Port), case whereis(Name) of Pid when is_pid(Pid) -> do_stop(Pid), ok; _ -> not_started end. do_stop(Pid) -> exit(Pid, shutdown). old_make_name(Addr,Port) -> httpd_util:make_name("httpd_instance_sup",Addr,Port).