aboutsummaryrefslogblamecommitdiffstats
path: root/lib/dialyzer/test/r9c_SUITE_data/src/inets/httpd_request_handler.erl
blob: ebda5c6562508854c7c5dbe86a04fd9f06199e9e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                                                                    
  






                                                                           
  


                                                                         
  
                                                                           





                                       
                                                             

















                                                                   
            








































                                                                           
                













                                                                              
                                  












                                                                               
                                                  







                                                                       

                                                                            

                                            
                                                                        





                                                                         
                    


                                                                         
                              
                                                                      
                                   





                                                                          
                                                            










                                                                               
 




                                                                     
                                                                   























                                                                        
                                                 








                                                          
                                                            

                         
                                       












                                                                             


                                                                          
  
                                                            



                                                                     
 



                                                          
                                                      
                                                              
                                                                            




                                                       
 


                                                                
                          
                              
                          
                                              
                                                                           
                                            


                                                                   







                                                                           

                          
                              
                          
                                              
                                                                            
                                            


                                                                   




                                                                             
                                                                           

                                                   
 





                                                                
                                                                  




























                                                                               
                                                                     








                                                                            
                        


































                                                                               
                                                                   








                                                                           
                                                                       












                                                                        
                                                 


















                                                                           
                                                                       

                                                        
                                                 



                                                                          

            


                                                                        
                                                                     




                                                  
 












                                                                               
                                                                      


                                      
 





                                                                         
                                                                     























                                                                          

                                                                    
                     
                               









                                                             
                                    





                                                                        
                                                                           














                                                                         
                                                                











                                                                               
                                                                   






                                                                              
                                                                             




















                                                                       
                                                         






                                                                        
                                                              




                                                                        
                                                                     




                                                        
                                                                     


                                             
                                                                      






                                                                        
                                                                 
                                 
                                        

                                                                    
                                                            







                                                                         
 

                                                                        
                                                                            

                                                                        
                                                                 


















































                                                                                                                        

                                                                         








                                                                        
                                                                        







                                                                          
                                                             



                                                                              
                                                                        



















                                                                              
                                                                















                                                                       
                                          






                                                                                      
                                                                        






                                                                            
                                                     












                                                                           
                                    

















                                                                        

                                               





                                                         
                         


































































                                                                                    

 







                                                                        
                                                                

                                                          


                                                                         
                                                                        
                                           



                                                                
                             





















                                                                        
                                                   






































                                                                              
 
                                                                     
                                                                      












                                                                          
 














































                                                                     
%% ``Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%%
%%     $Id: httpd_request_handler.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
-module(httpd_request_handler).

%% app internal api
-export([start_link/2, synchronize/3]).

%% module internal api
-export([connection/2, do_next_connection/6, read_header/7]).
-export([parse_trailers/1, newline/1]).

-include("httpd.hrl").
-include("httpd_verbosity.hrl").


%% start_link

start_link(Manager, ConfigDB) ->
    Pid = proc_lib:spawn(?MODULE, connection, [Manager, ConfigDB]),
    {ok, Pid}.


%% synchronize

synchronize(Pid, SocketType, Socket) ->
    Pid ! {synchronize, SocketType, Socket}.

% connection

connection(Manager, ConfigDB) ->
    {SocketType, Socket, {Status, Verbosity}} = await_synchronize(Manager),
    put(sname,self()),
    put(verbosity,?vvalidate(Verbosity)),
    connection1(Status, Manager, ConfigDB, SocketType, Socket).


connection1({reject, busy}, Manager, ConfigDB, SocketType, Socket) ->
    handle_busy(Manager, ConfigDB, SocketType, Socket);

connection1({reject, blocked}, Manager, ConfigDB, SocketType, Socket) ->
    handle_blocked(Manager, ConfigDB, SocketType, Socket);

connection1(accept, Manager, ConfigDB, SocketType, Socket) ->
    handle_connection(Manager, ConfigDB, SocketType, Socket).


%% await_synchronize

await_synchronize(Manager) ->
    receive
	{synchronize, SocketType, Socket} ->
	    ?vlog("received syncronize: "
		  "~n   SocketType: ~p"
		  "~n   Socket:     ~p", [SocketType, Socket]),
	    {SocketType, Socket, httpd_manager:new_connection(Manager)}
    after 5000 ->
	    exit(synchronize_timeout)
    end.


% handle_busy

handle_busy(Manager, ConfigDB, SocketType, Socket) ->
    ?vlog("handle busy: ~p", [Socket]),
    MaxClients = httpd_util:lookup(ConfigDB, max_clients, 150),
    String = io_lib:format("heavy load (>~w processes)", [MaxClients]),
    reject_connection(Manager, ConfigDB, SocketType, Socket, String).


% handle_blocked

handle_blocked(Manager, ConfigDB, SocketType, Socket) ->
    ?vlog("handle blocked: ~p", [Socket]),
    String = "Server maintenance performed, try again later",
    reject_connection(Manager, ConfigDB, SocketType, Socket, String).


% reject_connection

reject_connection(Manager, ConfigDB, SocketType, Socket, Info) ->
    String = lists:flatten(Info),
    ?vtrace("send status (503) message", []),
    httpd_response:send_status(SocketType, Socket, 503, String, ConfigDB),
    %% This ugly thing is to make ssl deliver the message, before the close...
    close_sleep(SocketType, 1000),
    ?vtrace("close the socket", []),
    close(SocketType, Socket, ConfigDB).


% handle_connection

handle_connection(Manager, ConfigDB, SocketType, Socket) ->
    ?vlog("handle connection: ~p", [Socket]),
    Resolve     = httpd_socket:resolve(SocketType),
    Peername    = httpd_socket:peername(SocketType, Socket),
    InitData    = #init_data{peername=Peername, resolve=Resolve},
    TimeOut     = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
    NrOfRequest = httpd_util:lookup(ConfigDB, max_keep_alive_request, forever),
    ?MODULE:do_next_connection(ConfigDB, InitData,
			       SocketType, Socket,NrOfRequest,TimeOut),
    ?vlog("handle connection: done", []),
    httpd_manager:done_connection(Manager),
    ?vlog("handle connection: close socket", []),
    close(SocketType, Socket, ConfigDB).


% do_next_connection
do_next_connection(_ConfigDB, _InitData, _SocketType, _Socket, NrOfRequests,
		   _Timeout) when NrOfRequests < 1 ->
    ?vtrace("do_next_connection: done", []),
    ok;
do_next_connection(ConfigDB, InitData, SocketType, Socket, NrOfRequests,
		   Timeout) ->
    Peername = InitData#init_data.peername,
    case (catch read(ConfigDB, SocketType, Socket, InitData, Timeout)) of
        {'EXIT', Reason} ->
            ?vlog("exit reading from socket: ~p",[Reason]),
            error_logger:error_report({'EXIT',Reason}),
	    String =
		lists:flatten(
		  io_lib:format("exit reading from socket: ~p => ~n~p~n",
				[Socket, Reason])),
	    error_log(mod_log,
		      SocketType, Socket, ConfigDB, Peername, String),
	    error_log(mod_disk_log,
		      SocketType, Socket, ConfigDB, Peername, String);
        {error, Reason} ->
            handle_read_error(Reason,SocketType,Socket,ConfigDB,Peername);
        Info when record(Info, mod) ->
            case Info#mod.connection of
                true ->
                    ReqTimeout = httpd_util:lookup(ConfigDB,
						   keep_alive_timeout, 150000),
		    ?MODULE:do_next_connection(ConfigDB,          InitData,
					       SocketType,        Socket,
					       dec(NrOfRequests), ReqTimeout);
		_ ->
                    ok
            end;
        _ ->
            ok
    end.



%% read
read(ConfigDB, SocketType, Socket, InitData, Timeout) ->
    ?vdebug("read from socket ~p with Timeout ~p",[Socket, Timeout]),
    MaxHdrSz = httpd_util:lookup(ConfigDB, max_header_size, 10240),
    case ?MODULE:read_header(SocketType, Socket, Timeout, MaxHdrSz,
			     ConfigDB, InitData, []) of
	{socket_closed, Reason} ->
	    ?vlog("Socket closed while reading request header: "
		  "~n   ~p", [Reason]),
	    socket_close;
	{error, Error} ->
	    {error, Error};
	{ok, Info, EntityBodyPart} ->
	    read1(SocketType, Socket, ConfigDB, InitData, Timeout, Info,
		  EntityBodyPart)
    end.

%% Got the head and maybe a part of the body: read in the rest
read1(SocketType, Socket, ConfigDB, InitData, Timeout, Info, BodyPart)->
    MaxBodySz     = httpd_util:lookup(ConfigDB, max_body_size, nolimit),
    ContentLength = content_length(Info),
    ?vtrace("ContentLength: ~p", [ContentLength]),
    case read_entity_body(SocketType, Socket, Timeout, MaxBodySz,
			  ContentLength, BodyPart, Info, ConfigDB) of
	{socket_closed, Reason} ->
	    ?vlog("Socket closed while reading request body: "
		  "~n   ~p", [Reason]),
	    socket_close;
	{ok, EntityBody} ->
	    finish_request(EntityBody, [], Info);
	{ok, ExtraHeader, EntityBody} ->
	    finish_request(EntityBody, ExtraHeader, Info);
	Response ->
	    httpd_socket:close(SocketType, Socket),
	    socket_closed
	    %% Catch up all bad return values
    end.


%% The request is read in send it forward to the module that
%% generates the response

finish_request(EntityBody, ExtraHeader,
	       #mod{parsed_header = ParsedHeader} = Info)->
    ?DEBUG("finish_request -> ~n"
	"    EntityBody:   ~p~n"
	"    ExtraHeader:  ~p~n"
	"    ParsedHeader: ~p~n",
	[EntityBody, ExtraHeader, ParsedHeader]),
    httpd_response:send(Info#mod{parsed_header = ParsedHeader ++ ExtraHeader,
				 entity_body   = EntityBody}).


%% read_header

%% This algorithm rely on the buffer size of the inet driver together
%% with the {active, once} socket option. Atmost one message of this
%% size will be received at a given time. When a full header has been
%% read, the body is read with the recv function (the body size is known).
%%
read_header(SocketType, Socket, Timeout, MaxHdrSz, ConfigDB,
	    InitData, SoFar0) ->
    T = t(),
    %% remove any newlines at the begining, they might be crap from ?
    SoFar = remove_newline(SoFar0),

    case terminated_header(MaxHdrSz, SoFar) of
	{true, Header, EntityBodyPart} ->
	    ?vdebug("read_header -> done reading header: "
		    "~n   length(Header):         ~p"
		    "~n   length(EntityBodyPart): ~p",
		    [length(Header), length(EntityBodyPart)]),
	    transform_header(SocketType, Socket, Header, ConfigDB, InitData,
			     EntityBodyPart);
	false ->
	    ?vtrace("read_header -> "
		    "~n   set active = 'once' and "
		    "await a chunk of the header", []),

	    case httpd_socket:active_once(SocketType, Socket) of
		ok ->
		    receive
			%%
			%% TCP
			%%
			{tcp, Socket, Data} ->
			    ?vtrace("read_header(ip) -> got some data: ~p",
				[sz(Data)]),
			    ?MODULE:read_header(SocketType, Socket,
						Timeout - (t()-T),
						MaxHdrSz, ConfigDB,
						InitData, SoFar ++ Data);
			{tcp_closed, Socket} ->
			    ?vtrace("read_header(ip) -> socket closed",[]),
			    {socket_closed,normal};
			{tcp_error, Socket, Reason} ->
			    ?vtrace("read_header(ip) -> socket error: ~p",
				[Reason]),
			    {socket_closed, Reason};

			%%
			%% SSL
			%%
			{ssl, Socket, Data} ->
			    ?vtrace("read_header(ssl) -> got some data: ~p",
				[sz(Data)]),
			    ?MODULE:read_header(SocketType, Socket,
						Timeout - (t()-T),
						MaxHdrSz, ConfigDB,
						InitData, SoFar ++ Data);
			{ssl_closed, Socket} ->
			    ?vtrace("read_header(ssl) -> socket closed", []),
			    {socket_closed, normal};
			{ssl_error, Socket, Reason} ->
			    ?vtrace("read_header(ssl) -> socket error: ~p",
				[Reason]),
			    {socket_closed, Reason}

		    after Timeout ->
			    ?vlog("read_header -> timeout", []),
			    {socket_closed, timeout}
		    end;

		Error ->
		    httpd_response:send_status(SocketType, Socket,
					       500, none, ConfigDB),
		    Error
	    end
    end.


terminated_header(MaxHdrSz, Data) ->
    D1 = lists:flatten(Data),
    ?vtrace("terminated_header -> Data size: ~p",[sz(D1)]),
    case hsplit(MaxHdrSz,[],D1) of
	not_terminated ->
	    false;
	[Header, EntityBodyPart] ->
	    {true, Header++"\r\n\r\n",EntityBodyPart}
    end.


transform_header(SocketType, Socket, Request, ConfigDB, InitData, BodyPart) ->
    case httpd_parse:request_header(Request) of
	{not_implemented, RequestLine, Method, RequestURI, ParsedHeader,
	 HTTPVersion} ->
	    httpd_response:send_status(SocketType, Socket, 501,
				       {Method, RequestURI, HTTPVersion},
				       ConfigDB),
	    {error,"Not Implemented"};
	{bad_request, {forbidden, URI}} ->
	    httpd_response:send_status(SocketType, Socket, 403, URI, ConfigDB),
	    {error,"Forbidden Request"};
	{bad_request, Reason} ->
	    httpd_response:send_status(SocketType, Socket, 400, none,
				       ConfigDB),
	    {error,"Malformed request"};
	{ok,[Method, RequestURI, HTTPVersion, RequestLine, ParsedHeader]} ->
	    ?DEBUG("send -> ~n"
		   "    Method:      ~p~n"
		   "    RequestURI:  ~p~n"
		   "    HTTPVersion: ~p~n"
		   "    RequestLine: ~p~n",
		   [Method, RequestURI, HTTPVersion, RequestLine]),
	    {ok, Info} =
		httpd_parse:get_request_record(Socket, SocketType, ConfigDB,
					       Method, RequestURI, HTTPVersion,
					       RequestLine, ParsedHeader,
					       [], InitData),
	    %% Control that the Host header field is provided
	    case Info#mod.absolute_uri of
		nohost ->
		    case Info#mod.http_version of
			"HTTP/1.1" ->
			    httpd_response:send_status(Info, 400, none),
			    {error,"No host specified"};
			_ ->
			    {ok, Info, BodyPart}
		    end;
		_ ->
		    {ok, Info, BodyPart}
	    end
    end.


hsplit(_MaxHdrSz, Accu,[]) ->
    not_terminated;
hsplit(_MaxHdrSz, Accu, [ $\r, $\n, $\r, $\n | Tail]) ->
    [lists:reverse(Accu), Tail];
hsplit(nolimit, Accu, [H|T]) ->
    hsplit(nolimit,[H|Accu],T);
hsplit(MaxHdrSz, Accu, [H|T]) when length(Accu) < MaxHdrSz ->
    hsplit(MaxHdrSz,[H|Accu],T);
hsplit(MaxHdrSz, Accu, D) ->
    throw({error,{header_too_long,length(Accu),length(D)}}).



%%----------------------------------------------------------------------
%% The http/1.1 standard chapter 8.2.3 says that a request containing
%% An Except header-field must be responded to by 100 (Continue) by
%% the server before the client sends the body.
%%----------------------------------------------------------------------

read_entity_body(SocketType, Socket, Timeout, Max, Length, BodyPart, Info,
		 ConfigDB) when integer(Max) ->
    case expect(Info#mod.http_version, Info#mod.parsed_header, ConfigDB) of
	continue when Max > Length ->
	    ?DEBUG("read_entity_body()->100 Continue  ~n", []),
	    httpd_response:send_status(Info, 100, ""),
	    read_entity_body2(SocketType, Socket, Timeout, Max, Length,
			      BodyPart, Info, ConfigDB);
	continue when Max < Length ->
	    httpd_response:send_status(Info, 417, "Body to big"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Expect denied according to size"};
	break ->
	    httpd_response:send_status(Info, 417, "Method not allowed"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Expect conditions was not fullfilled"};
	no_expect_header ->
	    read_entity_body2(SocketType, Socket, Timeout, Max, Length,
			      BodyPart, Info, ConfigDB);
	http_1_0_expect_header ->
	    httpd_response:send_status(Info, 400,
				       "Only HTTP/1.1 Clients "
				       "may use the Expect Header"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Due to a HTTP/1.0 expect header"}
    end;

read_entity_body(SocketType, Socket, Timeout, Max, Length, BodyPart,
		 Info, ConfigDB) ->
    case expect(Info#mod.http_version, Info#mod.parsed_header, ConfigDB) of
	continue ->
	    ?DEBUG("read_entity_body() -> 100 Continue  ~n", []),
	    httpd_response:send_status(Info, 100, ""),
	    read_entity_body2(SocketType, Socket, Timeout, Max, Length,
			      BodyPart, Info, ConfigDB);
	break->
	    httpd_response:send_status(Info, 417, "Method not allowed"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Expect conditions was not fullfilled"};
	no_expect_header ->
	    read_entity_body2(SocketType, Socket, Timeout, Max, Length,
			      BodyPart, Info, ConfigDB);
	http_1_0_expect_header ->
	    httpd_response:send_status(Info, 400,
				       "HTTP/1.0 Clients are not allowed "
				       "to use the Expect Header"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Expect header field in an HTTP/1.0 request"}
	end.

%%----------------------------------------------------------------------
%% control if the body is transfer encoded
%%----------------------------------------------------------------------
read_entity_body2(SocketType, Socket, Timeout, Max, Length, BodyPart,
		  Info, ConfigDB) ->
    ?DEBUG("read_entity_body2() -> "
	"~n   Max:    ~p"
	"~n   Length: ~p"
	"~n   Socket: ~p", [Max, Length, Socket]),

    case transfer_coding(Info) of
	{chunked, ChunkedData} ->
	    ?DEBUG("read_entity_body2() -> "
		"Transfer-encoding: Chunked Data: BodyPart ~s", [BodyPart]),
	    read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, [],
				BodyPart);
	unknown_coding ->
	    ?DEBUG("read_entity_body2() -> Transfer-encoding: Unknown",[]),
	    httpd_response:send_status(Info, 501, "Unknown Transfer-Encoding"),
	    httpd_socket:close(SocketType, Socket),
	    {socket_closed,"Expect conditions was not fullfilled"};
	none ->
	      ?DEBUG("read_entity_body2() -> Transfer-encoding: none",[]),
	    read_entity_body(SocketType, Socket, Timeout, Max, Length,
			     BodyPart)
    end.


%%----------------------------------------------------------------------
%% The body was plain read it from the socket
%% ----------------------------------------------------------------------
read_entity_body(_SocketType, _Socket, _Timeout, _Max, 0, _BodyPart) ->
    {ok, []};

read_entity_body(_SocketType, _Socket, _Timeout, Max, Len, _BodyPart)
  when Max < Len ->
    ?vlog("body to long: "
	  "~n   Max: ~p"
	  "~n   Len: ~p", [Max,Len]),
    throw({error,{body_too_long,Max,Len}});

%% OTP-4409: Fixing POST problem
read_entity_body(_,_,_,_, Len, BodyPart) when Len == length(BodyPart) ->
    ?vtrace("read_entity_body -> done when"
	"~n   Len = length(BodyPart): ~p", [Len]),
    {ok, BodyPart};

%% OTP-4550: Fix problem with trailing garbage produced by some clients.
read_entity_body(_, _, _, _, Len, BodyPart) when Len < length(BodyPart) ->
    ?vtrace("read_entity_body -> done when"
	"~n   Len:              ~p"
	"~n   length(BodyPart): ~p", [Len, length(BodyPart)]),
    {ok, lists:sublist(BodyPart,Len)};

read_entity_body(SocketType, Socket, Timeout, Max, Len, BodyPart) ->
    ?vtrace("read_entity_body -> entry when"
	"~n   Len:              ~p"
	"~n   length(BodyPart): ~p", [Len, length(BodyPart)]),
    %% OTP-4548:
    %% The length calculation was previously (inets-2.*) done in the
    %% read function. As of 3.0 it was removed from read but not
    %% included here.
    L = Len - length(BodyPart),
    case httpd_socket:recv(SocketType, Socket, L, Timeout) of
	{ok, Body} ->
	    ?vtrace("read_entity_body -> received some data:"
		"~n   length(Body): ~p", [length(Body)]),
	    {ok, BodyPart ++ Body};
	{error,closed} ->
	    {socket_closed,normal};
	{error,etimedout} ->
	    {socket_closed, timeout};
	{error,Reason} ->
	    {socket_closed, Reason};
	Other ->
	    {socket_closed, Other}
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% If the body of the message is encoded used the chunked transfer encoding
%% it looks somethin like this:
%% METHOD URI HTTP/VSN
%% Transfer-Encoding: chunked
%% CRLF
%% ChunkSize
%% Chunk
%% ChunkSize
%% Chunk
%% 0
%% Trailer
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, Body, []) ->
    ?DEBUG("read_chunked_entity()->:no_chunks ~n", []),
    read_chunked_entity(Info#mod.socket_type, Info#mod.socket,
			Timeout, Max, Length, ChunkedData, Body,
			Info#mod.config_db, Info);

read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, Body, BodyPart) ->
    %% Get the size
    ?DEBUG("read_chunked_entity() -> PrefetchedBodyPart: ~p ~n",[BodyPart]),
    case parse_chunk_size(Info, Timeout, BodyPart) of
	{ok, Size, NewBodyPart} when Size > 0 ->
	    ?DEBUG("read_chunked_entity() -> Size: ~p ~n", [Size]),
	    case parse_chunked_entity_body(Info, Timeout, Max, length(Body),
					   Size, NewBodyPart) of
		{ok, Chunk, NewBodyPart1} ->
		    ?DEBUG("read_chunked_entity()->Size: ~p ~n", [Size]),
		    read_chunked_entity(Info, Timeout, Max, Length,
					ChunkedData, Body ++ Chunk,
					NewBodyPart1);
		OK ->
		    httpd_socket:close(Info#mod.socket_type, Info#mod.socket),
		    {socket_closed, error}
	    end;
	{ok, 0, Trailers} ->
	   ?DEBUG("read_chunked_entity()->Size: 0, Trailers: ~s Body: ~s ~n",
		[Trailers, Body]),
	    case parse_chunk_trailer(Info, Timeout, Info#mod.config_db,
				     Trailers) of
		{ok, TrailerFields} ->
		    {ok, TrailerFields, Body};
		_->
		    {ok, []}
	    end;
	Error ->
	    Error
    end.


parse_chunk_size(Info, Timeout, BodyPart) ->
    case httpd_util:split(remove_newline(BodyPart), "\r\n", 2) of
	{ok, [Size, Body]} ->
	    ?DEBUG("parse_chunk_size()->Size: ~p ~n", [Size]),
	    {ok, httpd_util:hexlist_to_integer(Size), Body};
	{ok, [Size]} ->
	    ?DEBUG("parse_chunk_size()->Size: ~p ~n", [Size]),
	    Sz = get_chunk_size(Info#mod.socket_type,
				Info#mod.socket, Timeout,
				lists:reverse(Size)),
	    {ok, Sz, []}
    end.

%%----------------------------------------------------------------------
%% We got the chunk size get the chunk
%%
%% Max:     Max numbers of bytes to read may also be undefined
%% Length:  Numbers of bytes already read
%% Size     Numbers of byte to read for the chunk
%%----------------------------------------------------------------------

%% body to big
parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart)
  when Max =< (Length + Size) ->
    {error, body_to_big};

%% Prefetched body part is bigger than the current chunk
%% (i.e. BodyPart includes more than one chunk)
parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart)
  when (Size+2) =< length(BodyPart) ->
    Chunk = string:substr(BodyPart, 1, Size),
    Rest  = string:substr(BodyPart, Size+3),
    ?DEBUG("parse_chunked_entity_body() -> ~nChunk: ~s ~nRest: ~s ~n",
	[Chunk, Rest]),
    {ok, Chunk, Rest};


%% We just got a part of the current chunk
parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart) ->
    %% OTP-4551:
    %% Subtracting BodyPart from Size does not produce an integer
    %% when BodyPart is a list...
    Remaining = Size - length(BodyPart),
    LastPartOfChunk = read_chunked_entity_body(Info#mod.socket_type,
					       Info#mod.socket,
					       Timeout, Max,
					       Length, Remaining),
    %% Remove newline
    httpd_socket:recv(Info#mod.socket_type, Info#mod.socket, 2, Timeout),
    ?DEBUG("parse_chunked_entity_body() -> "
	"~nBodyPart: ~s"
	"~nLastPartOfChunk: ~s  ~n",
	[BodyPart, LastPartOfChunk]),
    {ok, BodyPart ++ LastPartOfChunk, []}.


%%----------------------------------------------------------------------
%% If the data we got along with the header contained the whole chunked body
%% It may aswell contain the trailer :-(
%%----------------------------------------------------------------------
%% Either trailer begins with  \r\n and then all data is there or
%% The trailer has data  then read upto \r\n\r\n
parse_chunk_trailer(Info,Timeout,ConfigDB,"\r\n")->
    {ok,[]};
parse_chunk_trailer(Info,Timeout,ConfigDB,Trailers) ->
    ?DEBUG("parse_chunk_trailer()->Trailers: ~s ~n", [Trailers]),
    case string:rstr(Trailers,"\r\n\r\n") of
	0 ->
	    MaxHdrSz=httpd_util:lookup(ConfigDB, max_header_size, 10240),
	    read_trailer_end(Info,Timeout,MaxHdrSz,Trailers);
	_->
	    %%We got the whole header parse it up
	    parse_trailers(Trailers)
    end.

parse_trailers(Trailer)->
    ?DEBUG("parse_trailer()->Trailer: ~s",[Trailer]),
    {ok,[Fields0|Crap]}=httpd_util:split(Trailer,"\r\n\r\n",2),
    Fields=string:tokens(Fields0,"\r\n"),
	[getTrailerField(X)||X<-Fields,lists:member($:,X)].


read_trailer_end(Info,Timeout,MaxHdrSz,[])->
    ?DEBUG("read_trailer_end()->[]",[]),
    case read_trailer(Info#mod.socket_type,Info#mod.socket,
		 Timeout,MaxHdrSz,[],[],
		      httpd_util:key1search(Info#mod.parsed_header,"trailer",[])) of
	{ok,Trailers}->
	    Trailers;
	_->
	    []
    end;
read_trailer_end(Info,Timeout,MaxHdrSz,Trailers)->
    ?DEBUG("read_trailer_end()->Trailers: ~s ~n ",[Trailers]),
    %% Get the last paart of the the last headerfield
    End=lists:reverse(lists:takewhile(fun(X)->case X of 10 ->false;13->false;_ ->true end end,lists:reverse(Trailers))),
    Fields0=regexp:split(Trailers,"\r\n"),
    %%Get rid of the last header field
    [_Last|Fields]=lists:reverse(Fields0),
    Headers=[getTrailerField(X)||X<-Fields,lists:member($:,X)],
    case read_trailer(Info#mod.socket_type,Info#mod.socket,
		 Timeout,MaxHdrSz,Headers,End,
		      httpd_util:key1search(Info#mod.parsed_header,"trailer",[])) of
	{ok,Trailers}->
	    Trailers;
	_->
	    []
    end.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The code below is a a good way to read in chunked encoding but
%% that require that the encoding comes from a stream and not from a list
%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

%%----------------------------------------------------------------------
%% The body is encoded by chubnked encoding read it in
%% ChunkedData= Chunked extensions
%% Body= the inread chunked body
%% Max:     Max numbers of bytes to read
%% Length:  Numbers of bytes already readed
%% Size     Numbers of byte to read for the chunk
%%----------------------------------------------------------------------



read_chunked_entity(SocketType, Socket, Timeout, Max, Length, ChunkedData,
		    Body, ConfigDB, Info) ->
    T = t(),
    case get_chunk_size(SocketType,Socket,Timeout,[]) of
	Size when integer(Size), Size>0 ->
	    case read_chunked_entity_body(SocketType, Socket,
					  Timeout-(t()-T),
					  Max, length(Body), Size) of
		{ok,Chunk} ->
		    ?DEBUG("read_chunked_entity/9 Got a chunk: ~p " ,[Chunk]),
		    %% Two bytes are left of the chunk, that is the CRLF
		    %% at the end that is not a part of the message
		    %% So we read it and do nothing with it.
		    httpd_socket:recv(SocketType,Socket,2,Timeout-(t()-T)),
		    read_chunked_entity(SocketType, Socket, Timeout-(t()-T),
					Max, Length, ChunkedData, Body++Chunk,
					ConfigDB, Info);
		Error ->
		    ?DEBUG("read_chunked_entity/9 Error: ~p " ,[Error]),
		    httpd_socket:close(SocketType,Socket),
		    {socket_closed,error}
	    end;
	Size when integer(Size), Size == 0 ->
	    %% Must read in any trailer fields here
	    read_chunk_trailer(SocketType, Socket, Timeout,
			       Max, Info, ChunkedData, Body, ConfigDB);
	Error ->
	    Error
    end.


%% If a user wants to send header data after the chunked data we
%% must pick it out
read_chunk_trailer(SocketType, Socket, Timeout, Max, Info, ChunkedData,
		   Body, ConfigDB) ->
    ?DEBUG("read_chunk_trailer/8: ~p " ,[Body]),
    MaxHdrSz = httpd_util:lookup(ConfigDB,max_header_size,10240),
    case httpd_util:key1search(Info#mod.parsed_header,"trailer")of
	undefined ->
	    {ok,Body};
	Fields ->
	    case read_trailer(SocketType, Socket, Timeout,
			      MaxHdrSz, [], [],
			      string:tokens(
				httpd_util:to_lower(Fields),",")) of
		{ok,[]} ->
		    {ok,Body};
		{ok,HeaderFields} ->
		    % ParsedExtraHeaders =
		    % httpd_parse:tagup_header(httpd_parse:split_lines(HeaderFields)),
		    {ok,HeaderFields,Body};
		Error ->
		    Error
	    end
    end.

read_chunked_entity_body(SocketType, Socket, Timeout, Max, Length, Size)
  when integer(Max) ->
    read_entity_body(SocketType, Socket, Timeout, Max-Length, Size, []);

read_chunked_entity_body(SocketType, Socket, Timeout, Max, _Length, Size) ->
    read_entity_body(SocketType, Socket, Timeout, Max, Size, []).

%% If we read in the \r\n the httpd_util:hexlist_to_integer
%% Will remove it and we get rid of it emmediatly :-)
get_chunk_size(SocketType, Socket, Timeout, Size) ->
    T = t(),
    ?DEBUG("get_chunk_size: ~p " ,[Size]),
    case httpd_socket:recv(SocketType,Socket,1,Timeout) of
	{ok,[Digit]} when Digit==$\n ->
	    httpd_util:hexlist_to_integer(lists:reverse(Size));
       {ok,[Digit]} ->
	    get_chunk_size(SocketType,Socket,Timeout-(t()-T),[Digit|Size]);
	{error,closed} ->
	    {socket_closed,normal};
	{error,etimedout} ->
	    {socket_closed, timeout};
	{error,Reason} ->
	    {socket_closed, Reason};
	Other ->
	    {socket_closed,Other}
    end.




%%----------------------------------------------------------------------
%% Reads the HTTP-trailer
%% Would be easy to tweak the read_head to do this but in this way
%% the chunked encoding can be updated better.
%%----------------------------------------------------------------------


%% When end is reached
%% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Last,[]) ->
%%    {ok,Headers};

%% When header to big
read_trailer(_,_,_,MaxHdrSz,Headers,Bs,_Fields)
  when MaxHdrSz < length(Headers) ->
    ?vlog("header to long: "
	  "~n   MaxHdrSz:   ~p"
	  "~n   length(Bs): ~p", [MaxHdrSz,length(Bs)]),
    throw({error,{header_too_long,MaxHdrSz,length(Bs)}});

%% The last Crlf is there
read_trailer(_, _, _, _, Headers, [$\n, $\r], _) ->
    {ok,Headers};

read_trailer(SocketType, Socket, Timeout, MaxHdrSz, Headers,
	     [$\n, $\r|Rest], Fields) ->
    case getTrailerField(lists:reverse(Rest))of
	{error,Reason}->
	    {error,"Bad trailer"};
	{HeaderField,Value}->
	    case lists:member(HeaderField,Fields) of
		true ->
		    read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
				 [{HeaderField,Value} |Headers],[],
				 lists:delete(HeaderField,Fields));
		false ->
		    read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
				 Headers,[],Fields)
	    end
    end;

% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,[$\n, $\r|Rest],Fields) ->
%     case Rest of
% 	[] ->
% 	   read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Rest,Fields);
% 	Field ->
% 	    case getTrailerField(lists:reverse(Rest))of
% 		{error,Reason}->
% 		    {error,"Bad trailer"};
% 		{HeaderField,Value}->
% 		    case lists:member(HeaderField,Fields) of
% 			true ->
% 			    read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
% 					 [{HeaderField,Value} |Headers],[],
% 					 lists:delete(HeaderField,Fields));
% 			false ->
% 			    read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
% 					 Headers,[],Fields)
% 		    end
% 	    end
%     end;

read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Bs,Fields) ->
    %% ?vlog("read_header -> entry with Timeout: ~p",[Timeout]),
    T = t(),
    case (catch httpd_socket:recv(SocketType,Socket,1,Timeout)) of
	{ok,[B]} ->
	    read_trailer(SocketType, Socket, Timeout-(t()-T),
			 MaxHdrSz, Headers, [B|Bs], Fields);
	{error,closed} ->
	    {socket_closed,normal};
	{error,etimedout} ->
	    {socket_closed, timeout};
	{error,Reason} ->
	    {socket_closed, Reason};
	Other ->
	    {socket_closed,Other}
    end.

getTrailerField(HeaderField)->
   case string:str(HeaderField,":") of
       0->
	   {error,"badheaderfield"};
       Number ->
	   {httpd_util:to_lower(string:substr(HeaderField,1,Number-1)),
	    httpd_util:to_lower(string:substr(HeaderField,Number+1))}
   end.




%% Time in milli seconds
t() ->
    {A,B,C} = erlang:now(),
    A*1000000000+B*1000+(C div 1000).

%%----------------------------------------------------------------------
%% If the user sends an expect header-field with the value 100-continue
%% We must send a 100 status message if he is a HTTP/1.1 client.

%% If it is an HTTP/1.0 client it's little more difficult.
%% If expect is not defined it is easy but in the other case shall we
%% Break or the transmission or let it continue the standard is not clear
%% if to break connection or wait for data.
%%----------------------------------------------------------------------
expect(HTTPVersion,ParsedHeader,ConfigDB)->
    case HTTPVersion of
	[$H,$T,$T,$P,$\/,$1,$.,N|_Whatever]when N>=1->
	    case httpd_util:key1search(ParsedHeader,"expect") of
		"100-continue" ->
		    continue;
		undefined ->
		    no_expect_header;
		NewValue ->
		    break
	    end;
	_OldVersion ->
	    case httpd_util:key1search(ParsedHeader,"expect") of
		undefined ->
		    no_expect_header;
		NewValue ->
		    case httpd_util:lookup(ConfigDB,expect,continue) of
			continue->
			    no_expect_header;
			_ ->
			    http_1_0_expect_header
		    end
	    end
    end.


%%----------------------------------------------------------------------
%% According to the http/1.1 standard all applications must understand
%% Chunked encoded data. (Last line chapter 3.6.1).
transfer_coding(#mod{parsed_header = Ph}) ->
    case httpd_util:key1search(Ph, "transfer-encoding", none) of
	none ->
	    none;
	[$c,$h,$u,$n,$k,$e,$d|Data]->
	    {chunked,Data};
	_ ->
	    unknown_coding
    end.



handle_read_error({header_too_long,Max,Rem},
                  SocketType,Socket,ConfigDB,Peername) ->
    String = io_lib:format("header too long: ~p : ~p",[Max,Rem]),
    handle_read_error(ConfigDB,String,SocketType,Socket,Peername,
                      max_header_action,close);
handle_read_error({body_too_long,Max,Actual},
                  SocketType,Socket,ConfigDB,Peername) ->
    String = io_lib:format("body too long: ~p : ~p",[Max,Actual]),
    handle_read_error(ConfigDB,String,SocketType,Socket,Peername,
                      max_body_action,close);
handle_read_error(Error,SocketType,Socket,ConfigDB,Peername) ->
    ok.


handle_read_error(ConfigDB, ReasonString, SocketType, Socket, Peername,
                  Item, Default) ->
    ?vlog("error reading request: ~s",[ReasonString]),
    E = lists:flatten(
          io_lib:format("Error reading request: ~s",[ReasonString])),
    error_log(mod_log, SocketType, Socket, ConfigDB, Peername, E),
    error_log(mod_disk_log, SocketType, Socket, ConfigDB, Peername, E),
    case httpd_util:lookup(ConfigDB,Item,Default) of
        reply414 ->
            send_read_status(SocketType, Socket, 414, ReasonString, ConfigDB);
        _ ->
            ok
    end.

send_read_status(SocketType, Socket, Code, ReasonString, ConfigDB) ->
    httpd_response:send_status(SocketType, Socket, Code, ReasonString,
			       ConfigDB).


error_log(Mod, SocketType, Socket, ConfigDB, Peername, String) ->
    Modules = httpd_util:lookup(ConfigDB, modules,
				[mod_get, mod_head, mod_log]),
    case lists:member(Mod, Modules) of
	true ->
	    Mod:error_log(SocketType, Socket, ConfigDB, Peername, String);
	_ ->
	    ok
    end.


sz(L) when list(L) ->
    length(L);
sz(B) when binary(B) ->
    size(B);
sz(O) ->
    {unknown_size,O}.


%% Socket utility functions:

close(SocketType, Socket, ConfigDB) ->
    case httpd_socket:close(SocketType, Socket) of
        ok ->
            ok;
        {error, Reason} ->
            ?vlog("error while closing socket: ~p",[Reason]),
	    ok
    end.

close_sleep({ssl, _}, Time) ->
    sleep(Time);
close_sleep(_, _) ->
    ok.


sleep(T) -> receive after T -> ok end.


dec(N) when integer(N) ->
    N-1;
dec(N) ->
    N.


content_length(#mod{parsed_header = Ph}) ->
    list_to_integer(httpd_util:key1search(Ph, "content-length","0")).


remove_newline(List)->
    lists:dropwhile(fun newline/1,List).

newline($\r) ->
    true;
newline($\n) ->
    true;
newline(_Sign) ->
    false.