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