~ubuntu-branches/debian/wheezy/couchdb/wheezy

« back to all changes in this revision

Viewing changes to src/couch_inets/httpd_request_handler.erl

  • Committer: Bazaar Package Importer
  • Author(s): Noah Slater
  • Date: 2008-02-06 17:03:38 UTC
  • Revision ID: james.westby@ubuntu.com-20080206170338-y411anylx3oplqid
Tags: upstream-0.7.3~svn684
ImportĀ upstreamĀ versionĀ 0.7.3~svn684

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%% ``The contents of this file are subject to the Erlang Public License,
 
2
%% Version 1.1, (the "License"); you may not use this file except in
 
3
%% compliance with the License. You should have received a copy of the
 
4
%% Erlang Public License along with this software. If not, it can be
 
5
%% retrieved via the world wide web at http://www.erlang.org/.
 
6
%% 
 
7
%% Software distributed under the License is distributed on an "AS IS"
 
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
9
%% the License for the specific language governing rights and limitations
 
10
%% under the License.
 
11
%% 
 
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 
14
%% AB. All Rights Reserved.''
 
15
%% 
 
16
%%     $Id$
 
17
%%
 
18
%% Description: Implements a request handler process for the HTTP server.  
 
19
%% 
 
20
 
 
21
-module(httpd_request_handler).
 
22
 
 
23
-behaviour(gen_server).
 
24
 
 
25
%% Application internal API
 
26
-export([start/2, start/3, socket_ownership_transfered/3]).
 
27
 
 
28
%% gen_server callbacks
 
29
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 
30
         terminate/2, code_change/3]).
 
31
 
 
32
-include("httpd.hrl").
 
33
-include("http_internal.hrl").
 
34
 
 
35
-record(state, {mod,     %% #mod{}
 
36
                manager, %% pid()
 
37
                status,  %% accept | busy | blocked
 
38
                mfa,     %% {Module, Function, Args} 
 
39
                max_keep_alive_request = infinity, %% integer() | infinity
 
40
                response_sent = false, %% true | false 
 
41
                timeout,  %% infinity | integer() > 0
 
42
                timer,     %% ref() - Request timer
 
43
                headers,  %% #http_request_h{}
 
44
                body      %% binary()
 
45
               }).
 
46
 
 
47
%%====================================================================
 
48
%% Application internal API
 
49
%%====================================================================
 
50
%%--------------------------------------------------------------------
 
51
%% Function: start() -> {ok, Pid} | ignore | {error,Error}
 
52
%% Description: Starts a httpd-request handler process. Intended to be
 
53
%% called by the httpd acceptor process.
 
54
%%--------------------------------------------------------------------
 
55
start(Manager, ConfigDB) ->
 
56
    start(Manager, ConfigDB, 15000).
 
57
start(Manager, ConfigDB, AcceptTimeout) ->
 
58
    proc_lib:start(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]).
 
59
 
 
60
%%--------------------------------------------------------------------
 
61
%% socket_ownership_transfered(Pid, SocketType, Socket) -> void()
 
62
%%
 
63
%% Pid = pid()
 
64
%% SocketType = ip_comm | ssl 
 
65
%% Socket = socket()
 
66
%%
 
67
%% Description: Send a message to the request handler process
 
68
%% confirming that the socket ownership has now sucssesfully been
 
69
%% transfered to it. Intended to be called by the httpd acceptor
 
70
%% process.
 
71
%%--------------------------------------------------------------------
 
72
socket_ownership_transfered(Pid, SocketType, Socket) ->
 
73
    Pid ! {socket_ownership_transfered, SocketType, Socket}.
 
74
 
 
75
%%--------------------------------------------------------------------
 
76
%% Function: init(Args) -> _
 
77
%%
 
78
%% Description: Initiates the server. Obs special init that uses 
 
79
%% gen_server:enter_loop/3. This is used instead of the normal
 
80
%% gen_server callback init, as a more complex init than the
 
81
%% gen_server provides is needed. 
 
82
%%--------------------------------------------------------------------
 
83
init([Manager, ConfigDB,AcceptTimeout]) ->
 
84
    %% Make sure this process terminates if the httpd manager process
 
85
    %% should die!
 
86
    link(Manager), 
 
87
    %% At this point the function httpd_request_handler:start/2 will return.
 
88
    proc_lib:init_ack({ok, self()}),
 
89
    
 
90
    {SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout),
 
91
    
 
92
    Resolve = http_transport:resolve(),
 
93
    Peername = httpd_socket:peername(SocketType, Socket),
 
94
    InitData = #init_data{peername = Peername, resolve = Resolve},
 
95
    Mod = #mod{config_db = ConfigDB, 
 
96
               socket_type = SocketType, 
 
97
               socket = Socket,
 
98
               init_data = InitData},
 
99
 
 
100
    MaxHeaderSize = httpd_util:lookup(ConfigDB, max_header_size, 
 
101
                                      ?HTTP_MAX_HEADER_SIZE),
 
102
    TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
 
103
    NrOfRequest = httpd_util:lookup(ConfigDB, 
 
104
                                    max_keep_alive_request, infinity),
 
105
    
 
106
    {_, Status} = httpd_manager:new_connection(Manager),
 
107
    
 
108
    
 
109
    State = #state{mod = Mod, manager = Manager, status = Status,
 
110
                   timeout = TimeOut, max_keep_alive_request = NrOfRequest,
 
111
                   mfa = {httpd_request, parse, [MaxHeaderSize]}}, 
 
112
    
 
113
    NewState = activate_request_timeout(State),
 
114
 
 
115
    http_transport:setopts(SocketType, Socket, [binary,{packet, 0},
 
116
                                                {active, once}]),
 
117
    gen_server:enter_loop(?MODULE, [], NewState).
 
118
            
 
119
%%====================================================================
 
120
%% gen_server callbacks
 
121
%%====================================================================
 
122
 
 
123
%%--------------------------------------------------------------------
 
124
%% handle_call(Request, From, State) -> {reply, Reply, State} |
 
125
%%                                      {reply, Reply, State, Timeout} |
 
126
%%                                      {noreply, State} |
 
127
%%                                      {noreply, State, Timeout} |
 
128
%%                                      {stop, Reason, Reply, State} |
 
129
%%                                      {stop, Reason, State}
 
130
%% Description: Handling call messages
 
131
%%--------------------------------------------------------------------
 
132
handle_call(Request, From, State) ->
 
133
    {stop, {call_api_violation, Request, From}, State}.
 
134
 
 
135
%%--------------------------------------------------------------------
 
136
%% handle_cast(Msg, State) -> {noreply, State} |
 
137
%%                                      {noreply, State, Timeout} |
 
138
%%                                      {stop, Reason, State}
 
139
%% Description: Handling cast messages
 
140
%%--------------------------------------------------------------------
 
141
handle_cast(Msg, State) ->
 
142
    {reply, {cast_api_violation, Msg}, State}.
 
143
 
 
144
%%--------------------------------------------------------------------
 
145
%% handle_info(Info, State) -> {noreply, State} |
 
146
%%                                       {noreply, State, Timeout} |
 
147
%%                                       {stop, Reason, State}
 
148
%% Description: Handling all non call/cast messages
 
149
%%--------------------------------------------------------------------
 
150
handle_info({Proto, Socket, Data}, State = 
 
151
            #state{mfa = {Module, Function, Args},
 
152
                   mod = #mod{socket_type = SockType, 
 
153
                              socket = Socket} = ModData} 
 
154
            = State) when Proto == tcp; Proto == ssl; Proto == dummy ->
 
155
  
 
156
    case Module:Function([Data | Args]) of
 
157
        {ok, Result} ->
 
158
            NewState = cancel_request_timeout(State),
 
159
            handle_http_msg(Result, NewState); 
 
160
        {error, {header_too_long, MaxHeaderSize}, Version} ->
 
161
            NewModData =  ModData#mod{http_version = Version},
 
162
            httpd_response:send_status(NewModData, 413, "Header too big"),
 
163
            Reason = io_lib:format("Header too big, max size is ~p~n", 
 
164
                                   [MaxHeaderSize]),
 
165
            error_log(Reason, NewModData),
 
166
            {stop, normal, State#state{response_sent = true, 
 
167
                                       mod = NewModData}};
 
168
        NewMFA ->
 
169
            http_transport:setopts(SockType, Socket, [{active, once}]),
 
170
            {noreply, State#state{mfa = NewMFA}}
 
171
    end;
 
172
 
 
173
%% Error cases
 
174
handle_info({tcp_closed, _}, State) ->
 
175
    {stop, normal, State};
 
176
handle_info({ssl_closed, _}, State) ->
 
177
    {stop, normal, State};
 
178
handle_info({tcp_error, _, _} = Reason, State) ->
 
179
    {stop, Reason, State};
 
180
handle_info({ssl_error, _, _} = Reason, State) ->
 
181
    {stop, Reason, State};
 
182
 
 
183
%% Timeouts
 
184
handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) ->
 
185
    error_log("No request received on keep-alive connection" 
 
186
              "before server side timeout", ModData),
 
187
    %% No response should be sent!
 
188
    {stop, normal, State#state{response_sent = true}}; 
 
189
handle_info(timeout, #state{mod = ModData} = State) ->
 
190
    httpd_response:send_status(ModData, 408, "Request timeout"),
 
191
    error_log("The client did not send the whole request before the"
 
192
              "server side timeout", ModData),
 
193
    {stop, normal, State#state{response_sent = true}};
 
194
 
 
195
%% Default case
 
196
handle_info(Info, #state{mod = ModData} = State) ->
 
197
    Error = lists:flatten(
 
198
              io_lib:format("Unexpected message received: ~n~p~n", [Info])),
 
199
    error_log(Error, ModData),
 
200
    {noreply, State}.
 
201
 
 
202
%%--------------------------------------------------------------------
 
203
%% terminate(Reason, State) -> void()
 
204
%%
 
205
%% Description: This function is called by a gen_server when it is about to
 
206
%% terminate. It should be the opposite of Module:init/1 and do any necessary
 
207
%% cleaning up. When it returns, the gen_server terminates with Reason.
 
208
%% The return value is ignored.
 
209
%%--------------------------------------------------------------------
 
210
terminate(normal, State) ->
 
211
    do_terminate(State);
 
212
terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
 
213
    httpd_response:send_status(ModData, 500, none),
 
214
    error_log(httpd_util:reason_phrase(500), ModData),
 
215
    terminate(Reason, State#state{response_sent = true, mod = ModData});
 
216
terminate(_, State) ->
 
217
    do_terminate(State).
 
218
 
 
219
do_terminate(#state{mod = ModData, manager = Manager} = State) ->
 
220
    catch httpd_manager:done_connection(Manager),
 
221
    cancel_request_timeout(State),
 
222
    httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket).
 
223
 
 
224
%%--------------------------------------------------------------------
 
225
%% code_change(OldVsn, State, Extra) -> {ok, NewState}
 
226
%%
 
227
%% Description: Convert process state when code is changed
 
228
%%--------------------------------------------------------------------
 
229
code_change(_OldVsn, State, _Extra) ->
 
230
    {ok, State}.
 
231
 
 
232
%%--------------------------------------------------------------------
 
233
%%% Internal functions
 
234
%%--------------------------------------------------------------------
 
235
await_socket_ownership_transfer(AcceptTimeout) ->
 
236
    receive
 
237
        {socket_ownership_transfered, SocketType, Socket} ->
 
238
            {SocketType, Socket}
 
239
    after AcceptTimeout ->
 
240
            exit(accept_socket_timeout)
 
241
    end.
 
242
 
 
243
handle_http_msg({_, _, Version, {_, _}, _}, #state{status = busy,
 
244
                                                   mod = ModData} = State) -> 
 
245
    handle_manager_busy(State#state{mod = 
 
246
                                    ModData#mod{http_version = Version}}),
 
247
    {stop, normal, State}; 
 
248
 
 
249
handle_http_msg({_, _, Version, {_, _}, _}, 
 
250
                #state{status = blocked, mod = ModData} = State) ->
 
251
    handle_manager_blocked(State#state{mod = 
 
252
                                       ModData#mod{http_version = Version}}),
 
253
    {stop, normal, State}; 
 
254
 
 
255
handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
 
256
                #state{status = accept, mod = ModData} = State) ->        
 
257
    case httpd_request:validate(Method, Uri, Version) of
 
258
        ok  ->
 
259
            {ok, NewModData} = 
 
260
                httpd_request:update_mod_data(ModData, Method, Uri,
 
261
                                              Version, Headers),
 
262
      
 
263
            case is_host_specified_if_required(NewModData#mod.absolute_uri,
 
264
                                               RecordHeaders, Version) of
 
265
                true ->
 
266
                    handle_body(State#state{headers = RecordHeaders,
 
267
                                            body = Body,
 
268
                                            mod = NewModData});
 
269
                false ->
 
270
                    httpd_response:send_status(ModData#mod{http_version = 
 
271
                                                           Version}, 
 
272
                                               400, none),
 
273
                    {stop, normal, State#state{response_sent = true}}
 
274
            end;
 
275
        {error, {not_supported, What}} ->
 
276
            httpd_response:send_status(ModData#mod{http_version = Version},
 
277
                                       501, {Method, Uri, Version}),
 
278
            Reason = io_lib:format("Not supported: ~p~n", [What]),
 
279
            error_log(Reason, ModData),
 
280
            {stop, normal, State#state{response_sent = true}};
 
281
        {error, {bad_request, {forbidden, URI}}} ->
 
282
            httpd_response:send_status(ModData#mod{http_version = Version},
 
283
                                       403, URI),
 
284
            Reason = io_lib:format("Forbidden URI: ~p~n", [URI]),
 
285
            error_log(Reason, ModData),
 
286
            {stop, normal, State#state{response_sent = true}};
 
287
        {error,{bad_request, {malformed_syntax, URI}}} ->
 
288
            httpd_response:send_status(ModData#mod{http_version = Version},
 
289
                                       400, URI),
 
290
            Reason = io_lib:format("Malformed syntax in URI: ~p~n", [URI]),
 
291
            error_log(Reason, ModData),
 
292
            {stop, normal, State#state{response_sent = true}}
 
293
    end;
 
294
handle_http_msg({ChunkedHeaders, Body}, 
 
295
                State = #state{headers = Headers}) ->
 
296
    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
 
297
    handle_response(State#state{headers = NewHeaders, body = Body});
 
298
handle_http_msg(Body, State) ->
 
299
    handle_response(State#state{body = Body}).
 
300
 
 
301
handle_manager_busy(#state{mod = #mod{config_db = ConfigDB}} = State) ->
 
302
    MaxClients = httpd_util:lookup(ConfigDB, max_clients, 150),
 
303
    Reason = io_lib:format("heavy load (>~w processes)", [MaxClients]),
 
304
    reject_connection(State, lists:flatten(Reason)).
 
305
 
 
306
handle_manager_blocked(State) ->
 
307
    Reason = "Server maintenance performed, try again later",
 
308
    reject_connection(State, Reason).
 
309
 
 
310
reject_connection(#state{mod = ModData} = State, Reason) ->
 
311
    httpd_response:send_status(ModData, 503, Reason),
 
312
    {stop, normal, State#state{response_sent = true}}. 
 
313
 
 
314
is_host_specified_if_required(nohost, #http_request_h{host = undefined}, 
 
315
                              "HTTP/1.1") ->
 
316
    false;
 
317
is_host_specified_if_required(_, _, _) ->
 
318
    true.
 
319
 
 
320
handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) ->
 
321
    
 
322
    MaxHeaderSize =
 
323
        httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE),
 
324
    MaxBodySize = httpd_util:lookup(ConfigDB, max_body_size, nolimit),
 
325
   
 
326
    case handle_expect(State, MaxBodySize) of 
 
327
        ok ->
 
328
            handle_body(State, MaxHeaderSize, MaxBodySize);
 
329
        Other ->
 
330
            Other
 
331
    
 
332
    end.
 
333
        
 
334
handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
 
335
            MaxHeaderSize, MaxBodySize) ->
 
336
    case Headers#http_request_h.'transfer-encoding' of
 
337
        "chunked" ->
 
338
            case http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
 
339
                {Module, Function, Args} ->
 
340
                    http_transport:setopts(ModData#mod.socket_type, 
 
341
                                           ModData#mod.socket, 
 
342
                                           [{active, once}]),
 
343
                    {noreply, State#state{mfa = 
 
344
                                          {Module, Function, Args}}};
 
345
                {ok, {ChunkedHeaders, NewBody}} ->
 
346
                    NewHeaders = 
 
347
                        http_chunk:handle_headers(Headers, ChunkedHeaders),
 
348
                    handle_response(State#state{headers = NewHeaders,
 
349
                                                body = NewBody})
 
350
            end;
 
351
        Encoding when list(Encoding) ->
 
352
            httpd_response:send_status(ModData, 501, 
 
353
                                       "Unknown Transfer-Encoding"),
 
354
            Reason = io_lib:format("Unknown Transfer-Encoding: ~p~n", 
 
355
                                   [Encoding]),
 
356
            error_log(Reason, ModData),
 
357
            {stop, normal, State#state{response_sent = true}};
 
358
        _ -> 
 
359
            Length = 
 
360
                list_to_integer(Headers#http_request_h.'content-length'),
 
361
            case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of
 
362
                true ->
 
363
                    case httpd_request:whole_body(Body, Length) of 
 
364
                        {Module, Function, Args} ->
 
365
                            http_transport:setopts(ModData#mod.socket_type, 
 
366
                                                   ModData#mod.socket, 
 
367
                                                   [{active, once}]),
 
368
                            {noreply, State#state{mfa = 
 
369
                                                  {Module, Function, Args}}};
 
370
                        
 
371
                        {ok, NewBody} ->
 
372
                            handle_response(
 
373
                              State#state{headers = Headers,
 
374
                                          body = NewBody})
 
375
                    end;
 
376
                false ->
 
377
                    httpd_response:send_status(ModData, 413, "Body too big"),
 
378
                    error_log("Body too big", ModData),
 
379
                    {stop, normal,  State#state{response_sent = true}}
 
380
            end
 
381
    end.
 
382
 
 
383
handle_expect(#state{headers = Headers, mod = 
 
384
                     #mod{config_db = ConfigDB} = ModData} = State, 
 
385
              MaxBodySize) ->
 
386
    Length = Headers#http_request_h.'content-length',
 
387
    case expect(Headers, ModData#mod.http_version, ConfigDB) of
 
388
        continue when MaxBodySize > Length; MaxBodySize == nolimit ->
 
389
            httpd_response:send_status(ModData, 100, ""),
 
390
            ok;
 
391
        continue when MaxBodySize < Length ->
 
392
            httpd_response:send_status(ModData, 413, "Body too big"),
 
393
            error_log("Body too big", ModData),
 
394
            {stop, normal, State#state{response_sent = true}};
 
395
        {break, Value} ->
 
396
            httpd_response:send_status(ModData, 417, 
 
397
                                       "Unexpected expect value"),
 
398
            Reason = io_lib:format("Unexpected expect value: ~p~n", [Value]),
 
399
            error_log(Reason, ModData),
 
400
            {stop, normal,  State#state{response_sent = true}};
 
401
        no_expect_header ->
 
402
            ok;
 
403
        http_1_0_expect_header ->
 
404
            httpd_response:send_status(ModData, 400, 
 
405
                                       "Only HTTP/1.1 Clients "
 
406
                                       "may use the Expect Header"),
 
407
            error_log("Client with lower version than 1.1 tried to send"
 
408
                      "an expect header", ModData),
 
409
            {stop, normal, State#state{response_sent = true}}
 
410
    end.
 
411
 
 
412
expect(Headers, "HTTP/1.1", _) ->
 
413
    case Headers#http_request_h.expect of
 
414
        "100-continue" ->
 
415
            continue; 
 
416
        undefined ->
 
417
            no_expect_header;
 
418
        Other ->
 
419
            {break, Other}
 
420
    end;
 
421
expect(Headers, _, ConfigDB) ->
 
422
    case Headers#http_request_h.expect of
 
423
        undefined ->
 
424
            no_expect_header;
 
425
        _ ->
 
426
            case httpd_util:lookup(ConfigDB, expect, continue) of
 
427
                continue->
 
428
                    no_expect_header;
 
429
                _ ->
 
430
                    http_1_0_expect_header
 
431
            end
 
432
    end.
 
433
 
 
434
handle_response(#state{body = Body, mod = ModData, headers = Headers,
 
435
                       max_keep_alive_request = Max} = State) when Max > 0 ->
 
436
    {NewBody, Data} = httpd_request:body_data(Headers, Body),
 
437
    ok = httpd_response:generate_and_send_response(
 
438
           ModData#mod{entity_body = NewBody}),
 
439
    handle_next_request(State#state{response_sent = true}, Data);
 
440
 
 
441
handle_response(#state{body = Body, headers = Headers, 
 
442
                       mod = ModData} = State) ->
 
443
    {NewBody, _} = httpd_request:body_data(Headers, Body),
 
444
    ok = httpd_response:generate_and_send_response(
 
445
           ModData#mod{entity_body = NewBody}),
 
446
    {stop, normal, State#state{response_sent = true}}.
 
447
 
 
448
handle_next_request(#state{mod = #mod{connection = true} = ModData,
 
449
                          max_keep_alive_request = Max} = State, Data) ->
 
450
    NewModData = #mod{socket_type = ModData#mod.socket_type, 
 
451
                      socket = ModData#mod.socket, 
 
452
                      config_db = ModData#mod.config_db, 
 
453
                      init_data = ModData#mod.init_data},
 
454
    MaxHeaderSize =
 
455
        httpd_util:lookup(ModData#mod.config_db, 
 
456
                          max_header_size, ?HTTP_MAX_HEADER_SIZE),
 
457
   
 
458
     TmpState = State#state{mod = NewModData,
 
459
                           mfa = {httpd_request, parse, [MaxHeaderSize]},
 
460
                            max_keep_alive_request = decrease(Max),
 
461
                           headers = undefined, body = undefined,
 
462
                           response_sent = false},
 
463
    
 
464
    NewState = activate_request_timeout(TmpState),
 
465
 
 
466
    case Data of
 
467
        <<>> ->
 
468
            http_transport:setopts(ModData#mod.socket_type,
 
469
                                   ModData#mod.socket, [{active, once}]),
 
470
            {noreply, NewState};
 
471
        _ ->
 
472
            handle_info({dummy, ModData#mod.socket, Data}, NewState)
 
473
    end;
 
474
 
 
475
handle_next_request(State, _) ->
 
476
    {stop, normal, State}.
 
477
 
 
478
activate_request_timeout(#state{timeout = Time} = State) ->
 
479
    Ref = erlang:send_after(Time, self(), timeout),
 
480
    State#state{timer = Ref}.
 
481
 
 
482
cancel_request_timeout(#state{timer = undefined} = State) ->
 
483
    State;
 
484
cancel_request_timeout(#state{timer = Timer} = State) ->
 
485
    erlang:cancel_timer(Timer),
 
486
    receive 
 
487
        timeout ->
 
488
            ok
 
489
    after 0 ->
 
490
            ok
 
491
    end,
 
492
    State#state{timer = undefined}.
 
493
 
 
494
decrease(N) when integer(N)->
 
495
    N-1;
 
496
decrease(N) ->
 
497
    N.
 
498
 
 
499
error_log(ReasonString, #mod{socket = Socket, socket_type = SocketType,
 
500
                             config_db = ConfigDB, 
 
501
                             init_data = #init_data{peername = Peername}}) ->
 
502
    Error = lists:flatten(
 
503
              io_lib:format("Error reading request: ~s",[ReasonString])),
 
504
    error_log(mod_log, SocketType, Socket, ConfigDB, Peername, Error),
 
505
    error_log(mod_disk_log, SocketType, Socket, ConfigDB, Peername, Error).
 
506
 
 
507
error_log(Mod, SocketType, Socket, ConfigDB, Peername, String) ->
 
508
    Modules = httpd_util:lookup(ConfigDB, modules,
 
509
                                [mod_get, mod_head, mod_log]),
 
510
    case lists:member(Mod, Modules) of
 
511
        true ->
 
512
            Mod:error_log(SocketType, Socket, ConfigDB, Peername, String);
 
513
        _ ->
 
514
            ok
 
515
    end.
 
516