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
165    http_transport:setopts(SocketType, Socket,
166			   [binary, {packet, 0}, {active, once}]),
167    NewState =  data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)),
168     gen_server:enter_loop(?MODULE, [], NewState).
169
170
171%%====================================================================
172%% gen_server callbacks
173%%====================================================================
174
175%%--------------------------------------------------------------------
176%% handle_call(Request, From, State) -> {reply, Reply, State} |
177%%                                      {reply, Reply, State, Timeout} |
178%%                                      {noreply, State} |
179%%                                      {noreply, State, Timeout} |
180%%                                      {stop, Reason, Reply, State} |
181%%                                      {stop, Reason, State}
182%% Description: Handling call messages
183%%--------------------------------------------------------------------
184handle_call(Request, From, #state{mod = #mod{config_db = Db} = ModData} = State) ->
185    httpd_util:error_log(Db,
186                         httpd_logger:error_report(internal,
187                                                   [{unexpected_call, Request}, {to, self()}, {from, From}], ModData,
188                                                   ?LOCATION)),
189    {stop, {call_api_violation, Request, From}, State}.
190
191%%--------------------------------------------------------------------
192%% handle_cast(Msg, State) -> {noreply, State} |
193%%                                      {noreply, State, Timeout} |
194%%                                      {stop, Reason, State}
195%% Description: Handling cast messages
196%%--------------------------------------------------------------------
197handle_cast(Msg, #state{mod = #mod{config_db = Db} = ModData} = State) ->
198    httpd_util:error_log(Db,
199                         httpd_logger:error_report(internal, [{unexpected_cast, Msg}, {to, self()}], ModData,
200                                                   ?LOCATION)),
201    {noreply, State}.
202
203%%--------------------------------------------------------------------
204%% handle_info(Info, State) -> {noreply, State} |
205%%                                       {noreply, State, Timeout} |
206%%                                       {stop, Reason, State}
207%% Description: Handling all non call/cast messages
208%%--------------------------------------------------------------------
209handle_info({Proto, Socket, Data},
210	    #state{mfa = {Module, Function, Args},
211                   chunk = {ChunkState, _},
212		   mod = #mod{socket_type = SockType,
213			      socket = Socket} = ModData} = State)
214  when (((Proto =:= tcp) orelse
215	 (Proto =:= ssl) orelse
216	 (Proto =:= dummy)) andalso is_binary(Data)) ->
217
218    PROCESSED = (catch Module:Function([Data | Args])),
219    NewDataSize = case State#state.byte_limit of
220		      undefined ->
221			  undefined;
222		      _ ->
223			  State#state.data + byte_size(Data)
224		  end,
225
226    case PROCESSED of
227        {ok, Result} ->
228	    NewState = case NewDataSize of
229			   undefined ->
230			       cancel_request_timeout(State);
231			   _ ->
232			       set_new_data_size(cancel_request_timeout(State), NewDataSize)
233		       end,
234            handle_msg(Result, NewState);
235	{error, {size_error, MaxSize, ErrCode, ErrStr}, Version} ->
236	    NewModData =  ModData#mod{http_version = Version},
237	    httpd_response:send_status(NewModData, ErrCode, ErrStr, {max_size, MaxSize}),
238	    {stop, normal, State#state{response_sent = true,
239				       mod = NewModData}};
240
241        {http_chunk = Module, Function, Args} when ChunkState =/= undefined ->
242            NewState = handle_chunk(Module, Function, Args, State),
243            {noreply, NewState};
244	NewMFA ->
245	    http_transport:setopts(SockType, Socket, [{active, once}]),
246	    case NewDataSize of
247		undefined ->
248		    {noreply, State#state{mfa = NewMFA}};
249		_ ->
250		    {noreply, State#state{mfa = NewMFA, data = NewDataSize}}
251	    end
252    end;
253
254%% Error cases
255handle_info({tcp_closed, _}, State) ->
256    {stop, normal, State};
257handle_info({ssl_closed, _}, State) ->
258    {stop, normal, State};
259handle_info({tcp_error, _, _} = Reason, State) ->
260    {stop, {shutdown, Reason}, State};
261handle_info({ssl_error, _, _} = Reason, State) ->
262    {stop, {shutdown, Reason}, State};
263
264%% Timeouts
265handle_info(timeout, #state{mfa = {_, parse, _}} = State) ->
266    %% No request received on keep-alive connection
267    %% before server side timeout. No response should be sent!
268    {stop, normal, State#state{response_sent = true}};
269handle_info(timeout, #state{mod = ModData} = State) ->
270    httpd_response:send_status(ModData, 408, "Request timeout", "The client did not send the whole request before the "
271                               "server side timeout"),
272    {stop, normal, State#state{response_sent = true}};
273handle_info(check_data_first, #state{data = Data, byte_limit = Byte_Limit} = State) ->
274    case Data >= (Byte_Limit*3) of
275	true ->
276	    erlang:send_after(1000, self(), check_data),
277	    {noreply, State#state{data = 0}};
278	_ ->
279	    {stop, normal, State#state{response_sent = true}}
280    end;
281handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) ->
282    case Data >= Byte_Limit of
283	true ->
284	    erlang:send_after(1000, self(), check_data),
285	    {noreply, State#state{data = 0}};
286	_ ->
287	    {stop, normal, State#state{response_sent = true}}
288    end;
289
290handle_info({'EXIT', _, Reason}, State) ->
291    {stop, Reason, State};
292
293%% Default case
294handle_info(Info, #state{mod = #mod{config_db = Db} =ModData} = State) ->
295    httpd_util:error_log(Db,
296                         httpd_logger:error_report(internal,
297                                                   [{unexpected_info, Info}, {to, self()}], ModData,
298                                                   ?LOCATION)),
299    {noreply, State}.
300
301
302%%--------------------------------------------------------------------
303%% terminate(Reason, State) -> void()
304%%
305%% Description: This function is called by a gen_server when it is about to
306%% terminate. It should be the opposite of Module:init/1 and do any necessary
307%% cleaning up. When it returns, the gen_server terminates with Reason.
308%% The return value is ignored.
309%%--------------------------------------------------------------------
310terminate(Reason, State) when Reason == normal;
311			      Reason == shutdown ->
312    do_terminate(State);
313terminate({shutdown,_}, State) ->
314    do_terminate(State);
315terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
316    httpd_response:send_status(ModData, 500, none),
317    terminate(Reason, State#state{response_sent = true, mod = ModData});
318terminate(_Reason, State) ->
319    do_terminate(State).
320
321do_terminate(#state{mod = ModData} = State) ->
322    cancel_request_timeout(State),
323    httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket).
324
325format_status(normal, [_, State]) ->
326    [{data, [{"StateData", State}]}];
327format_status(terminate, [_, State]) ->
328    Mod = (State#state.mod),
329    case Mod#mod.socket_type of
330	ip_comm ->
331	    [{data, [{"StateData", State}]}];
332	{essl, _} ->
333	    %% Do not print ssl options in superviosr reports
334	    [{data, [{"StateData",
335		      State#state{mod = Mod#mod{socket_type = 'TLS'}}}]}]
336    end.
337
338%%--------------------------------------------------------------------
339%% code_change(OldVsn, State, Extra) -> {ok, NewState}
340%%
341%% Description: Convert process state when code is changed
342%%--------------------------------------------------------------------
343code_change(_OldVsn, State, _Extra) ->
344    {ok, State}.
345
346
347%%--------------------------------------------------------------------
348%%% Internal functions
349%%--------------------------------------------------------------------
350set_new_data_size(State, NewData) ->
351    State#state{data = NewData}.
352await_socket_ownership_transfer(AcceptTimeout) ->
353    receive
354	{socket_ownership_transfered, SocketType, Socket} ->
355	    {SocketType, Socket}
356    after AcceptTimeout ->
357	    exit(accept_socket_timeout)
358    end.
359
360
361%%% Internal chunking of client body
362handle_msg({{continue, Chunk}, Module, Function, Args}, #state{chunk = {_, CbState}} = State) ->
363    handle_internal_chunk(State#state{chunk = {continue, CbState},
364                                      body = Chunk}, Module, Function, Args);
365handle_msg({continue, Module, Function, Args}, 	#state{mod = ModData} = State) ->
366    http_transport:setopts(ModData#mod.socket_type,
367                           ModData#mod.socket,
368                           [{active, once}]),
369    {noreply, State#state{mfa = {Module, Function, Args}}};
370handle_msg({last, Body}, #state{headers = Headers, chunk = {_, CbState}} = State) ->
371    NewHeaders = Headers#http_request_h{'content-length' = integer_to_list(size(Body))},
372    handle_response(State#state{chunk = {last, CbState},
373                                headers = NewHeaders,
374                                body = Body});
375%%% Last data chunked by client
376handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {ChunkState, CbState}} = State) when ChunkState =/= undefined ->
377    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
378    handle_response(State#state{chunk = {last, CbState},
379                                headers = NewHeaders,
380                                body = Body});
381handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {undefined, _}} = State) ->
382    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
383    handle_response(State#state{headers = NewHeaders,
384                                body = Body});
385handle_msg(Result, State) ->
386    handle_http_msg(Result, State).
387
388handle_http_msg({_, _, Version, {_, _}, _},
389		#state{status = busy, mod = ModData} = State) ->
390    handle_manager_busy(State#state{mod =
391				    ModData#mod{http_version = Version}}),
392    {stop, normal, State};
393
394handle_http_msg({_, _, Version, {_, _}, _},
395		#state{status = blocked, mod = ModData} = State) ->
396    handle_manager_blocked(State#state{mod =
397				       ModData#mod{http_version = Version}}),
398    {stop, normal, State};
399
400handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
401		#state{status = accept, mod = ModData} = State) ->
402    case httpd_request:validate(Method, Uri, Version) of
403	ok  ->
404	    {ok, NewModData} =
405		httpd_request:update_mod_data(ModData, Method, Uri,
406					      Version, Headers),
407
408	    case is_host_specified_if_required(NewModData#mod.absolute_uri,
409					       RecordHeaders, Version) of
410		true ->
411		    handle_body(State#state{headers = RecordHeaders,
412					    body = Body,
413					    mod = NewModData});
414		false ->
415		    httpd_response:send_status(ModData#mod{http_version =
416							   Version},
417					       400, none),
418		    {stop, normal, State#state{response_sent = true}}
419	    end;
420	{error, {not_supported, What}} ->
421	    httpd_response:send_status(ModData#mod{http_version = Version},
422				       501, {Method, Uri, Version}, {not_sup, What}),
423	    {stop, normal, State#state{response_sent = true}};
424	{error, {bad_request, {forbidden, URI}}} ->
425	    httpd_response:send_status(ModData#mod{http_version = Version},
426				       403, URI),
427	    {stop, normal, State#state{response_sent = true}};
428	{error, {bad_request, {malformed_syntax, URI}}} ->
429	    httpd_response:send_status(ModData#mod{http_version = Version},
430				       400, URI, {malformed_syntax, URI}),
431	    {stop, normal, State#state{response_sent = true}};
432	{error, {bad_version, Ver}} ->
433	    httpd_response:send_status(ModData#mod{http_version = "HTTP/0.9"}, 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		    http_transport:setopts(ModData#mod.socket_type,
479					   ModData#mod.socket,
480					   [{active, once}]),
481		    {noreply, State#state{mfa =
482                                              {Module, Function, Args},
483                                          chunk = chunk_start(MaxChunk)}};
484                {ok, {ChunkedHeaders, NewBody}} ->
485		    NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
486                    handle_response(State#state{headers = NewHeaders,
487                                                body = NewBody,
488                                                chunk = chunk_finish(ChunkState, CbState, MaxChunk)})
489	    catch
490		throw:Error ->
491		    httpd_response:send_status(ModData, 400,
492					       "Bad input", {chunk_decoding, bad_input, Error}),
493		    {stop, normal, State#state{response_sent = true}}
494	    end;
495	Encoding when is_list(Encoding) ->
496	    httpd_response:send_status(ModData, 501,
497				       "Unknown Transfer-Encoding",
498                                       {unknown_transfer_encoding, Encoding}),
499	    {stop, normal, State#state{response_sent = true}};
500	_ ->
501	    Length = list_to_integer(Headers#http_request_h.'content-length'),
502	    MaxChunk = max_client_body_chunk(ConfigDB),
503	    case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of
504		true ->
505		    case httpd_request:body_chunk_first(Body, Length, MaxChunk) of
506                        %% This is the case that the we need more data to complete
507                        %% the body but chunking to the mod_esi user is not enabled.
508                        {Module, add_chunk = Function,  Args} ->
509                            http_transport:setopts(ModData#mod.socket_type,
510						   ModData#mod.socket,
511						   [{active, once}]),
512			    {noreply, State#state{mfa =
513						      {Module, Function, Args}}};
514                        %% Chunking to mod_esi user is enabled
515                        {ok, {continue, Module, Function, Args}} ->
516                                http_transport:setopts(ModData#mod.socket_type,
517						   ModData#mod.socket,
518						   [{active, once}]),
519			    {noreply, State#state{mfa =
520						      {Module, Function, Args}}};
521                        {ok, {{continue, Chunk}, Module, Function, Args}} ->
522                            handle_internal_chunk(State#state{chunk =  chunk_start(MaxChunk),
523                                                              body = Chunk}, Module, Function, Args);
524                        %% Whole body delivered, if chunking mechanism is enabled the whole
525                        %% body fits in one chunk.
526                        {ok, NewBody} ->
527                            handle_response(State#state{chunk = chunk_finish(ChunkState,
528                                                                             CbState, MaxChunk),
529                                                        headers = Headers,
530                                                        body = NewBody})
531		    end;
532		false ->
533		    httpd_response:send_status(ModData, 413, "Body too long"),
534		    {stop, normal,  State#state{response_sent = true}}
535	    end
536    end.
537
538handle_expect(#state{headers = Headers, mod =
539		     #mod{config_db = ConfigDB} = ModData} = State,
540	      MaxBodySize) ->
541    Length = list_to_integer(Headers#http_request_h.'content-length'),
542    case expect(Headers, ModData#mod.http_version, ConfigDB) of
543	continue when (MaxBodySize > Length) orelse (MaxBodySize =:= nolimit) ->
544	    httpd_response:send_status(ModData, 100, ""),
545	    ok;
546	continue when MaxBodySize < Length ->
547	    httpd_response:send_status(ModData, 413, "Body too long"),
548	    {stop, normal, State#state{response_sent = true}};
549	{break, Value} ->
550	    httpd_response:send_status(ModData, 417,
551				       "Unexpected expect value",
552                                       {unexpected, Value}
553                                      ),
554	    {stop, normal,  State#state{response_sent = true}};
555	no_expect_header ->
556	    ok;
557	http_1_0_expect_header ->
558	    httpd_response:send_status(ModData, 400,
559				       "Only HTTP/1.1 Clients "
560				       "may use the Expect Header",
561                                       "Client with lower version than 1.1 tried to send"
562                                       "an expect header"),
563	    {stop, normal, State#state{response_sent = true}}
564    end.
565
566expect(Headers, "HTTP/1.1", _) ->
567    case Headers#http_request_h.expect of
568	"100-continue" ->
569	    continue;
570	undefined ->
571	    no_expect_header;
572	Other ->
573	    {break, Other}
574    end;
575expect(Headers, _, ConfigDB) ->
576    case Headers#http_request_h.expect of
577	undefined ->
578	    no_expect_header;
579	_ ->
580	    case httpd_util:lookup(ConfigDB, expect, continue) of
581		continue->
582		    no_expect_header;
583		_ ->
584		    http_1_0_expect_header
585	    end
586    end.
587
588handle_chunk(http_chunk = Module, decode_data = Function,
589             [ChunkSize, TotalChunk, {MaxBodySize, BodySoFar, _AccLength, MaxHeaderSize}],
590             #state{chunk = {_, CbState},
591                    mod = #mod{socket_type = SockType,
592                               socket = Socket} = ModData} = State) ->
593    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body =
594                                                                                {continue, BodySoFar, CbState}}),
595    http_transport:setopts(SockType, Socket, [{active, once}]),
596    State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, [ChunkSize, TotalChunk, {MaxBodySize, <<>>, 0, MaxHeaderSize}]}};
597
598handle_chunk(http_chunk = Module, decode_size = Function,
599             [Data, HexList, _AccSize, {MaxBodySize, BodySoFar, _AccLength, MaxHeaderSize}],
600             #state{chunk = {_, CbState},
601                    mod = #mod{socket_type = SockType,
602                               socket = Socket} = ModData} = State) ->
603    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body = {continue, BodySoFar, CbState}}),
604    http_transport:setopts(SockType, Socket, [{active, once}]),
605    State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, [Data, HexList, 0, {MaxBodySize, <<>>, 0, MaxHeaderSize}]}};
606handle_chunk(Module, Function, Args, #state{mod = #mod{socket_type = SockType,
607                                                                      socket = Socket}} = State) ->
608    http_transport:setopts(SockType, Socket, [{active, once}]),
609    State#state{mfa = {Module, Function, Args}}.
610
611handle_internal_chunk(#state{chunk = {ChunkState, CbState}, body = Chunk,
612                             mod = #mod{socket_type = SockType,
613                                        socket = Socket} = ModData} = State, Module, Function, Args)->
614    Bodychunk = body_chunk(ChunkState, CbState, Chunk),
615    {continue, NewCbState} = httpd_response:handle_continuation(ModData#mod{entity_body = Bodychunk}),
616    case Args of
617        [<<>> | _] ->
618            http_transport:setopts(SockType, Socket, [{active, once}]),
619            {noreply, State#state{chunk = {continue, NewCbState}, mfa = {Module, Function, Args}}};
620        _ ->
621            handle_info({dummy, Socket, <<>>}, State#state{chunk = {continue, NewCbState},
622                                                           mfa = {Module, Function, Args}})
623    end.
624
625handle_response(#state{body    = Body,
626                       headers = Headers,
627		       mod     = ModData,
628                       chunk   = {last, CbState},
629		       max_keep_alive_request = Max} = State) when Max > 0 ->
630    {NewBody, Data} = httpd_request:body_data(Headers, Body),
631    ok = httpd_response:generate_and_send_response(
632           ModData#mod{entity_body = {last, NewBody, CbState}}),
633    handle_next_request(State#state{response_sent = true}, Data);
634handle_response(#state{body    = Body,
635		       mod     = ModData,
636		       headers = Headers,
637		       max_keep_alive_request = Max} = State) when Max > 0 ->
638    {NewBody, Data} = httpd_request:body_data(Headers, Body),
639    %% Backwards compatible, may cause memory explosion
640    ok = httpd_response:generate_and_send_response(
641           ModData#mod{entity_body = binary_to_list(NewBody)}),
642    handle_next_request(State#state{response_sent = true}, Data);
643handle_response(#state{body    = Body,
644		       headers = Headers,
645		       mod     = ModData} = State) ->
646    {NewBody, _} = httpd_request:body_data(Headers, Body),
647    ok = httpd_response:generate_and_send_response(
648	   ModData#mod{entity_body = NewBody}),
649    {stop, normal, State#state{response_sent = true}}.
650
651handle_next_request(#state{mod = #mod{connection = true} = ModData,
652			   max_keep_alive_request = Max} = State, Data) ->
653
654    NewModData = #mod{socket_type = ModData#mod.socket_type,
655 		      socket      = ModData#mod.socket,
656 		      config_db   = ModData#mod.config_db,
657 		      init_data   = ModData#mod.init_data},
658    MaxHeaderSize = max_header_size(ModData#mod.config_db),
659    MaxURISize    = max_uri_size(ModData#mod.config_db),
660    MaxContentLen = max_content_length(ModData#mod.config_db),
661    Customize = customize(ModData#mod.config_db),
662    MaxChunk = max_client_body_chunk(ModData#mod.config_db),
663
664    MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
665				   {max_version, ?HTTP_MAX_VERSION_STRING},
666				   {max_method, ?HTTP_MAX_METHOD_STRING},
667				   {max_content_length, MaxContentLen},
668				   {customize, Customize}
669				  ]]},
670    TmpState = State#state{mod                    = NewModData,
671			   mfa                    = MFA,
672			   max_keep_alive_request = decrease(Max),
673			   headers                = undefined,
674			   body                   = undefined,
675                           chunk                  = chunk_start(MaxChunk),
676			   response_sent          = false},
677
678    NewState = activate_request_timeout(TmpState),
679
680    case Data of
681	<<>> ->
682	    http_transport:setopts(ModData#mod.socket_type,
683				   ModData#mod.socket, [{active, once}]),
684	    {noreply, NewState};
685	_ ->
686	    handle_info({dummy, ModData#mod.socket, Data}, NewState)
687    end;
688
689handle_next_request(State, _) ->
690    {stop, normal, State}.
691
692activate_request_timeout(#state{timeout = Time} = State) ->
693    Ref = erlang:send_after(Time, self(), timeout),
694    State#state{timer = Ref}.
695data_receive_counter(State, Byte_limit) ->
696    case Byte_limit of
697	false ->
698	    State#state{data = 0};
699	Nr ->
700	    erlang:send_after(3000, self(), check_data_first),
701	    State#state{data = 0, byte_limit = Nr}
702    end.
703cancel_request_timeout(#state{timer = undefined} = State) ->
704    State;
705cancel_request_timeout(#state{timer = Timer} = State) ->
706    erlang:cancel_timer(Timer),
707    receive
708	timeout ->
709	    ok
710    after 0 ->
711	    ok
712    end,
713    State#state{timer = undefined}.
714
715decrease(N) when is_integer(N) ->
716    N-1;
717decrease(N) ->
718    N.
719
720%--------------------------------------------------------------------
721%% Config access wrapper functions
722%%--------------------------------------------------------------------
723
724max_header_size(ConfigDB) ->
725    httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE).
726
727max_client_body_chunk(ConfigDB) ->
728    httpd_util:lookup(ConfigDB, max_client_body_chunk, nolimit).
729
730max_uri_size(ConfigDB) ->
731    httpd_util:lookup(ConfigDB, max_uri_size, ?HTTP_MAX_URI_SIZE).
732
733max_body_size(ConfigDB) ->
734    httpd_util:lookup(ConfigDB, max_body_size, nolimit).
735
736max_keep_alive_request(ConfigDB) ->
737    httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity).
738
739max_content_length(ConfigDB) ->
740    httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
741
742customize(ConfigDB) ->
743    httpd_util:lookup(ConfigDB, customize, httpd_custom).
744
745chunk_start(nolimit) ->
746    {undefined, undefined};
747chunk_start(_) ->
748    {first, undefined}.
749chunk_finish(_, _, nolimit) ->
750    {undefined, undefined};
751chunk_finish(_, CbState, _) ->
752    {last, CbState}.
753
754body_chunk(first, _, Chunk) ->
755    {first, Chunk};
756body_chunk(ChunkState, CbState, Chunk) ->
757    {ChunkState, Chunk, CbState}.
758