aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src/http_server/httpd_acceptor.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/inets/src/http_server/httpd_acceptor.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/inets/src/http_server/httpd_acceptor.erl')
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl211
1 files changed, 211 insertions, 0 deletions
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
new file mode 100644
index 0000000000..568fd3c610
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -0,0 +1,211 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-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_acceptor).
+
+-include("httpd.hrl").
+-include("httpd_internal.hrl").
+
+%% Internal application API
+-export([start_link/5, start_link/6]).
+
+%% Other exports (for spawn's etc.)
+-export([acceptor_init/6, acceptor_init/7, acceptor_loop/5]).
+
+%%
+%% External API
+%%
+
+%% start_link
+
+start_link(Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout) ->
+ ?hdrd("start link",
+ [{manager, Manager},
+ {socket_type, SocketType},
+ {address, Addr},
+ {port, Port},
+ {timeout, AcceptTimeout}]),
+ Args = [self(), Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout],
+ proc_lib:start_link(?MODULE, acceptor_init, Args).
+
+start_link(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
+ ?hdrd("start link",
+ [{manager, Manager},
+ {socket_type, SocketType},
+ {listen_socket, ListenSocket},
+ {timeout, AcceptTimeout}]),
+ Args = [self(), Manager, SocketType, ListenSocket,
+ ConfigDb, AcceptTimeout],
+ proc_lib:start_link(?MODULE, acceptor_init, Args).
+
+acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket},
+ ConfigDb, AcceptTimeout) ->
+ ?hdrd("acceptor init",
+ [{parent, Parent},
+ {manager, Manager},
+ {socket_type, SocketType},
+ {listen_owner, ListenOwner},
+ {listen_socket, ListenSocket},
+ {timeout, AcceptTimeout}]),
+ link(ListenOwner),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout).
+
+acceptor_init(Parent, Manager, SocketType, Addr, Port,
+ ConfigDb, AcceptTimeout) ->
+ ?hdrd("acceptor init",
+ [{parent, Parent},
+ {manager, Manager},
+ {socket_type, SocketType},
+ {address, Addr},
+ {port, Port},
+ {timeout, AcceptTimeout}]),
+ case (catch do_init(SocketType, Addr, Port)) of
+ {ok, ListenSocket} ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor_loop(Manager, SocketType,
+ ListenSocket, ConfigDb, AcceptTimeout);
+ Error ->
+ proc_lib:init_ack(Parent, Error),
+ error
+ end.
+
+do_init(SocketType, Addr, Port) ->
+ ?hdrt("do init", []),
+ do_socket_start(SocketType),
+ ListenSocket = do_socket_listen(SocketType, Addr, Port),
+ {ok, ListenSocket}.
+
+
+do_socket_start(SocketType) ->
+ ?hdrt("do socket start", []),
+ case http_transport:start(SocketType) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ ?hdrv("failed starting transport", [{reason, Reason}]),
+ throw({error, {socket_start_failed, Reason}})
+ end.
+
+
+do_socket_listen(SocketType, Addr, Port) ->
+ ?hdrt("do socket listen", []),
+ case http_transport:listen(SocketType, Addr, Port) of
+ {ok, ListenSocket} ->
+ ListenSocket;
+ {error, Reason} ->
+ ?hdrv("listen failed", [{reason, Reason},
+ {socket_type, SocketType},
+ {addr, Addr},
+ {port, Port}]),
+ throw({error, {listen, Reason}})
+ end.
+
+
+%% acceptor
+
+acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
+ ?hdrd("awaiting accept",
+ [{manager, Manager},
+ {socket_type, SocketType},
+ {listen_socket, ListenSocket},
+ {timeout, AcceptTimeout}]),
+ case (catch http_transport:accept(SocketType, ListenSocket, 50000)) of
+ {ok, Socket} ->
+ ?hdrv("accepted", [{socket, Socket}]),
+ handle_connection(Manager, ConfigDb, AcceptTimeout,
+ SocketType, Socket),
+ ?MODULE:acceptor_loop(Manager, SocketType,
+ ListenSocket, ConfigDb,AcceptTimeout);
+ {error, Reason} ->
+ ?hdri("accept failed", [{reason, Reason}]),
+ handle_error(Reason, ConfigDb),
+ ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
+ ConfigDb, AcceptTimeout);
+ {'EXIT', Reason} ->
+ ?hdri("accept exited", [{reason, Reason}]),
+ handle_error({'EXIT', Reason}, ConfigDb),
+ ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
+ ConfigDb, AcceptTimeout)
+ end.
+
+
+handle_connection(Manager, ConfigDb, AcceptTimeout, SocketType, Socket) ->
+ {ok, Pid} = httpd_request_handler:start(Manager, ConfigDb, AcceptTimeout),
+ http_transport:controlling_process(SocketType, Socket, Pid),
+ httpd_request_handler:socket_ownership_transfered(Pid, SocketType, Socket).
+
+handle_error(timeout, _) ->
+ ok;
+
+handle_error({enfile, _}, _) ->
+ %% Out of sockets...
+ sleep(200);
+
+handle_error(emfile, _) ->
+ %% Too many open files -> Out of sockets...
+ sleep(200);
+
+handle_error(closed, _) ->
+ error_logger:info_report("The httpd accept socket was closed by "
+ "a third party. "
+ "This will not have an impact on inets "
+ "that will open a new accept socket and "
+ "go on as nothing happened. It does however "
+ "indicate that some other software is behaving "
+ "badly."),
+ exit(normal);
+
+%% This will only happen when the client is terminated abnormaly
+%% and is not a problem for the server, so we want
+%% to terminate normal so that we can restart without any
+%% error messages.
+handle_error(econnreset,_) ->
+ exit(normal);
+
+handle_error(econnaborted, _) ->
+ ok;
+
+handle_error(esslaccept, _) ->
+ %% The user has selected to cancel the installation of
+ %% the certifikate, This is not a real error, so we do
+ %% not write an error message.
+ ok;
+
+handle_error({'EXIT', Reason}, ConfigDb) ->
+ String = lists:flatten(io_lib:format("Accept exit: ~p", [Reason])),
+ accept_failed(ConfigDb, String);
+
+handle_error(Reason, ConfigDb) ->
+ String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
+ accept_failed(ConfigDb, String).
+
+-spec accept_failed(_, string()) -> no_return().
+
+accept_failed(ConfigDb, String) ->
+ error_logger:error_report(String),
+ InitData = #init_data{peername = {0, "unknown"}},
+ Info = #mod{config_db = ConfigDb, init_data = InitData},
+ mod_log:error_log(Info, String),
+ mod_disk_log:error_log(Info, String),
+ exit({accept_failed, String}).
+
+sleep(T) -> receive after T -> ok end.
+
+