1%% ``Licensed under the Apache License, Version 2.0 (the "License");
2%% you may not use this file except in compliance with the License.
3%% You may obtain a copy of the License at
4%%
5%%     http://www.apache.org/licenses/LICENSE-2.0
6%%
7%% Unless required by applicable law or agreed to in writing, software
8%% distributed under the License is distributed on an "AS IS" BASIS,
9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10%% See the License for the specific language governing permissions and
11%% limitations under the License.
12%%
13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15%% AB. All Rights Reserved.''
16%%
17%%     $Id: httpd_acceptor.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
18-module(httpd_acceptor).
19
20-include("httpd.hrl").
21-include("httpd_verbosity.hrl").
22
23
24%% External API
25-export([start_link/6]).
26
27%% Other exports (for spawn's etc.)
28-export([acceptor/4, acceptor/7]).
29
30
31%%
32%% External API
33%%
34
35%% start_link
36
37start_link(Manager, SocketType, Addr, Port, ConfigDb, Verbosity) ->
38    Args = [self(), Manager, SocketType, Addr, Port, ConfigDb, Verbosity],
39    proc_lib:start_link(?MODULE, acceptor, Args).
40
41
42acceptor(Parent, Manager, SocketType, Addr, Port, ConfigDb, Verbosity) ->
43    put(sname,acc),
44    put(verbosity,Verbosity),
45    ?vlog("starting",[]),
46    case (catch do_init(SocketType, Addr, Port)) of
47	{ok, ListenSocket} ->
48	    proc_lib:init_ack(Parent, {ok, self()}),
49	    acceptor(Manager, SocketType, ListenSocket, ConfigDb);
50	Error ->
51	    proc_lib:init_ack(Parent, Error),
52	    error
53    end.
54
55do_init(SocketType, Addr, Port) ->
56    do_socket_start(SocketType),
57    ListenSocket = do_socket_listen(SocketType, Addr, Port),
58    {ok, ListenSocket}.
59
60
61do_socket_start(SocketType) ->
62    case httpd_socket:start(SocketType) of
63	ok ->
64	    ok;
65	{error, Reason} ->
66	    ?vinfo("failed socket start: ~p",[Reason]),
67	    throw({error, {socket_start_failed, Reason}})
68    end.
69
70
71do_socket_listen(SocketType, Addr, Port) ->
72    case httpd_socket:listen(SocketType, Addr, Port) of
73	{error, Reason} ->
74	    ?vinfo("failed socket listen operation: ~p", [Reason]),
75	    throw({error, {listen, Reason}});
76	ListenSocket ->
77	    ListenSocket
78    end.
79
80
81%% acceptor
82
83acceptor(Manager, SocketType, ListenSocket, ConfigDb) ->
84    ?vdebug("await connection",[]),
85    case (catch httpd_socket:accept(SocketType, ListenSocket, 30000)) of
86	{error, Reason} ->
87	    handle_error(Reason, ConfigDb, SocketType),
88	    ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb);
89
90	{'EXIT', Reason} ->
91	    handle_error({'EXIT', Reason}, ConfigDb, SocketType),
92	    ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb);
93
94	Socket ->
95	    handle_connection(Manager, ConfigDb, SocketType, Socket),
96	    ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb)
97    end.
98
99
100handle_connection(Manager, ConfigDb, SocketType, Socket) ->
101    case httpd_request_handler:start_link(Manager, ConfigDb) of
102	{ok, Pid} ->
103	    httpd_socket:controlling_process(SocketType, Socket, Pid),
104	    httpd_request_handler:synchronize(Pid, SocketType, Socket);
105	{error, Reason} ->
106	    handle_connection_err(SocketType, Socket, ConfigDb, Reason)
107    end.
108
109
110handle_connection_err(SocketType, Socket, ConfigDb, Reason) ->
111    String =
112	lists:flatten(
113	  io_lib:format("failed starting request handler:~n   ~p", [Reason])),
114    report_error(ConfigDb, String),
115    httpd_socket:close(SocketType, Socket).
116
117
118handle_error(timeout, _, _) ->
119    ?vtrace("Accept timeout",[]),
120    ok;
121
122handle_error({enfile, _}, _, _) ->
123    ?vinfo("Accept error: enfile",[]),
124    %% Out of sockets...
125    sleep(200);
126
127handle_error(emfile, _, _) ->
128    ?vinfo("Accept error: emfile",[]),
129    %% Too many open files -> Out of sockets...
130    sleep(200);
131
132handle_error(closed, _, _) ->
133    ?vlog("Accept error: closed",[]),
134    %% This propably only means that the application is stopping,
135    %% but just in case
136    exit(closed);
137
138handle_error(econnaborted, _, _) ->
139    ?vlog("Accept aborted",[]),
140    ok;
141
142handle_error(esslaccept, _, _) ->
143    %% The user has selected to cancel the installation of
144    %% the certifikate, This is not a real error, so we do
145    %% not write an error message.
146    ok;
147
148handle_error({'EXIT', Reason}, ConfigDb, SocketType) ->
149    ?vinfo("Accept exit:~n   ~p",[Reason]),
150    String = lists:flatten(io_lib:format("Accept exit: ~p", [Reason])),
151    accept_failed(SocketType, ConfigDb, String);
152
153handle_error(Reason, ConfigDb, SocketType) ->
154    ?vinfo("Accept error:~n   ~p",[Reason]),
155    String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
156    accept_failed(SocketType, ConfigDb, String).
157
158
159accept_failed(SocketType, ConfigDb, String) ->
160    error_logger:error_report(String),
161    mod_log:error_log(SocketType, undefined, ConfigDb,
162		      {0, "unknown"}, String),
163    mod_disk_log:error_log(SocketType, undefined, ConfigDb,
164			   {0, "unknown"}, String),
165    exit({accept_failed, String}).
166
167
168report_error(Db, String) ->
169    error_logger:error_report(String),
170    mod_log:report_error(Db, String),
171    mod_disk_log:report_error(Db, String).
172
173
174sleep(T) -> receive after T -> ok end.
175