%% ``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 via the world wide web 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. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: httpd.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $ %% -module(httpd). -export([multi_start/1, multi_start_link/1, start/0, start/1, start/2, start_link/0, start_link/1, start_link/2, start_child/0,start_child/1, multi_stop/1, stop/0,stop/1,stop/2, stop_child/0,stop_child/1,stop_child/2, multi_restart/1, restart/0,restart/1,restart/2, parse_query/1]). %% Optional start related stuff... -export([load/1, load_mime_types/1, start2/1, start2/2, start_link2/1, start_link2/2, stop2/1]). %% Management stuff -export([block/0,block/1,block/2,block/3,block/4, unblock/0,unblock/1,unblock/2]). %% Debugging and status info stuff... -export([verbosity/3,verbosity/4]). -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]). -include("httpd.hrl"). -define(D(F, A), io:format("~p:" ++ F ++ "~n", [?MODULE|A])). %% start start() -> start("/var/tmp/server_root/conf/8888.conf"). start(ConfigFile) -> %% ?D("start(~s) -> entry", [ConfigFile]), start(ConfigFile, []). start(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) -> httpd_sup:start(ConfigFile, Verbosity). %% start_link start_link() -> start("/var/tmp/server_root/conf/8888.conf"). start_link(ConfigFile) -> start_link(ConfigFile, []). start_link(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) -> httpd_sup:start_link(ConfigFile, Verbosity). %% start2 & start_link2 start2(Config) -> start2(Config, []). start2(Config, Verbosity) when list(Config), list(Verbosity) -> httpd_sup:start2(Config, Verbosity). start_link2(Config) -> start_link2(Config, []). start_link2(Config, Verbosity) when list(Config), list(Verbosity) -> httpd_sup:start_link2(Config, Verbosity). %% stop stop() -> stop(8888). stop(Port) when integer(Port) -> stop(undefined, Port); stop(Pid) when pid(Pid) -> httpd_sup:stop(Pid); stop(ConfigFile) when list(ConfigFile) -> %% ?D("stop(~s) -> entry", [ConfigFile]), httpd_sup:stop(ConfigFile). stop(Addr, Port) when integer(Port) -> httpd_sup:stop(Addr, Port). stop2(Config) when list(Config) -> httpd_sup:stop2(Config). %% start_child start_child() -> start_child("/var/tmp/server_root/conf/8888.conf"). start_child(ConfigFile) -> start_child(ConfigFile, []). start_child(ConfigFile, Verbosity) -> inets_sup:start_child(ConfigFile, Verbosity). %% stop_child stop_child() -> stop_child(8888). stop_child(Port) -> stop_child(undefined,Port). stop_child(Addr, Port) when integer(Port) -> inets_sup:stop_child(Addr, Port). %% multi_start multi_start(MultiConfigFile) -> case read_multi_file(MultiConfigFile) of {ok,ConfigFiles} -> mstart(ConfigFiles); Error -> Error end. mstart(ConfigFiles) -> mstart(ConfigFiles,[]). mstart([],Results) -> {ok,lists:reverse(Results)}; mstart([H|T],Results) -> Res = start(H), mstart(T,[Res|Results]). %% multi_start_link multi_start_link(MultiConfigFile) -> case read_multi_file(MultiConfigFile) of {ok,ConfigFiles} -> mstart_link(ConfigFiles); Error -> Error end. mstart_link(ConfigFiles) -> mstart_link(ConfigFiles,[]). mstart_link([],Results) -> {ok,lists:reverse(Results)}; mstart_link([H|T],Results) -> Res = start_link(H), mstart_link(T,[Res|Results]). %% multi_stop multi_stop(MultiConfigFile) -> case read_multi_file(MultiConfigFile) of {ok,ConfigFiles} -> mstop(ConfigFiles); Error -> Error end. mstop(ConfigFiles) -> mstop(ConfigFiles,[]). mstop([],Results) -> {ok,lists:reverse(Results)}; mstop([H|T],Results) -> Res = stop(H), mstop(T,[Res|Results]). %% multi_restart multi_restart(MultiConfigFile) -> case read_multi_file(MultiConfigFile) of {ok,ConfigFiles} -> mrestart(ConfigFiles); Error -> Error end. mrestart(ConfigFiles) -> mrestart(ConfigFiles,[]). mrestart([],Results) -> {ok,lists:reverse(Results)}; mrestart([H|T],Results) -> Res = restart(H), mrestart(T,[Res|Results]). %% restart restart() -> restart(undefined,8888). restart(Port) when integer(Port) -> restart(undefined,Port); restart(ConfigFile) when list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> restart(Addr,Port); Error -> Error end. restart(Addr,Port) when integer(Port) -> do_restart(Addr,Port). do_restart(Addr,Port) when integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when pid(Pid) -> httpd_manager:restart(Pid); _ -> {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 integer(Port) -> block(undefined,Port,disturbing); block(ConfigFile) when list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> block(Addr,Port,disturbing); Error -> Error end. block(Addr,Port) when integer(Port) -> block(Addr,Port,disturbing); block(Port,Mode) when integer(Port), atom(Mode) -> block(undefined,Port,Mode); block(ConfigFile,Mode) when list(ConfigFile), atom(Mode) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> block(Addr,Port,Mode); Error -> Error end. block(Addr,Port,disturbing) when integer(Port) -> do_block(Addr,Port,disturbing); block(Addr,Port,non_disturbing) when integer(Port) -> do_block(Addr,Port,non_disturbing); block(ConfigFile,Mode,Timeout) when list(ConfigFile), atom(Mode), 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 integer(Port), integer(Timeout) -> do_block(Addr,Port,non_disturbing,Timeout); block(Addr,Port,disturbing,Timeout) when integer(Port), integer(Timeout) -> do_block(Addr,Port,disturbing,Timeout). do_block(Addr,Port,Mode) when integer(Port), atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when pid(Pid) -> httpd_manager:block(Pid,Mode); _ -> {error,not_started} end. do_block(Addr,Port,Mode,Timeout) when integer(Port), atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when 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 integer(Port) -> unblock(undefined,Port); unblock(ConfigFile) when list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. unblock(Addr,Port) when integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when pid(Pid) -> httpd_manager:unblock(Pid); _ -> {error,not_started} end. verbosity(Port,Who,Verbosity) -> verbosity(undefined,Port,Who,Verbosity). verbosity(Addr,Port,Who,Verbosity) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when pid(Pid) -> httpd_manager:verbosity(Pid,Who,Verbosity); _ -> not_started end. %%% ========================================================= %%% 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 integer(Port) -> get_admin_state(undefined,Port); get_admin_state(ConfigFile) when list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. get_admin_state(Addr,Port) when integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when 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 integer(Port) -> get_usage_state(undefined,Port); get_usage_state(ConfigFile) when list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> unblock(Addr,Port); Error -> Error end. get_usage_state(Addr,Port) when integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when 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 list(ConfigFile) -> case get_addr_and_port(ConfigFile) of {ok,Addr,Port} -> get_status(Addr,Port); Error -> Error end; get_status(Port) when integer(Port) -> get_status(undefined,Port,5000). get_status(Port,Timeout) when integer(Port), integer(Timeout) -> get_status(undefined,Port,Timeout); get_status(Addr,Port) when list(Addr), integer(Port) -> get_status(Addr,Port,5000). get_status(Addr,Port,Timeout) when integer(Port) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when pid(Pid) -> httpd_manager:get_status(Pid,Timeout); _ -> not_started end. %% load config load(ConfigFile) -> httpd_conf:load(ConfigFile). load_mime_types(MimeTypesFile) -> httpd_conf:load_mime_types(MimeTypesFile). %% parse_query parse_query(String) -> {ok, SplitString} = regexp:split(String,"[&;]"), foreach(SplitString). foreach([]) -> []; foreach([KeyValue|Rest]) -> {ok, Plus2Space, _} = regexp:gsub(KeyValue,"[\+]"," "), case 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 get_addr_and_port(ConfigFile) -> case httpd_conf:load(ConfigFile) of {ok,ConfigList} -> Port = httpd_util:key1search(ConfigList,port,80), Addr = httpd_util:key1search(ConfigList,bind_address), {ok,Addr,Port}; Error -> Error end. %% make_name make_name(Addr,Port) -> httpd_util:make_name("httpd",Addr,Port). %% Multi stuff %% read_multi_file(File) -> read_mfile(file:open(File,[read])). read_mfile({ok,Fd}) -> read_mfile(read_line(Fd),Fd,[]); read_mfile(Error) -> Error. read_mfile(eof,_Fd,SoFar) -> {ok,lists:reverse(SoFar)}; read_mfile({error,Reason},_Fd,SoFar) -> {error,Reason}; read_mfile([$#|Comment],Fd,SoFar) -> read_mfile(read_line(Fd),Fd,SoFar); read_mfile([],Fd,SoFar) -> read_mfile(read_line(Fd),Fd,SoFar); read_mfile(Line,Fd,SoFar) -> read_mfile(read_line(Fd),Fd,[Line|SoFar]). read_line(Fd) -> read_line1(io:get_line(Fd,[])). read_line1(eof) -> eof; read_line1(String) -> httpd_conf:clean(String).