1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2004-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% 21%%---------------------------------------------------------------------- 22%% Purpose: The top supervisor for the http server (httpd) hangs under 23%% inets_sup. 24%%---------------------------------------------------------------------- 25 26-module(httpd_sup). 27 28-behaviour(supervisor). 29 30%% Internal application API 31-export([start_link/1, start_link/2]). 32-export([start_child/1, restart_child/3, stop_child/3]). 33 34%% Supervisor callback 35-export([init/1]). 36 37-export([listen_init/4]). 38 39-define(TIMEOUT, 15000). 40-include("httpd_internal.hrl"). 41 42%%%========================================================================= 43%%% API 44%%%========================================================================= 45start_link(HttpdServices) -> 46 supervisor:start_link({local, ?MODULE}, ?MODULE, [HttpdServices]). 47 48start_link(HttpdServices, stand_alone) -> 49 supervisor:start_link(?MODULE, [HttpdServices]). 50 51start_child(Config) -> 52 try httpd_config(Config) of 53 {ok, NewConfig} -> 54 Spec = httpd_child_spec(NewConfig, ?TIMEOUT, []), 55 case supervisor:start_child(?MODULE, Spec) of 56 {error, {invalid_child_spec, Error}} -> 57 Error; 58 Other -> 59 Other 60 end 61 catch 62 throw:Error -> 63 Error 64 end. 65 66 67restart_child(Address, Port, Profile) -> 68 Name = id(Address, Port, Profile), 69 case supervisor:terminate_child(?MODULE, Name) of 70 ok -> 71 supervisor:restart_child(?MODULE, Name); 72 Error -> 73 Error 74 end. 75 76stop_child(Address, Port, Profile) -> 77 Name = id(Address, Port, Profile), 78 case supervisor:terminate_child(?MODULE, Name) of 79 ok -> 80 supervisor:delete_child(?MODULE, Name); 81 Error -> 82 Error 83 end. 84 85id(Address, Port, Profile) -> 86 {httpd_instance_sup, Address, Port, Profile}. 87 88 89%%%========================================================================= 90%%% Supervisor callback 91%%%========================================================================= 92init([HttpdServices]) -> 93 RestartStrategy = one_for_one, 94 MaxR = 10, 95 MaxT = 3600, 96 Children = child_specs(HttpdServices, []), 97 {ok, {{RestartStrategy, MaxR, MaxT}, Children}}. 98 99 100%%%========================================================================= 101%%% Internal functions 102%%%========================================================================= 103 104%% The format of the httpd service is: 105%% httpd_service() -> {httpd,httpd()} 106%% httpd() -> [httpd_config()] | file() 107%% httpd_config() -> {file,file()} | 108%% {debug,debug()} | 109%% {accept_timeout,integer()} 110%% debug() -> disable | [debug_options()] 111%% debug_options() -> {all_functions,modules()} | 112%% {exported_functions,modules()} | 113%% {disable,modules()} 114%% modules() -> [atom()] 115 116 117child_specs([], Acc) -> 118 Acc; 119child_specs([{httpd, HttpdService} | Rest], Acc) -> 120 NewHttpdService = (catch mk_tuple_list(HttpdService)), 121 case catch child_spec(NewHttpdService) of 122 {error, Reason} -> 123 error_msg("Failed to start service: ~n~p ~n due to: ~p~n", 124 [HttpdService, Reason]), 125 child_specs(Rest, Acc); 126 Spec -> 127 child_specs(Rest, [Spec | Acc]) 128 end. 129 130child_spec(HttpdService) -> 131 {ok, Config} = httpd_config(HttpdService), 132 Debug = proplists:get_value(debug, Config, []), 133 AcceptTimeout = proplists:get_value(accept_timeout, Config, 15000), 134 httpd_util:valid_options(Debug, AcceptTimeout, Config), 135 httpd_child_spec(Config, AcceptTimeout, Debug). 136 137httpd_config([Value| _] = Config) when is_tuple(Value) -> 138 case proplists:get_value(proplist_file, Config) of 139 undefined -> 140 httpd_conf:validate_properties(Config); 141 File -> 142 try file:consult(File) of 143 {ok, [PropList]} -> 144 httpd_conf:validate_properties(PropList) 145 catch 146 exit:_ -> 147 throw({error, 148 {could_not_consult_proplist_file, File}}) 149 end 150 end. 151 152httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug) 153 when is_tuple(Value) -> 154 Address = proplists:get_value(bind_address, Config, any), 155 Port = proplists:get_value(port, Config, 80), 156 Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE), 157 httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile). 158 159 160httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 161 case get_fd(Port) of 162 {ok, Fd} -> 163 case Port == 0 orelse Fd =/= undefined of 164 true -> 165 httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile); 166 false -> 167 httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) 168 end; 169 Error -> 170 Error 171 end. 172 173httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 174 case start_listen(Addr, Port, Config) of 175 {Pid, {NewPort, NewConfig, ListenSocket}} -> 176 Name = {httpd_instance_sup, Addr, NewPort, Profile}, 177 StartFunc = {httpd_instance_sup, start_link, 178 [NewConfig, AcceptTimeout, 179 {Pid, ListenSocket}, Debug]}, 180 Restart = permanent, 181 Shutdown = infinity, 182 Modules = [httpd_instance_sup], 183 Type = supervisor, 184 {Name, StartFunc, Restart, Shutdown, Type, Modules}; 185 {Pid, {error, Reason}} -> 186 exit(Pid, normal), 187 {error, Reason} 188 end. 189 190httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 191 Name = {httpd_instance_sup, Addr, Port, Profile}, 192 StartFunc = {httpd_instance_sup, start_link, 193 [Config, AcceptTimeout, Debug]}, 194 Restart = permanent, 195 Shutdown = infinity, 196 Modules = [httpd_instance_sup], 197 Type = supervisor, 198 {Name, StartFunc, Restart, Shutdown, Type, Modules}. 199 200 201mk_tuple_list([]) -> 202 []; 203mk_tuple_list([H={_,_}|T]) -> 204 [H|mk_tuple_list(T)]; 205mk_tuple_list(F) -> 206 [{file, F}]. 207 208error_msg(F, A) -> 209 error_logger:error_msg(F ++ "~n", A). 210 211listen(Address, Port, Config) -> 212 try socket_type(Config) of 213 SocketType -> 214 case http_transport:start(SocketType) of 215 ok -> 216 {ok, Fd} = get_fd(Port), 217 IpFamily = proplists:get_value(ipfamily, Config, inet), 218 case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of 219 {ok, ListenSocket} -> 220 NewConfig = proplists:delete(port, Config), 221 {NewPort, _} = http_transport:sockname(SocketType, ListenSocket), 222 {NewPort, [{port, NewPort} | NewConfig], ListenSocket}; 223 {error, Reason} -> 224 {error, {listen, Reason}} 225 end; 226 {error, Reason} -> 227 {error, {socket_start_failed, Reason}} 228 end 229 catch 230 _:Reason -> 231 {error, {socket_start_failed, Reason}} 232 end. 233 234start_listen(Address, Port, Config) -> 235 Pid = listen_owner(Address, Port, Config), 236 receive 237 {Pid, Result} -> 238 {Pid, Result} 239 end. 240 241listen_owner(Address, Port, Config) -> 242 spawn(?MODULE, listen_init, [self(), Address, Port, Config]). 243 244listen_init(From, Address, Port, Config) -> 245 process_flag(trap_exit, true), 246 Result = listen(Address, Port, Config), 247 From ! {self(), Result}, 248 listen_loop(). 249 250listen_loop() -> 251 receive 252 {'EXIT', _, _} -> 253 ok 254 end. 255 256socket_type(Config) -> 257 SocketType = proplists:get_value(socket_type, Config, ip_comm), 258 socket_type(SocketType, Config). 259 260socket_type(ip_comm = SocketType, _) -> 261 SocketType; 262socket_type({ip_comm, _} = SocketType, _) -> 263 SocketType; 264socket_type({essl, _} = SocketType, _) -> 265 SocketType; 266socket_type(_, Config) -> 267 {essl, ssl_config(Config)}. 268 269%%% Backwards compatibility 270ssl_config(Config) -> 271 ssl_certificate_key_file(Config) ++ 272 ssl_verify_client(Config) ++ 273 ssl_ciphers(Config) ++ 274 ssl_password(Config) ++ 275 ssl_verify_depth(Config) ++ 276 ssl_ca_certificate_file(Config). 277 278ssl_certificate_key_file(Config) -> 279 case proplists:get_value(ssl_certificate_key_file, Config) of 280 undefined -> 281 []; 282 SSLCertificateKeyFile -> 283 [{keyfile,SSLCertificateKeyFile}] 284 end. 285 286ssl_verify_client(Config) -> 287 case proplists:get_value(ssl_verify_client, Config) of 288 undefined -> 289 []; 290 SSLVerifyClient -> 291 [{verify,SSLVerifyClient}] 292 end. 293 294ssl_ciphers(Config) -> 295 case proplists:get_value(ssl_ciphers, Config) of 296 undefined -> 297 []; 298 Ciphers -> 299 [{ciphers, Ciphers}] 300 end. 301 302ssl_password(Config) -> 303 case proplists:get_value(ssl_password_callback_module, Config) of 304 undefined -> 305 []; 306 Module -> 307 case proplists:get_value(ssl_password_callback_function, Config) of 308 undefined -> 309 []; 310 Function -> 311 Args = case proplists:get_value(ssl_password_callback_arguments, Config) of 312 undefined -> 313 []; 314 Arguments -> 315 [Arguments] 316 end, 317 Password = apply(Module, Function, Args), 318 [{password, Password}] 319 end 320 end. 321 322ssl_verify_depth(Config) -> 323 case proplists:get_value(ssl_verify_client_depth, Config) of 324 undefined -> 325 []; 326 Depth -> 327 [{depth, Depth}] 328 end. 329 330ssl_ca_certificate_file(Config) -> 331 case proplists:get_value(ssl_ca_certificate_file, Config) of 332 undefined -> 333 []; 334 File -> 335 [{cacertfile, File}] 336 end. 337 338get_fd(0) -> 339 {ok, undefined}; 340get_fd(Port) -> 341 FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), 342 case init:get_argument(FdKey) of 343 {ok, [[Value]]} -> 344 case (catch list_to_integer(Value)) of 345 N when is_integer(N) -> 346 {ok, N}; 347 _ -> 348 {error, {bad_descriptor, Value}} 349 end; 350 _ -> 351 {ok, undefined} 352 end. 353