From a94f7bd152a08a5d3fc43475f3fae7d886705b08 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 6 Nov 2012 14:56:57 +0100 Subject: Option {bit_limit, integer()} to close sockets where clients are too slow --- .../src/http_server/httpd_request_handler.erl | 52 +++++++++++++++++----- 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'lib/inets/src/http_server') diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index b62c10bbc7..0d584c70ae 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -44,7 +44,9 @@ timeout, %% infinity | integer() > 0 timer, %% ref() - Request timer headers, %% #http_request_h{} - body %% binary() + body, %% binary() + data, %% The total data received in bits, checked after 10s + bit_limit %% Bit limit per second before kick out }). %%==================================================================== @@ -98,7 +100,6 @@ init([Manager, ConfigDB, AcceptTimeout]) -> [{socket_type, SocketType}, {socket, Socket}]), TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000), - Then = erlang:now(), ?hdrd("negotiate", []), @@ -139,12 +140,11 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> mfa = MFA}, ?hdrt("activate request timeout", []), - NewState = activate_request_timeout(State), ?hdrt("set socket options (binary, packet & active)", []), http_transport:setopts(SocketType, Socket, [binary, {packet, 0}, {active, once}]), - + NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, bit_limit, false)), ?hdrt("init done", []), gen_server:enter_loop(?MODULE, [], NewState). @@ -205,16 +205,25 @@ handle_info({Proto, Socket, Data}, ?hdrd("received data", [{data, Data}, {proto, Proto}, {socket, Socket}, {socket_type, SockType}, {mfa, MFA}]), - + %% case (catch Module:Function([Data | Args])) of PROCESSED = (catch Module:Function([Data | Args])), - + NewDataSize = case State#state.bit_limit of + undefined -> + undefined; + _ -> + State#state.data + bit_size(Data) + end, ?hdrt("data processed", [{processing_result, PROCESSED}]), - case PROCESSED of {ok, Result} -> ?hdrd("data processed", [{result, Result}]), - NewState = cancel_request_timeout(State), + NewState = case NewDataSize of + undefined -> + cancel_request_timeout(State); + _ -> + set_new_data_size(cancel_request_timeout(State), NewDataSize) + end, handle_http_msg(Result, NewState); {error, {uri_too_long, MaxSize}, Version} -> @@ -239,7 +248,12 @@ handle_info({Proto, Socket, Data}, NewMFA -> ?hdrd("data processed - reactivate socket", [{new_mfa, NewMFA}]), http_transport:setopts(SockType, Socket, [{active, once}]), - {noreply, State#state{mfa = NewMFA}} + case NewDataSize of + undefined -> + {noreply, State#state{mfa = NewMFA}}; + _ -> + {noreply, State#state{mfa = NewMFA, data = NewDataSize}} + end end; %% Error cases @@ -263,7 +277,14 @@ handle_info(timeout, #state{mod = ModData} = State) -> error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; - +handle_info(check_data, #state{data = Data, bit_limit = Bit_Limit} = State) -> + case Data >= (Bit_Limit * 10) of %% Ten seconds itnerval + true -> + erlang:send_after(10000, self(), check_data), + {noreply, State#state{data = 0}}; + _ -> + {stop, normal, State#state{response_sent = true}} + end; %% Default case handle_info(Info, #state{mod = ModData} = State) -> Error = lists:flatten( @@ -311,6 +332,8 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +set_new_data_size(State, NewData) -> + State#state{data = NewData}. await_socket_ownership_transfer(AcceptTimeout) -> receive {socket_ownership_transfered, SocketType, Socket} -> @@ -603,7 +626,14 @@ activate_request_timeout(#state{timeout = Time} = State) -> ?hdrt("activate request timeout", [{time, Time}]), Ref = erlang:send_after(Time, self(), timeout), State#state{timer = Ref}. - +data_receive_counter(State, Bit_limit) -> + case Bit_limit of + false -> + State#state{data = 0}; + Nr -> + erlang:send_after(10000, self(), check_data), + State#state{data = 0, bit_limit = Nr} + end. cancel_request_timeout(#state{timer = undefined} = State) -> State; cancel_request_timeout(#state{timer = Timer} = State) -> -- cgit v1.2.3 From 6291667b75dc66f57b1e00fed9aac356538b9ed9 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 6 Nov 2012 16:00:52 +0100 Subject: Changed to Bytes Per Second when checking clients rate, new options {byte_limit, integer()} --- lib/inets/src/http_server/httpd_request_handler.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'lib/inets/src/http_server') diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 0d584c70ae..73ccd74917 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -46,7 +46,7 @@ headers, %% #http_request_h{} body, %% binary() data, %% The total data received in bits, checked after 10s - bit_limit %% Bit limit per second before kick out + byte_limit %% Bit limit per second before kick out }). %%==================================================================== @@ -144,7 +144,7 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> ?hdrt("set socket options (binary, packet & active)", []), http_transport:setopts(SocketType, Socket, [binary, {packet, 0}, {active, once}]), - NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, bit_limit, false)), + NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, byte_limit, false)), ?hdrt("init done", []), gen_server:enter_loop(?MODULE, [], NewState). @@ -208,11 +208,11 @@ handle_info({Proto, Socket, Data}, %% case (catch Module:Function([Data | Args])) of PROCESSED = (catch Module:Function([Data | Args])), - NewDataSize = case State#state.bit_limit of + NewDataSize = case State#state.byte_limit of undefined -> undefined; _ -> - State#state.data + bit_size(Data) + State#state.data + byte_size(Data) end, ?hdrt("data processed", [{processing_result, PROCESSED}]), case PROCESSED of @@ -277,8 +277,8 @@ handle_info(timeout, #state{mod = ModData} = State) -> error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; -handle_info(check_data, #state{data = Data, bit_limit = Bit_Limit} = State) -> - case Data >= (Bit_Limit * 10) of %% Ten seconds itnerval +handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) -> + case Data >= (Byte_Limit * 10) of %% Ten seconds itnerval true -> erlang:send_after(10000, self(), check_data), {noreply, State#state{data = 0}}; @@ -626,13 +626,13 @@ activate_request_timeout(#state{timeout = Time} = State) -> ?hdrt("activate request timeout", [{time, Time}]), Ref = erlang:send_after(Time, self(), timeout), State#state{timer = Ref}. -data_receive_counter(State, Bit_limit) -> - case Bit_limit of +data_receive_counter(State, Byte_limit) -> + case Byte_limit of false -> State#state{data = 0}; Nr -> erlang:send_after(10000, self(), check_data), - State#state{data = 0, bit_limit = Nr} + State#state{data = 0, byte_limit = Nr} end. cancel_request_timeout(#state{timer = undefined} = State) -> State; -- cgit v1.2.3 From 40d2dd2e50cf245ae88e0a10b3d2f50386264ca5 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 6 Nov 2012 16:42:09 +0100 Subject: Changed property to minimum_bytes_per_second and checking if is integer on start --- lib/inets/src/http_server/httpd_conf.erl | 15 +++++++++++++-- lib/inets/src/http_server/httpd_request_handler.erl | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'lib/inets/src/http_server') diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index b575d7331b..884e3defb8 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -483,7 +483,7 @@ validate_properties(Properties) -> case mandatory_properties(Properties) of ok -> %% Second, check that property dependency are ok - {ok, validate_properties2(Properties)}; + {ok, check_minimum_bytes_per_second(validate_properties2(Properties))}; Error -> throw(Error) end. @@ -522,7 +522,18 @@ validate_properties2(Properties) -> throw(Error) end end. - +check_minimum_bytes_per_second(Properties) -> + case proplists:get_value(minimum_bytes_per_second, Properties, false) of + false -> + Properties; + Nr -> + case is_integer(Nr) of + false -> + throw({error, {minimum_bytes_per_second, is_not_integer}}); + _ -> + Properties + end + end. mandatory_properties(ConfigList) -> a_must(ConfigList, [server_name, port, server_root, document_root]). diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 73ccd74917..0f9e82fd29 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -144,7 +144,7 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> ?hdrt("set socket options (binary, packet & active)", []), http_transport:setopts(SocketType, Socket, [binary, {packet, 0}, {active, once}]), - NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, byte_limit, false)), + NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)), ?hdrt("init done", []), gen_server:enter_loop(?MODULE, [], NewState). -- cgit v1.2.3 From 39537f88c45304d0fbd1cd61882fc7df7473041a Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Fri, 9 Nov 2012 12:04:48 +0100 Subject: Reduced checking of bytes per second to every second --- lib/inets/src/http_server/httpd_request_handler.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/inets/src/http_server') diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 0f9e82fd29..56c934a0f6 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -278,9 +278,9 @@ handle_info(timeout, #state{mod = ModData} = State) -> "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) -> - case Data >= (Byte_Limit * 10) of %% Ten seconds itnerval + case Data >= Byte_Limit of true -> - erlang:send_after(10000, self(), check_data), + erlang:send_after(1000, self(), check_data), {noreply, State#state{data = 0}}; _ -> {stop, normal, State#state{response_sent = true}} @@ -631,7 +631,7 @@ data_receive_counter(State, Byte_limit) -> false -> State#state{data = 0}; Nr -> - erlang:send_after(10000, self(), check_data), + erlang:send_after(1000, self(), check_data), State#state{data = 0, byte_limit = Nr} end. cancel_request_timeout(#state{timer = undefined} = State) -> -- cgit v1.2.3 From fec356cb10757fbeb32abcfdb07f5152ca18c0de Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Wed, 14 Nov 2012 16:08:00 +0100 Subject: Fine adjustments of slowdos alg --- lib/inets/src/http_server/httpd_request_handler.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lib/inets/src/http_server') diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 56c934a0f6..5e0bd39cb3 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -277,6 +277,14 @@ handle_info(timeout, #state{mod = ModData} = State) -> error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; +handle_info(check_data_first, #state{data = Data, byte_limit = Byte_Limit} = State) -> + case Data >= (Byte_Limit*3) of + true -> + erlang:send_after(1000, self(), check_data), + {noreply, State#state{data = 0}}; + _ -> + {stop, normal, State#state{response_sent = true}} + end; handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) -> case Data >= Byte_Limit of true -> @@ -631,7 +639,7 @@ data_receive_counter(State, Byte_limit) -> false -> State#state{data = 0}; Nr -> - erlang:send_after(1000, self(), check_data), + erlang:send_after(3000, self(), check_data_first), State#state{data = 0, byte_limit = Nr} end. cancel_request_timeout(#state{timer = undefined} = State) -> -- cgit v1.2.3