1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2017. 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%% Description: Implements a request handler process for the HTTP server.
22%%
23
24-module(httpd_request_handler).
25
26-behaviour(gen_server).
27
28%% Application internal API
29-export([start_link/2, start_link/3, socket_ownership_transfered/3]).
30
31%% gen_server callbacks
32-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
33	 terminate/2, code_change/3, format_status/2]).
34
35-include("httpd.hrl").
36-include("http_internal.hrl").
37-include_lib("kernel/include/logger.hrl").
38
39-define(HANDSHAKE_TIMEOUT, 5000).
40
41-record(state, {mod,     %% #mod{}
42		manager, %% pid()
43		status,  %% accept | busy | blocked
44		mfa,     %% {Module, Function, Args}
45		max_keep_alive_request = infinity, %% integer() | infinity
46		response_sent = false :: boolean(),
47		timeout,   %% infinity | integer() > 0
48		timer      :: 'undefined' | reference(), % Request timer
49		headers,   %% #http_request_h{}
50		body,      %% binary()
51		data,      %% The total data received in bits, checked after 10s
52		byte_limit, %% Bit limit per second before kick out
53                chunk
54	       }).
55
56%%====================================================================
57%% Application internal API
58%%====================================================================
59%%--------------------------------------------------------------------
60%% Function: start() -> {ok, Pid} | ignore | {error,Error}
61%% Description: Starts a httpd-request handler process. Intended to be
62%% called by the httpd acceptor process.
63%%--------------------------------------------------------------------
64start_link(Manager, ConfigDB) ->
65    start_link(Manager, ConfigDB, 15000).
66start_link(Manager, ConfigDB, AcceptTimeout) ->
67    proc_lib:start_link(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]).
68
69
70%%--------------------------------------------------------------------
71%% socket_ownership_transfered(Pid, SocketType, Socket) -> void()
72%%
73%% Pid = pid()
74%% SocketType = ip_comm | ssl
75%% Socket = socket()
76%%
77%% Description: Send a message to the request handler process
78%% confirming that the socket ownership has now sucssesfully been
79%% transfered to it. Intended to be called by the httpd acceptor
80%% process.
81%%--------------------------------------------------------------------
82socket_ownership_transfered(Pid, SocketType, Socket) ->
83    Pid ! {socket_ownership_transfered, SocketType, Socket}.
84
85%%--------------------------------------------------------------------
86%% Function: init(Args) -> _
87%%
88%% Description: Initiates the server. Obs special init that uses
89%% gen_server:enter_loop/3. This is used instead of the normal
90%% gen_server callback init, as a more complex init than the
91%% gen_server provides is needed.
92%%--------------------------------------------------------------------
93init([Manager, ConfigDB, AcceptTimeout]) ->
94    process_flag(trap_exit, true),
95    %% Make sure this process terminates if the httpd manager process
96    %% should die!
97    %%link(Manager),
98    %% At this point the function httpd_request_handler:start/2 will return.
99    proc_lib:init_ack({ok, self()}),
100
101    {SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout),
102
103    Peername = http_transport:peername(SocketType, Socket),
104    Sockname = http_transport:sockname(SocketType, Socket),
105
106    %%Timeout value is in seconds we want it in milliseconds
107    KeepAliveTimeOut = 1000 * httpd_util:lookup(ConfigDB, keep_alive_timeout, 150),
108
109    case http_transport:negotiate(SocketType, Socket, ?HANDSHAKE_TIMEOUT) of
110	{error, {tls_alert, {_, AlertDesc}} = Error} ->
111            ModData = #mod{config_db = ConfigDB, init_data = #init_data{peername = Peername,
112                                                                        sockname = Sockname}},
113            httpd_util:error_log(ConfigDB, httpd_logger:error_report('TLS', AlertDesc,
114                                                                     ModData, ?LOCATION)),
115	    exit({shutdown, Error});
116        {error, _Reason} = Error ->
117            %% This happens if the peer closes the connection
118            %% or the handshake is timed out. This is not
119            %% an error condition of the server and client will
120            %% retry in the timeout situation.
121            exit({shutdown, Error});
122        {ok, TLSSocket} ->
123	    continue_init(Manager, ConfigDB, SocketType, TLSSocket,
124                          Peername, Sockname, KeepAliveTimeOut);
125        ok  ->
126            continue_init(Manager, ConfigDB, SocketType, Socket,
127                          Peername, Sockname, KeepAliveTimeOut)
128    end.
129
130continue_init(Manager, ConfigDB, SocketType, Socket, Peername, Sockname,
131              TimeOut) ->
132    Resolve = http_transport:resolve(),
133    InitData = #init_data{peername = Peername,
134                          sockname = Sockname,
135                          resolve = Resolve},
136    Mod = #mod{config_db = ConfigDB,
137               socket_type = SocketType,
138               socket = Socket,
139               init_data = InitData},
140
141    MaxHeaderSize = max_header_size(ConfigDB),
142    MaxURISize    = max_uri_size(ConfigDB),
143    NrOfRequest   = max_keep_alive_request(ConfigDB),
144    MaxContentLen = max_content_length(ConfigDB),
145    Customize = customize(ConfigDB),
146    MaxChunk = max_client_body_chunk(ConfigDB),
147
148    {_, Status} = httpd_manager:new_connection(Manager),
149
150    MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
151				   {max_version, ?HTTP_MAX_VERSION_STRING},
152				   {max_method, ?HTTP_MAX_METHOD_STRING},
153				   {max_content_length, MaxContentLen},
154				   {customize, Customize}
155				  ]]},
156
157    State = #state{mod                    = Mod,
158		   manager                = Manager,
159		   status                 = Status,
160		   timeout                = TimeOut,
161		   max_keep_alive_request = NrOfRequest,
162		   mfa                    = MFA,
163                   chunk                   = chunk_start(MaxChunk)},
164    setopts(Socket, SocketType, [binary, {packet, 0}, {active, once}]),
165    NewState =  data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)),
166     gen_server:enter_loop(?MODULE, [], NewState).
167
168
169%%====================================================================
170%% gen_server callbacks
171%%====================================================================
172
173%%--------------------------------------------------------------------
174%% handle_call(Request, From, State) -> {reply, Reply, State} |
175%%                                      {reply, Reply, State, Timeout} |
176%%                                      {noreply, State} |
177%%                                      {noreply, State, Timeout} |
178%%                                      {stop, Reason, Reply, State} |
179%%                                      {stop, Reason, State}
180%% Description: Handling call messages
181%%--------------------------------------------------------------------
182handle_call(Request, From, #state{mod = #mod{config_db = Db} = ModData} = State) ->
183    httpd_util:error_log(Db,
184                         httpd_logger:error_report(internal,
185                                                   [{unexpected_call, Request}, {to, self()}, {from, From}], ModData,
186                                                   ?LOCATION)),
187    {stop, {call_api_violation, Request, From}, State}.
188
189%%--------------------------------------------------------------------
190%% handle_cast(Msg, State) -> {noreply, State} |
191%%                                      {noreply, State, Timeout} |
192%%                                      {stop, Reason, State}
193%% Description: Handling cast messages
194%%--------------------------------------------------------------------
195handle_cast(Msg, #state{mod = #mod{config_db = Db} = ModData} = State) ->
196    httpd_util:error_log(Db,
197                         httpd_logger:error_report(internal, [{unexpected_cast, Msg}, {to, self()}], ModData,
198                                                   ?LOCATION)),
199    {noreply, State}.
200
201%%--------------------------------------------------------------------
202%% handle_info(Info, State) -> {noreply, State} |
203%%                                       {noreply, State, Timeout} |
204%%                                       {stop, Reason, State}
205%% Description: Handling all non call/cast messages
206%%--------------------------------------------------------------------
207handle_info({Proto, Socket, Data},
208	    #state{mfa = {Module, Function, Args},
209                   chunk = {ChunkState, _},
210		   mod = #mod{socket_type = SockType,
211			      socket = Socket} = ModData} = State)
212  when (((Proto =:= tcp) orelse
213	 (Proto =:= ssl) orelse
214	 (Proto =:= dummy)) andalso is_binary(Data)) ->
215
216    PROCESSED = (catch Module:Function([Data | Args])),
217    NewDataSize = case State#state.byte_limit of
218		      undefined ->
219			  undefined;
220		      _ ->
221			  State#state.data + byte_size(Data)
222		  end,
223
224    case PROCESSED of
225        {ok, Result} ->
226	    NewState = case NewDataSize of
227			   undefined ->
228			       cancel_request_timeout(State);
229			   _ ->
230			       set_new_data_size(cancel_request_timeout(State), NewDataSize)
231		       end,
232            handle_msg(Result, NewState);
233	{error, {size_error, MaxSize, ErrCode, ErrStr}, Version} ->
234	    NewModData =  ModData#mod{http_version = Version},
235	    httpd_response:send_status(NewModData, ErrCode, ErrStr, {max_size, MaxSize}),
236	    {stop, normal, State#state{response_sent = true,
237				       mod = NewModData}};
238
239    {error, {version_error, ErrCode, ErrStr}, Version} ->
240        NewModData =  ModData#mod{http_version = Version},
241	    httpd_response:send_status(NewModData, ErrCode, ErrStr),
242	    {stop, normal, State#state{response_sent = true,
243				                   mod = NewModData}};
244
245    {http_chunk = Module, Function, Args} when ChunkState =/= undefined ->
246        NewState = handle_chunk(Module, Function, Args, State),
247        {noreply, NewState};
248	NewMFA ->
249        setopts(Socket, SockType, [{active, once}]),
250	    case NewDataSize of
251		undefined ->
252		    {noreply, State#state{mfa = NewMFA}};
253		_ ->
254		    {noreply, State#state{mfa = NewMFA, data = NewDataSize}}
255	    end
256    end;
257
258%% Error cases
259handle_info({tcp_closed, _}, State) ->
260    {stop, normal, State};
261handle_info({ssl_closed, _}, State) ->
262    {stop, normal, State};
263handle_info({tcp_error, _, _} = Reason, State) ->
264    {stop, {shutdown, Reason}, State};
265handle_info({ssl_error, _, _} = Reason, State) ->
266    {stop, {shutdown, Reason}, State};
267
268%% Timeouts
269handle_info(timeout, #state{mfa = {_, parse, _}} = State) ->
270    %% No request received on keep-alive connection
271    %% before server side timeout. No response should be sent!
272    {stop, normal, State#state{response_sent = true}};
273handle_info(timeout, #state{mod = ModData} = State) ->
274    httpd_response:send_status(ModData, 408, "Request timeout", "The client did not send the whole request before the "
275                               "server side timeout"),
276    {stop, normal, State#state{response_sent = true}};
277handle_info(check_data_first, #state{data = Data, byte_limit = Byte_Limit} = State) ->
278    case Data >= (Byte_Limit*3) of
279	true ->
280	    erlang:send_after(1000, self(), check_data),
281	    {noreply, State#state{data = 0}};
282	_ ->
283	    {stop, normal, State#state{response_sent = true}}
284    end;
285handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) ->
286    case Data >= Byte_Limit of
287	true ->
288	    erlang:send_after(1000, self(), check_data),
289	    {noreply, State#state{data = 0}};
290	_ ->
291	    {stop, normal, State#state{response_sent = true}}
292    end;
293
294handle_info({'EXIT', _, Reason}, State) ->
295    {stop, Reason, State};
296
297%% Default case
298handle_info(Info, #state{mod = #mod{config_db = Db} =ModData} = State) ->
299    httpd_util:error_log(Db,
300                         httpd_logger:error_report(internal,
301                                                   [{unexpected_info, Info}, {to, self()}], ModData,
302                                                   ?LOCATION)),
303    {noreply, State}.
304
305
306%%--------------------------------------------------------------------
307%% terminate(Reason, State) -> void()
308%%
309%% Description: This function is called by a gen_server when it is about to
310%% terminate. It should be the opposite of Module:init/1 and do any necessary
311%% cleaning up. When it returns, the gen_server terminates with Reason.
312%% The return value is ignored.
313%%--------------------------------------------------------------------
314terminate(Reason, State) when Reason == normal;
315			      Reason == shutdown ->
316    do_terminate(State);
317terminate({shutdown,_}, State) ->
318    do_terminate(State);
319terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
320    httpd_response:send_status(ModData, 500, none),
321    terminate(Reason, State#state{response_sent = true, mod = ModData});
322terminate(_Reason, State) ->
323    do_terminate(State).
324
325do_terminate(#state{mod = ModData} = State) ->
326    cancel_request_timeout(State),
327    httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket).
328
329format_status(normal, [_, State]) ->
330    [{data, [{"StateData", State}]}];
331format_status(terminate, [_, State]) ->
332    Mod = (State#state.mod),
333    case Mod#mod.socket_type of
334	ip_comm ->
335	    [{data, [{"StateData", State}]}];
336	{essl, _} ->
337	    %% Do not print ssl options in superviosr reports
338	    [{data, [{"StateData",
339		      State#state{mod = Mod#mod{socket_type = 'TLS'}}}]}]
340    end.
341
342%%--------------------------------------------------------------------
343%% code_change(OldVsn, State, Extra) -> {ok, NewState}
344%%
345%% Description: Convert process state when code is changed
346%%--------------------------------------------------------------------
347code_change(_OldVsn, State, _Extra) ->
348    {ok, State}.
349
350
351%%--------------------------------------------------------------------
352%%% Internal functions
353%%--------------------------------------------------------------------
354set_new_data_size(State, NewData) ->
355    State#state{data = NewData}.
356await_socket_ownership_transfer(AcceptTimeout) ->
357    receive
358	{socket_ownership_transfered, SocketType, Socket} ->
359	    {SocketType, Socket}
360    after AcceptTimeout ->
361	    exit(accept_socket_timeout)
362    end.
363
364
365%%% Internal chunking of client body
366handle_msg({{continue, Chunk}, Module, Function, Args}, #state{chunk = {_, CbState}} = State) ->
367    handle_internal_chunk(State#state{chunk = {continue, CbState},
368                                      body = Chunk}, Module, Function, Args);
369handle_msg({continue, Module, Function, Args}, 	#state{mod = ModData} = State) ->
370    setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]),
371    {noreply, State#state{mfa = {Module, Function, Args}}};
372handle_msg({last, Body}, #state{headers = Headers, chunk = {_, CbState}} = State) ->
373    NewHeaders = Headers#http_request_h{'content-length' = integer_to_list(size(Body))},
374    handle_response(State#state{chunk = {last, CbState},
375                                headers = NewHeaders,
376                                body = Body});
377%%% Last data chunked by client
378handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {ChunkState, CbState}} = State) when ChunkState =/= undefined ->
379    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
380    handle_response(State#state{chunk = {last, CbState},
381                                headers = NewHeaders,
382                                body = Body});
383handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {undefined, _}} = State) ->
384    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
385    handle_response(State#state{headers = NewHeaders,
386                                body = Body});
387handle_msg(Result, State) ->
388    handle_http_msg(Result, State).
389
390handle_http_msg({_, _, Version, {_, _}, _},
391		#state{status = busy, mod = ModData} = State) ->
392    handle_manager_busy(State#state{mod =
393				    ModData#mod{http_version = Version}}),
394    {stop, normal, State};
395
396handle_http_msg({_, _, Version, {_, _}, _},
397		#state{status = blocked, mod = ModData} = State) ->
398    handle_manager_blocked(State#state{mod =
399				       ModData#mod{http_version = Version}}),
400    {stop, normal, State};
401
402handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
403		#state{status = accept, mod = ModData} = State) ->
404    case httpd_request:validate(Method, Uri, Version) of
405	{ok, NormalizedURI}  ->
406	    {ok, NewModData} =
407		httpd_request:update_mod_data(ModData, Method, NormalizedURI,
408					      Version, Headers),
409
410	    case is_host_specified_if_required(NewModData#mod.absolute_uri,
411					       RecordHeaders, Version) of
412		true ->
413		    handle_body(State#state{headers = RecordHeaders,
414					    body = Body,
415					    mod = NewModData});
416		false ->
417		    httpd_response:send_status(ModData#mod{http_version =
418							   Version},
419					       400, none),
420		    {stop, normal, State#state{response_sent = true}}
421	    end;
422	{error, {not_supported, What}} ->
423	    httpd_response:send_status(ModData#mod{http_version = Version},
424				       501, {Method, Uri, Version}, {not_sup, What}),
425	    {stop, normal, State#state{response_sent = true}};
426	{error, {bad_request, {malformed_syntax, URI}}} ->
427	    httpd_response:send_status(ModData#mod{http_version = Version},
428				       400, URI, {malformed_syntax, URI}),
429	    {stop, normal, State#state{response_sent = true}};
430	{error, {bad_version, Ver}} ->
431	    httpd_response:send_status(
432            ModData#mod{http_version = httpd_request:default_version()},
433            400, Ver, {malformed_syntax, Ver}),
434	    {stop, normal, State#state{response_sent = true}}
435    end;
436handle_http_msg(Body, State) ->
437    handle_response(State#state{body = Body}).
438
439handle_manager_busy(#state{mod = #mod{config_db = ConfigDB}} = State) ->
440    MaxClients = httpd_util:lookup(ConfigDB, max_clients, 150),
441    Reason = io_lib:format("heavy load (>~w processes)", [MaxClients]),
442    reject_connection(State, lists:flatten(Reason)).
443
444handle_manager_blocked(State) ->
445    Reason = "Server maintenance performed, try again later",
446    reject_connection(State, Reason).
447
448reject_connection(#state{mod = ModData} = State, Reason) ->
449    httpd_response:send_status(ModData, 503, Reason),
450    {stop, normal, State#state{response_sent = true}}.
451
452is_host_specified_if_required(nohost, #http_request_h{host = undefined},
453			      "HTTP/1.1") ->
454    false;
455is_host_specified_if_required(_, _, _) ->
456    true.
457
458handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) ->
459    MaxHeaderSize = max_header_size(ConfigDB),
460    MaxBodySize   = max_body_size(ConfigDB),
461
462    case handle_expect(State, MaxBodySize) of
463	ok ->
464	    handle_body(State, MaxHeaderSize, MaxBodySize);
465	Other ->
466	    Other
467
468    end.
469
470handle_body(#state{headers = Headers, body = Body,
471                   chunk =  {ChunkState, CbState}, mod = #mod{config_db = ConfigDB} = ModData} = State,
472	    MaxHeaderSize, MaxBodySize) ->
473    MaxChunk = max_client_body_chunk(ConfigDB),
474    case Headers#http_request_h.'transfer-encoding' of
475	"chunked" ->
476	    try http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
477                {Module, Function, Args} ->
478                    setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]),
479		    {noreply, State#state{mfa =
480                                              {Module, Function, Args},
481                                          chunk = chunk_start(MaxChunk)}};
482                {ok, {ChunkedHeaders, NewBody}} ->
483		    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
484                    handle_response(State#state{headers = NewHeaders,
485                                                body = NewBody,
486                                                chunk = chunk_finish(ChunkState, CbState, MaxChunk)})
487	    catch
488		throw:Error ->
489		    httpd_response:send_status(ModData, 400,
490					       "Bad input", {chunk_decoding, bad_input, Error}),
491		    {stop, normal, State#state{response_sent = true}}
492	    end;
493	Encoding when is_list(Encoding) ->
494	    httpd_response:send_status(ModData, 501,
495				       "Unknown Transfer-Encoding",
496                                       {unknown_transfer_encoding, Encoding}),
497	    {stop, normal, State#state{response_sent = true}};
498	_ ->
499	    Length = list_to_integer(Headers#http_request_h.'content-length'),
500	    MaxChunk = max_client_body_chunk(ConfigDB),
501	    case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of
502		true ->
503		    case httpd_request:body_chunk_first(Body, Length, MaxChunk) of
504                        %% This is the case that the we need more data to complete
505                        %% the body but chunking to the mod_esi user is not enabled.
506                        {Module, add_chunk = Function,  Args} ->
507                            setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]),
508			    {noreply, State#state{mfa =
509						      {Module, Function, Args}}};
510                        %% Chunking to mod_esi user is enabled
511                        {ok, {continue, Module, Function, Args}} ->
512                            setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]),
513                            {noreply, State#state{mfa =
514						      {Module, Function, Args}}};
515                        {ok, {{continue, Chunk}, Module, Function, Args}} ->
516                            handle_internal_chunk(State#state{chunk =  chunk_start(MaxChunk),
517                                                              body = Chunk}, Module, Function, Args);
518                        %% Whole body delivered, if chunking mechanism is enabled the whole
519                        %% body fits in one chunk.
520                        {ok, NewBody} ->
521                            handle_response(State#state{chunk = chunk_finish(ChunkState,
522                                                                             CbState, MaxChunk),
523                                                        headers = Headers,
524                                                        body = NewBody})
525		    end;
526		false ->
527		    httpd_response:send_status(ModData, 413, "Body too long"),
528		    {stop, normal,  State#state{response_sent = true}}
529	    end
530    end.
531
532handle_expect(#state{headers = Headers, mod =
533		     #mod{config_db = ConfigDB} = ModData} = State,
534	      MaxBodySize) ->
535    Length = list_to_integer(Headers#http_request_h.'content-length'),
536    case expect(Headers, ModData#mod.http_version, ConfigDB) of
537	continue when (MaxBodySize > Length) orelse (MaxBodySize =:= nolimit) ->
538	    httpd_response:send_status(ModData, 100, ""),
539	    ok;
540	continue when MaxBodySize < Length ->
541	    httpd_response:send_status(ModData, 413, "Body too long"),
542	    {stop, normal, State#state{response_sent = true}};
543	{break, Value} ->
544	    httpd_response:send_status(ModData, 417,
545				       "Unexpected expect value",
546                                       {unexpected, Value}
547                                      ),
548	    {stop, normal,  State#state{response_sent = true}};
549	no_expect_header ->
550	    ok;
551	http_1_0_expect_header ->
552	    httpd_response:send_status(ModData, 400,
553				       "Only HTTP/1.1 Clients "
554				       "may use the Expect Header",
555                                       "Client with lower version than 1.1 tried to send"
556                                       "an expect header"),
557	    {stop, normal, State#state{response_sent = true}}
558    end.
559
560expect(Headers, "HTTP/1.1", _) ->
561    case Headers#http_request_h.expect of
562	"100-continue" ->
563	    continue;
564	undefined ->
565	    no_expect_header;
566	Other ->
567	    {break, Other}
568    end;
569expect(Headers, _, ConfigDB) ->
570    case Headers#http_request_h.expect of
571	undefined ->
572	    no_expect_header;
573	_ ->
574	    case httpd_util:lookup(ConfigDB, expect, continue) of
575		continue->
576		    no_expect_header;
577		_ ->
578		    http_1_0_expect_header
579	    end
580    end.
581
582handle_chunk(http_chunk = Module, decode_data = Function,
583             [ChunkSize, TotalChunk, {MaxBodySize, BodySoFar, _AccLength, MaxHeaderSize}],
584             #state{chunk = {_, CbState},
585                    mod = #mod{socket_type = SockType,
586                               socket = Socket} = ModData} = State) ->
587    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body =
588                                                                                {continue, BodySoFar, CbState}}),
589    setopts(Socket, SockType, [{active, once}]),
590    State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, [ChunkSize, TotalChunk, {MaxBodySize, <<>>, 0, MaxHeaderSize}]}};
591
592handle_chunk(http_chunk = Module, decode_size = Function,
593             [Data, HexList, _AccSize, {MaxBodySize, BodySoFar, _AccLength, MaxHeaderSize}],
594             #state{chunk = {_, CbState},
595                    mod = #mod{socket_type = SockType,
596                               socket = Socket} = ModData} = State) ->
597    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body = {continue, BodySoFar, CbState}}),
598    setopts(Socket, SockType, [{active, once}]),
599    State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, [Data, HexList, 0, {MaxBodySize, <<>>, 0, MaxHeaderSize}]}};
600handle_chunk(Module, Function, Args, #state{mod = #mod{socket_type = SockType,
601                                                                      socket = Socket}} = State) ->
602    setopts(Socket, SockType, [{active, once}]),
603    State#state{mfa = {Module, Function, Args}}.
604
605handle_internal_chunk(#state{chunk = {ChunkState, CbState}, body = Chunk,
606                             mod = #mod{socket_type = SockType,
607                                        socket = Socket} = ModData} = State, Module, Function, Args)->
608    Bodychunk = body_chunk(ChunkState, CbState, Chunk),
609    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body = Bodychunk}),
610    case Args of
611        [<<>> | _] ->
612            setopts(Socket, SockType, [{active, once}]),
613            {noreply, State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, Args}}};
614        _ ->
615            handle_info({dummy, Socket, <<>>}, State#state{chunk = {continue, NewCbState},
616                                                           mfa = {Module, Function, Args}})
617    end.
618
619handle_response(#state{body    = Body,
620                       headers = Headers,
621		       mod     = ModData,
622                       chunk   = {last, CbState},
623		       max_keep_alive_request = Max} = State) when Max > 0 ->
624    {NewBody, Data} = httpd_request:body_data(Headers, Body),
625    ok = httpd_response:generate_and_send_response(
626           ModData#mod{entity_body = {last, NewBody, CbState}}),
627    handle_next_request(State#state{response_sent = true}, Data);
628handle_response(#state{body    = Body,
629		       mod     = ModData,
630		       headers = Headers,
631		       max_keep_alive_request = Max} = State) when Max > 0 ->
632    {NewBody, Data} = httpd_request:body_data(Headers, Body),
633    %% Backwards compatible, may cause memory explosion
634    ok = httpd_response:generate_and_send_response(
635           ModData#mod{entity_body = binary_to_list(NewBody)}),
636    handle_next_request(State#state{response_sent = true}, Data);
637handle_response(#state{body    = Body,
638		       headers = Headers,
639		       mod     = ModData} = State) ->
640    {NewBody, _} = httpd_request:body_data(Headers, Body),
641    ok = httpd_response:generate_and_send_response(
642	   ModData#mod{entity_body = NewBody}),
643    {stop, normal, State#state{response_sent = true}}.
644
645handle_next_request(#state{mod = #mod{connection = true} = ModData,
646			   max_keep_alive_request = Max} = State, Data) ->
647
648    NewModData = #mod{socket_type = ModData#mod.socket_type,
649 		      socket      = ModData#mod.socket,
650 		      config_db   = ModData#mod.config_db,
651 		      init_data   = ModData#mod.init_data},
652    MaxHeaderSize = max_header_size(ModData#mod.config_db),
653    MaxURISize    = max_uri_size(ModData#mod.config_db),
654    MaxContentLen = max_content_length(ModData#mod.config_db),
655    Customize = customize(ModData#mod.config_db),
656    MaxChunk = max_client_body_chunk(ModData#mod.config_db),
657
658    MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
659				   {max_version, ?HTTP_MAX_VERSION_STRING},
660				   {max_method, ?HTTP_MAX_METHOD_STRING},
661				   {max_content_length, MaxContentLen},
662				   {customize, Customize}
663				  ]]},
664    TmpState = State#state{mod                    = NewModData,
665			   mfa                    = MFA,
666			   max_keep_alive_request = decrease(Max),
667			   headers                = undefined,
668			   body                   = undefined,
669                           chunk                  = chunk_start(MaxChunk),
670			   response_sent          = false},
671
672    NewState = activate_request_timeout(TmpState),
673
674    case Data of
675	<<>> ->
676        setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]),
677	    {noreply, NewState};
678	_ ->
679	    handle_info({dummy, ModData#mod.socket, Data}, NewState)
680    end;
681
682handle_next_request(State, _) ->
683    {stop, normal, State}.
684
685activate_request_timeout(#state{timeout = Time} = State) ->
686    Ref = erlang:send_after(Time, self(), timeout),
687    State#state{timer = Ref}.
688data_receive_counter(State, Byte_limit) ->
689    case Byte_limit of
690	false ->
691	    State#state{data = 0};
692	Nr ->
693	    erlang:send_after(3000, self(), check_data_first),
694	    State#state{data = 0, byte_limit = Nr}
695    end.
696cancel_request_timeout(#state{timer = undefined} = State) ->
697    State;
698cancel_request_timeout(#state{timer = Timer} = State) ->
699    erlang:cancel_timer(Timer),
700    receive
701	timeout ->
702	    ok
703    after 0 ->
704	    ok
705    end,
706    State#state{timer = undefined}.
707
708decrease(N) when is_integer(N) ->
709    N-1;
710decrease(N) ->
711    N.
712
713%--------------------------------------------------------------------
714%% Config access wrapper functions
715%%--------------------------------------------------------------------
716
717max_header_size(ConfigDB) ->
718    httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE).
719
720max_client_body_chunk(ConfigDB) ->
721    httpd_util:lookup(ConfigDB, max_client_body_chunk, nolimit).
722
723max_uri_size(ConfigDB) ->
724    httpd_util:lookup(ConfigDB, max_uri_size, ?HTTP_MAX_URI_SIZE).
725
726max_body_size(ConfigDB) ->
727    httpd_util:lookup(ConfigDB, max_body_size, nolimit).
728
729max_keep_alive_request(ConfigDB) ->
730    httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity).
731
732max_content_length(ConfigDB) ->
733    httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
734
735customize(ConfigDB) ->
736    httpd_util:lookup(ConfigDB, customize, httpd_custom).
737
738chunk_start(nolimit) ->
739    {undefined, undefined};
740chunk_start(_) ->
741    {first, undefined}.
742chunk_finish(_, _, nolimit) ->
743    {undefined, undefined};
744chunk_finish(_, CbState, _) ->
745    {last, CbState}.
746
747body_chunk(first, _, Chunk) ->
748    {first, Chunk};
749body_chunk(ChunkState, CbState, Chunk) ->
750    {ChunkState, Chunk, CbState}.
751
752setopts(Socket, SocketType, Options) ->
753    case http_transport:setopts(SocketType, Socket, Options) of
754        ok ->
755            ok;
756        {error, _} -> %% inet can return einval instead of closed
757            self() ! {http_transport:close_tag(SocketType), Socket}
758    end.
759