From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/inets/src/http_server/httpd.erl | 600 ++++++++++++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 lib/inets/src/http_server/httpd.erl (limited to 'lib/inets/src/http_server/httpd.erl') diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl new file mode 100644 index 0000000000..554f162fc5 --- /dev/null +++ b/lib/inets/src/http_server/httpd.erl @@ -0,0 +1,600 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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). + +-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 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 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). -- cgit v1.2.3