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(file, Config) of 139 undefined -> 140 case proplists:get_value(proplist_file, Config) of 141 undefined -> 142 httpd_conf:validate_properties(Config); 143 File -> 144 try file:consult(File) of 145 {ok, [PropList]} -> 146 httpd_conf:validate_properties(PropList) 147 catch 148 exit:_ -> 149 throw({error, 150 {could_not_consult_proplist_file, File}}) 151 end 152 end; 153 File -> 154 {ok, File} 155 end. 156 157httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug) 158 when is_tuple(Value) -> 159 Address = proplists:get_value(bind_address, Config, any), 160 Port = proplists:get_value(port, Config, 80), 161 Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE), 162 httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile); 163 164%% In this case the AcceptTimeout and Debug will only have default values... 165httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) -> 166 case httpd_conf:load(ConfigFile) of 167 {ok, ConfigList} -> 168 case (catch httpd_conf:validate_properties(ConfigList)) of 169 {ok, Config} -> 170 Address = proplists:get_value(bind_address, Config, any), 171 Port = proplists:get_value(port, Config, 80), 172 Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE), 173 AcceptTimeout = 174 proplists:get_value(accept_timeout, Config, 175 AcceptTimeoutDef), 176 Debug = 177 proplists:get_value(debug, Config, DebugDef), 178 httpd_child_spec([{file, ConfigFile} | Config], 179 AcceptTimeout, Debug, Address, Port, Profile); 180 Error -> 181 Error 182 end; 183 Error -> 184 Error 185 end. 186 187httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 188 case get_fd(Port) of 189 {ok, Fd} -> 190 case Port == 0 orelse Fd =/= undefined of 191 true -> 192 httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile); 193 false -> 194 httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) 195 end; 196 Error -> 197 Error 198 end. 199 200httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 201 case start_listen(Addr, Port, Config) of 202 {Pid, {NewPort, NewConfig, ListenSocket}} -> 203 Name = {httpd_instance_sup, Addr, NewPort, Profile}, 204 StartFunc = {httpd_instance_sup, start_link, 205 [NewConfig, AcceptTimeout, 206 {Pid, ListenSocket}, Debug]}, 207 Restart = permanent, 208 Shutdown = infinity, 209 Modules = [httpd_instance_sup], 210 Type = supervisor, 211 {Name, StartFunc, Restart, Shutdown, Type, Modules}; 212 {Pid, {error, Reason}} -> 213 exit(Pid, normal), 214 {error, Reason} 215 end. 216 217httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) -> 218 Name = {httpd_instance_sup, Addr, Port, Profile}, 219 StartFunc = {httpd_instance_sup, start_link, 220 [Config, AcceptTimeout, Debug]}, 221 Restart = permanent, 222 Shutdown = infinity, 223 Modules = [httpd_instance_sup], 224 Type = supervisor, 225 {Name, StartFunc, Restart, Shutdown, Type, Modules}. 226 227 228mk_tuple_list([]) -> 229 []; 230mk_tuple_list([H={_,_}|T]) -> 231 [H|mk_tuple_list(T)]; 232mk_tuple_list(F) -> 233 [{file, F}]. 234 235error_msg(F, A) -> 236 error_logger:error_msg(F ++ "~n", A). 237 238listen(Address, Port, Config) -> 239 try socket_type(Config) of 240 SocketType -> 241 case http_transport:start(SocketType) of 242 ok -> 243 {ok, Fd} = get_fd(Port), 244 IpFamily = proplists:get_value(ipfamily, Config, inet), 245 case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of 246 {ok, ListenSocket} -> 247 NewConfig = proplists:delete(port, Config), 248 {NewPort, _} = http_transport:sockname(SocketType, ListenSocket), 249 {NewPort, [{port, NewPort} | NewConfig], ListenSocket}; 250 {error, Reason} -> 251 {error, {listen, Reason}} 252 end; 253 {error, Reason} -> 254 {error, {socket_start_failed, Reason}} 255 end 256 catch 257 _:Reason -> 258 {error, {socket_start_failed, Reason}} 259 end. 260 261start_listen(Address, Port, Config) -> 262 Pid = listen_owner(Address, Port, Config), 263 receive 264 {Pid, Result} -> 265 {Pid, Result} 266 end. 267 268listen_owner(Address, Port, Config) -> 269 spawn(?MODULE, listen_init, [self(), Address, Port, Config]). 270 271listen_init(From, Address, Port, Config) -> 272 process_flag(trap_exit, true), 273 Result = listen(Address, Port, Config), 274 From ! {self(), Result}, 275 listen_loop(). 276 277listen_loop() -> 278 receive 279 {'EXIT', _, _} -> 280 ok 281 end. 282 283socket_type(Config) -> 284 SocketType = proplists:get_value(socket_type, Config, ip_comm), 285 socket_type(SocketType, Config). 286 287socket_type(ip_comm = SocketType, _) -> 288 SocketType; 289socket_type({ip_comm, _} = SocketType, _) -> 290 SocketType; 291socket_type({essl, _} = SocketType, _) -> 292 SocketType; 293socket_type(_, Config) -> 294 {essl, ssl_config(Config)}. 295 296%%% Backwards compatibility 297ssl_config(Config) -> 298 ssl_certificate_key_file(Config) ++ 299 ssl_verify_client(Config) ++ 300 ssl_ciphers(Config) ++ 301 ssl_password(Config) ++ 302 ssl_verify_depth(Config) ++ 303 ssl_ca_certificate_file(Config). 304 305ssl_certificate_key_file(Config) -> 306 case proplists:get_value(ssl_certificate_key_file, Config) of 307 undefined -> 308 []; 309 SSLCertificateKeyFile -> 310 [{keyfile,SSLCertificateKeyFile}] 311 end. 312 313ssl_verify_client(Config) -> 314 case proplists:get_value(ssl_verify_client, Config) of 315 undefined -> 316 []; 317 SSLVerifyClient -> 318 [{verify,SSLVerifyClient}] 319 end. 320 321ssl_ciphers(Config) -> 322 case proplists:get_value(ssl_ciphers, Config) of 323 undefined -> 324 []; 325 Ciphers -> 326 [{ciphers, Ciphers}] 327 end. 328 329ssl_password(Config) -> 330 case proplists:get_value(ssl_password_callback_module, Config) of 331 undefined -> 332 []; 333 Module -> 334 case proplists:get_value(ssl_password_callback_function, Config) of 335 undefined -> 336 []; 337 Function -> 338 Args = case proplists:get_value(ssl_password_callback_arguments, Config) of 339 undefined -> 340 []; 341 Arguments -> 342 [Arguments] 343 end, 344 Password = apply(Module, Function, Args), 345 [{password, Password}] 346 end 347 end. 348 349ssl_verify_depth(Config) -> 350 case proplists:get_value(ssl_verify_client_depth, Config) of 351 undefined -> 352 []; 353 Depth -> 354 [{depth, Depth}] 355 end. 356 357ssl_ca_certificate_file(Config) -> 358 case proplists:get_value(ssl_ca_certificate_file, Config) of 359 undefined -> 360 []; 361 File -> 362 [{cacertfile, File}] 363 end. 364 365get_fd(0) -> 366 {ok, undefined}; 367get_fd(Port) -> 368 FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), 369 case init:get_argument(FdKey) of 370 {ok, [[Value]]} -> 371 case (catch list_to_integer(Value)) of 372 N when is_integer(N) -> 373 {ok, N}; 374 _ -> 375 {error, {bad_descriptor, Value}} 376 end; 377 _ -> 378 {ok, undefined} 379 end. 380