diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b59f1fa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: erlang + +otp_release: + - 17.0 + +script: + - make + +sudo: false diff --git a/CHANGELOG.md b/CHANGELOG.md index c793af0..e709315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ eSockd ChangeLog ================== +3.0-beta (2015/10/30) +------------------------ + +Parameterized Connection Module + +Rate Limit + + 2.8.0-beta (2015/10/28) ------------------------ diff --git a/README.md b/README.md index 986cc97..52fda40 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,17 @@ -# eSockd +# eSockd [![Build Status](https://travis-ci.org/emqtt/esockd.svg?branch=3.0)](https://travis-ci.org/emqtt/esockd) Erlang General Non-blocking TCP/SSL Socket Server. ## Features -General Non-blocking TCP/SSL Socket Server. - -Acceptor Pool and Asynchronous TCP Accept. - -Max connections management. - -Allow/Deny by peer address. - -Keepalive Support. - -## Benchmark - -Benchmark 2.1.0-alpha release on one 8 cores, 32G memory ubuntu/14.04 server from qingcloud.com: - -``` -250K concurrent connections, 50K messages/sec, 40Mbps In/Out consumed 5G memory, 20% CPU/core -``` +* General Non-blocking TCP/SSL Socket Server +* Acceptor Pool and Asynchronous TCP Accept +* Parameterized Connection Module +* Max connections management +* Allow/Deny by peer address +* Keepalive Support +* Rate Limit ## Usage @@ -32,24 +22,24 @@ A Simple Echo Server: -export([start_link/1]). -start_link(SockArgs) -> - {ok, spawn_link(?MODULE, init, [SockArgs])}. +start_link(Conn) -> + {ok, spawn_link(?MODULE, init, [Conn])}. -init(SockArgs = {Transport, _Sock, _SockFun}) -> - {ok, NewSock} = esockd_connection:accept(SockArgs), - loop(Transport, NewSock, state). - -loop(Transport, Sock, State) -> - case Transport:recv(Sock, 0) of - {ok, Data} -> - {ok, Name} = Transport:peername(Sock), - io:format("~p: ~s~n", [Name, Data]), - Transport:send(Sock, Data), - loop(Transport, Sock, State); - {error, Reason} -> - io:format("tcp ~s~n", [Reason]), - {stop, Reason} - end. +init(Conn) -> + {ok, NewConn} = Conn:wait(), + loop(NewConn). + +loop(Conn) -> + case Conn:recv(0) of + {ok, Data} -> + {ok, PeerName} = Conn:peername(), + io:format("~s - ~s~n", [esockd_net:format(peername, PeerName), Data]), + Conn:send(Data), + loop(Conn); + {error, Reason} -> + io:format("tcp ~s~n", [Reason]), + {stop, Reason} + end. ``` Startup Echo Server: @@ -81,7 +71,7 @@ examples/ Example | Description ----------|------ -async_recv| prim_net async recv +async_recv| prim_net async recv/send gen_server| gen_server behaviour simple | simple echo server ssl | ssl echo server @@ -122,6 +112,7 @@ Options: {access, [esockd_access:rule()]} | {logger, atom() | {atom(), atom()}} | {ssl, [ssl:ssloption()]} | + {connopts, [{rate_limit, string()}]} | {sockopts, [gen_tcp:listen_option()]}. ``` @@ -212,11 +203,20 @@ Logger option: esockd:open(echo, 5000, [{logger, {error_logger, info}}], {echo_server, start_link, []}). ``` +## Benchmark + +Benchmark 2.1.0-alpha release on one 8 cores, 32G memory ubuntu/14.04 server from qingcloud.com: + +``` +250K concurrent connections, 50K messages/sec, 40Mbps In/Out consumed 5G memory, 20% CPU/core +``` + ## License The MIT License (MIT) ## Author -feng@emqtt.io +Feng Lee + diff --git a/doc/connection_sup.md b/doc/connection_sup.md deleted file mode 100644 index 53c8f86..0000000 --- a/doc/connection_sup.md +++ /dev/null @@ -1,17 +0,0 @@ -connection sup? -./inets-5.10.3/src/http_server/httpd_connection_sup.erl -./megaco-3.17.2/src/tcp/megaco_tcp_connection_sup.erl -./ssl-5.3.6/src/dtls_connection_sup.erl -./ssl-5.3.6/src/tls_connection_sup.erl - -rabbit_connection_sup? - - - -simple_one_for_one -temporary - -API: - -start_connection(Sup, SockArgs) - diff --git a/doc/keepalive.md b/doc/keepalive.md deleted file mode 100644 index 0d9454e..0000000 --- a/doc/keepalive.md +++ /dev/null @@ -1,3 +0,0 @@ - -## Socket KeepAlive - diff --git a/doc/supervisor.md b/doc/supervisor.md index 14d2caf..3a8b768 100644 --- a/doc/supervisor.md +++ b/doc/supervisor.md @@ -9,9 +9,9 @@ esockd_sup -> esockd_acceptor -> esockd_acceptor -> ...... - -> esockd_client_sup - -> esockd_client - -> esockd_client + -> esockd_connection_sup + -> esockd_connection + -> esockd_connection -> ...... ``` diff --git a/examples/async_recv/crt/demo.crt b/examples/async_recv/crt/demo.crt new file mode 100644 index 0000000..0018446 --- /dev/null +++ b/examples/async_recv/crt/demo.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuTCCAiICCQC8+3PPaqATfDANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMC +Q0gxETAPBgNVBAgTCFpoZUppYW5nMREwDwYDVQQHEwhIYW5nWmhvdTEUMBIGA1UE +ChMLWGlhb0xpIFRlY2gxHzAdBgNVBAsTFkluZm9ybWF0aW9uIFRlY2hub2xvZ3kx +EzARBgNVBAMTCnQuZW1xdHQuaW8xHzAdBgkqhkiG9w0BCQEWEGZlbmcgYXQgZW1x +dHQuaW8wHhcNMTUwMjI1MTc0NjQwWhcNMTYwMjI1MTc0NjQwWjCBoDELMAkGA1UE +BhMCQ0gxETAPBgNVBAgTCFpoZUppYW5nMREwDwYDVQQHEwhIYW5nWmhvdTEUMBIG +A1UEChMLWGlhb0xpIFRlY2gxHzAdBgNVBAsTFkluZm9ybWF0aW9uIFRlY2hub2xv +Z3kxEzARBgNVBAMTCnQuZW1xdHQuaW8xHzAdBgkqhkiG9w0BCQEWEGZlbmcgYXQg +ZW1xdHQuaW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAtN2OHsvltOk+9 +AtlwMtKuaWW2WpV/S0lRRG9x9k8pyd5PJeeYAr2jVsoWnZInb1CoEOHFcwxZLjv3 +gEvz+X+//W02YyI9hnvCJUpT/+6P0gJEbmTmqL078M6vbtwtiF1YC7mdo0nGAZuK +qedpIoEZbVJavf4S0vXWTsb3s5unAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAgUR3 +z4uDUsAl+xUorPMBIOS/ncHHVk1XucVv9Wi4chzzZ+4/Y77/fFqP6oxhQ59C9Q8i +iT5wjaE4R1eCge18lPSw3yb1tsTe5B3WkRTzziPq/Q/AsC+DifkkE1YW67leuJV/ +vz74sEi0dudmOVoe6peYxjEH8xXoIUqhnwXt/4Q= +-----END CERTIFICATE----- diff --git a/examples/async_recv/crt/demo.key b/examples/async_recv/crt/demo.key new file mode 100644 index 0000000..5d5786f --- /dev/null +++ b/examples/async_recv/crt/demo.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCwLTdjh7L5bTpPvQLZcDLSrmlltlqVf0tJUURvcfZPKcneTyXn +mAK9o1bKFp2SJ29QqBDhxXMMWS4794BL8/l/v/1tNmMiPYZ7wiVKU//uj9ICRG5k +5qi9O/DOr27cLYhdWAu5naNJxgGbiqnnaSKBGW1SWr3+EtL11k7G97ObpwIDAQAB +AoGBAKU1cbiLG0GdtU3rME3ZUj+RQNMZ4u5IVcBmTie4FcN8q4ombKQ2P3O4RX3z +IUZaZp+bS2F8uHt+8cVYPl57Zp5fwbIlv6jWgGpvXLsX8JBQl2OTw38B+hVwJvAM +h0mBzprUOs3KGZyF5cyA4osrZ4QvCZhwId9fAjwLGBF9i1yBAkEA4jWAF1sWQiwF +vY476m+0ihpRwGKjldKHWFZmvoB/AnNV/rXO+HRl3MB5wmO+Dqg3gJZrjGBgDeaV +g9hoQjK6ZwJBAMdg57iKLd8uUb7c4pR8fDdDbeeI5X7WDf2k9emT3BMPJPQ3EiSf +CStn1hRfp31U9CXEnw94rKHhrdMFrYjdzMECQCcWD3f5qTLt4GAMf5XWj199hLq1 +UIbGxdQhuccY9Nk7jJRiXczYb/Fg4KkSCvkFX/G8DAFJdc9xFEyfzAQEN+kCQH3a +nMrvZn9gBLffRKOIZPyZctHZp0xGIHTA4X39GMlrIN+Lt8coIKimlgssSlSiAK+q +iuFAQnC5PXlcNyuTHsECQAMNMY6jXikgSUZfVXitAFX3g9+IbjT9eJ92f60QneW8 +mxWQoqP3fqCSbTEysb7NojEEwppSZtaNgnBb5R4E+mU= +-----END RSA PRIVATE KEY----- diff --git a/examples/async_recv/src/async_recv_echo_server.app.src b/examples/async_recv/src/async_recv_echo_server.app.src index 8af9f4b..4936afc 100644 --- a/examples/async_recv/src/async_recv_echo_server.app.src +++ b/examples/async_recv/src/async_recv_echo_server.app.src @@ -1,7 +1,7 @@ {application, async_recv_echo_server, [ {description, "An Erlang async_recv_echo library"}, - {vsn, "1"}, + {vsn, "1.0"}, {modules, [ async_recv_echo ]}, diff --git a/examples/async_recv/src/async_recv_echo_server.erl b/examples/async_recv/src/async_recv_echo_server.erl index 14df482..5e6232d 100644 --- a/examples/async_recv/src/async_recv_echo_server.erl +++ b/examples/async_recv/src/async_recv_echo_server.erl @@ -26,6 +26,8 @@ %%%----------------------------------------------------------------------------- -module(async_recv_echo_server). +-include("../../../include/esockd.hrl"). + -behaviour(gen_server). %% start @@ -37,7 +39,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {transport, sock}). +-record(state, {conn}). -define(TCP_OPTIONS, [ binary, @@ -52,22 +54,26 @@ start() -> start([Port]) when is_atom(Port) -> start(list_to_integer(atom_to_list(Port))); start(Port) when is_integer(Port) -> - application:start(sasl), + [ok = application:start(App) || + App <- [sasl, syntax_tools, asn1, crypto, public_key, ssl]], ok = esockd:start(), + SslOpts = [{certfile, "./crt/demo.crt"}, + {keyfile, "./crt/demo.key"}], SockOpts = [{acceptors, 10}, {max_clients, 100000}, + {ssl, SslOpts}, {sockopts, ?TCP_OPTIONS}], MFArgs = {?MODULE, start_link, []}, esockd:open(echo, Port, SockOpts, MFArgs). %% eSockd Callbacks -start_link(SockArgs) -> - {ok, proc_lib:spawn_link(?MODULE, init, [SockArgs])}. +start_link(Conn) -> + {ok, proc_lib:spawn_link(?MODULE, init, [Conn])}. -init(SockArgs = {Transport, _Sock, _SockFun}) -> - {ok, NewSock} = esockd_connection:accept(SockArgs), - Transport:async_recv(NewSock, 0, infinity), - gen_server:enter_loop(?MODULE, [], #state{transport = Transport, sock = NewSock}). +init(Conn) -> + {ok, Conn1} = Conn:wait(), + Conn1:async_recv(0, infinity), + gen_server:enter_loop(?MODULE, [], #state{conn = Conn1}). handle_call(_Request, _From, State) -> {reply, ok, State}. @@ -75,17 +81,26 @@ handle_call(_Request, _From, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({inet_async, Sock, _Ref, {ok, Data}}, State = #state{transport = Transport, sock = Sock}) -> - {ok, PeerName} = Transport:peername(Sock), +handle_info({inet_async, Sock, _Ref, {ok, Data}}, State = #state{conn = ?ESOCK(Sock) = Conn}) -> + {ok, PeerName} = Conn:peername(), io:format("~s - ~s~n", [esockd_net:format(peername, PeerName), Data]), - Transport:send(Sock, Data), - Transport:async_recv(Sock, 0, infinity), + Conn:async_send(Data), + Conn:async_recv(0, infinity), {noreply, State}; -handle_info({inet_async, _Sock, _Ref, {error, Reason}}, State) -> - {stop, {shutdown, {inet_async_error, Reason}}, State}; +handle_info({inet_async, Sock, _Ref, {error, Reason}}, State = #state{conn = ?ESOCK(Sock)}) -> + io:format("shutdown for ~p~n", [Reason]), + shutdown(Reason, State); + +handle_info({inet_reply, Sock ,ok}, State = #state{conn = ?ESOCK(Sock)}) -> + {noreply, State}; -handle_info(_Info, State) -> +handle_info({inet_reply, Sock, {error, Reason}}, State = #state{conn = ?ESOCK(Sock)}) -> + io:format("shutdown for ~p~n", [Reason]), + shutdown(Reason, State); + +handle_info(Info, State) -> + io:format("~p~n", [Info]), {noreply, State}. terminate(_Reason, _State) -> @@ -94,3 +109,6 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. +shutdown(Reason, State) -> + {stop, {shutdown, Reason}, State}. + diff --git a/examples/gen_server/src/gen_echo_server.erl b/examples/gen_server/src/gen_echo_server.erl index 0a5824b..c969a18 100644 --- a/examples/gen_server/src/gen_echo_server.erl +++ b/examples/gen_server/src/gen_echo_server.erl @@ -26,6 +26,8 @@ %%%----------------------------------------------------------------------------- -module(gen_echo_server). +-include("../../../include/esockd.hrl"). + -behaviour(gen_server). %% start @@ -38,7 +40,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {transport, sock}). +-record(state, {conn}). -define(TCP_OPTIONS, [ binary, @@ -61,13 +63,13 @@ start(Port) when is_integer(Port) -> MFArgs = {?MODULE, start_link, []}, esockd:open(echo, Port, SockOpts, MFArgs). -start_link(SockArgs) -> - {ok, proc_lib:spawn_link(?MODULE, init, [SockArgs])}. +start_link(Conn) -> + {ok, proc_lib:spawn_link(?MODULE, init, [Conn])}. -init(SockArgs = {Transport, _Sock, _SockFun}) -> - {ok, NewSock} = esockd_connection:accept(SockArgs), - Transport:setopts(NewSock, [{active, once}]), - gen_server:enter_loop(?MODULE, [], #state{transport = Transport, sock = NewSock}). +init(Conn) -> + {ok, Conn1} = esockd_connection:ack(Conn), + Conn1:setopts([{active, once}]), + gen_server:enter_loop(?MODULE, [], #state{conn = Conn1}). handle_call(_Request, _From, State) -> {reply, ok, State}. @@ -75,18 +77,18 @@ handle_call(_Request, _From, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({tcp, Sock, Data}, State=#state{transport = Transport, sock = Sock}) -> - {ok, PeerName} = Transport:peername(Sock), +handle_info({tcp, Sock, Data}, State=#state{conn = ?ESOCK(Sock) = Conn}) -> + {ok, PeerName} = Conn:peername(), io:format("~s - ~s~n", [esockd_net:format(peername, PeerName), Data]), - Transport:send(Sock, Data), - Transport:setopts(Sock, [{active, once}]), + Conn:send(Data), + Conn:setopts([{active, once}]), {noreply, State}; -handle_info({tcp_error, Sock, Reason}, State=#state{sock = Sock}) -> +handle_info({tcp_error, Sock, Reason}, State=#state{conn = ?ESOCK(Sock)}) -> io:format("tcp_error: ~s~n", [Reason]), {stop, {shutdown, {tcp_error, Reason}}, State}; -handle_info({tcp_closed, Sock}, State=#state{sock=Sock}) -> +handle_info({tcp_closed, Sock}, State=#state{conn = ?ESOCK(Sock)}) -> io:format("tcp_closed~n"), {stop, normal, State}; diff --git a/examples/simple/src/echo_server.erl b/examples/simple/src/echo_server.erl index 91d7e2a..4cd0fdb 100644 --- a/examples/simple/src/echo_server.erl +++ b/examples/simple/src/echo_server.erl @@ -29,7 +29,7 @@ -export([start/0, start/1]). %%callback --export([start_link/1, init/1, loop/3]). +-export([start_link/1, init/1, loop/1]). -define(TCP_OPTIONS, [ binary, @@ -67,20 +67,20 @@ start(Port) when is_integer(Port) -> %% %% @end %%------------------------------------------------------------------------------ -start_link(SockArgs) -> - {ok, spawn_link(?MODULE, init, [SockArgs])}. +start_link(Conn) -> + {ok, spawn_link(?MODULE, init, [Conn])}. -init(SockArgs = {Transport, _Sock, _SockFun}) -> - {ok, NewSock} = esockd_connection:accept(SockArgs), - loop(Transport, NewSock, state). +init(Conn) -> + {ok, NewConn} = Conn:wait(), + loop(NewConn). -loop(Transport, Sock, State) -> - case Transport:recv(Sock, 0) of +loop(Conn) -> + case Conn:recv(0) of {ok, Data} -> - {ok, PeerName} = Transport:peername(Sock), + {ok, PeerName} = Conn:peername(), io:format("~s - ~s~n", [esockd_net:format(peername, PeerName), Data]), - Transport:send(Sock, Data), - loop(Transport, Sock, State); + Conn:send(Data), + loop(Conn); {error, Reason} -> io:format("tcp ~s~n", [Reason]), {stop, Reason} diff --git a/examples/ssl/src/ssl_echo_server.erl b/examples/ssl/src/ssl_echo_server.erl index 28d13d6..c39a9fe 100644 --- a/examples/ssl/src/ssl_echo_server.erl +++ b/examples/ssl/src/ssl_echo_server.erl @@ -30,7 +30,7 @@ -export([start/0, start/1]). %% callback --export([start_link/1, init/1, loop/3]). +-export([start_link/1, init/1, loop/1]). start() -> start(5000). @@ -49,20 +49,20 @@ start(Port) -> {sockopts, SockOpts}], {ok, _} = esockd:open('echo/ssl', Port, Opts, ssl_echo_server). -start_link(SockArgs) -> - {ok, spawn_link(?MODULE, init, [SockArgs])}. +start_link(Conn) -> + {ok, spawn_link(?MODULE, init, [Conn])}. -init(SockArgs = {Transport, _Sock, _SockFun}) -> - {ok, NewSock} = esockd_connection:accept(SockArgs), - loop(Transport, NewSock, state). +init(Conn) -> + {ok, NewConn} = Conn:wait(), + loop(NewConn). -loop(Transport, Sock, State) -> - case Transport:recv(Sock, 0) of - {ok, Data} -> - {ok, PeerName} = Transport:peername(Sock), +loop(Conn) -> + case Conn:recv(0) of + {ok, Data} -> + {ok, PeerName} = Conn:peername(), io:format("~s - ~p~n", [esockd_net:format(peername, PeerName), Data]), - Transport:send(Sock, Data), - loop(Transport, Sock, State); + Conn:send(Data), + loop(Conn); {error, Reason} -> io:format("tcp ~s~n", [Reason]), {stop, Reason} diff --git a/include/esockd.hrl b/include/esockd.hrl index 5fdae7f..b6d23dd 100644 --- a/include/esockd.hrl +++ b/include/esockd.hrl @@ -20,11 +20,14 @@ %%% SOFTWARE. %%%----------------------------------------------------------------------------- %%% @doc -%%% eSockd header. +%%% eSockd Header. %%% %%% @end %%%----------------------------------------------------------------------------- +%% Extract socket from esockd_connection +-define(ESOCK(Sock), {esockd_connection, [Sock, _, _]}). + %%------------------------------------------------------------------------------ %% SSL Sock Wrapper. %%------------------------------------------------------------------------------ diff --git a/src/esockd.app.src b/src/esockd.app.src index fbf968e..54e5997 100644 --- a/src/esockd.app.src +++ b/src/esockd.app.src @@ -1,7 +1,7 @@ {application, esockd, [ {id, "esockd"}, - {vsn, "2.8.0"}, + {vsn, "3.0"}, {description, "Erlang General Non-blocking TCP/SSL Server"}, {registered, []}, {applications, [ diff --git a/src/esockd.erl b/src/esockd.erl index 3a57856..0247456 100644 --- a/src/esockd.erl +++ b/src/esockd.erl @@ -20,11 +20,10 @@ %%% SOFTWARE. %%%----------------------------------------------------------------------------- %%% @doc -%%% esockd main api. +%%% eSockd main api. %%% %%% @end %%%----------------------------------------------------------------------------- - -module(esockd). -author("Feng Lee "). diff --git a/src/esockd_acceptor.erl b/src/esockd_acceptor.erl index 7280cdf..c4b74d5 100644 --- a/src/esockd_acceptor.erl +++ b/src/esockd_acceptor.erl @@ -151,10 +151,10 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %% accept... %%-------------------------------------------------------------------- -accept(State = #state{lsock=LSock}) -> +accept(State = #state{lsock = LSock}) -> case prim_inet:async_accept(LSock, -1) of {ok, Ref} -> - {noreply, State#state{ref=Ref}}; + {noreply, State#state{ref = Ref}}; {error, Error} -> sockerr(Error, State) end. diff --git a/src/esockd_access.erl b/src/esockd_access.erl index 2a1d61e..e353eae 100644 --- a/src/esockd_access.erl +++ b/src/esockd_access.erl @@ -46,23 +46,23 @@ {deny, all} | {deny, range()}. --export([rule/1, match/2, range/1, mask/1, atoi/1, itoa/1]). +-export([compile/1, match/2, range/1, mask/1, atoi/1, itoa/1]). %%------------------------------------------------------------------------------ -%% @doc Build CIDR, Make rule. +%% @doc Build CIDR, Compile rule. %% @end %%------------------------------------------------------------------------------ --spec rule(rule()) -> range_rule(). -rule({allow, all}) -> +-spec compile(rule()) -> range_rule(). +compile({allow, all}) -> {allow, all}; -rule({allow, CIDR}) when is_list(CIDR) -> - rule(allow, CIDR); -rule({deny, CIDR}) when is_list(CIDR) -> - rule(deny, CIDR); -rule({deny, all}) -> +compile({allow, CIDR}) when is_list(CIDR) -> + compile(allow, CIDR); +compile({deny, CIDR}) when is_list(CIDR) -> + compile(deny, CIDR); +compile({deny, all}) -> {deny, all}. -rule(Type, CIDR) when is_list(CIDR) -> +compile(Type, CIDR) when is_list(CIDR) -> {Start, End} = range(CIDR), {Type, {CIDR, Start, End}}. %%------------------------------------------------------------------------------ diff --git a/src/esockd_connection.erl b/src/esockd_connection.erl index 66fdaad..d582cdc 100644 --- a/src/esockd_connection.erl +++ b/src/esockd_connection.erl @@ -20,7 +20,7 @@ %%% SOFTWARE. %%%----------------------------------------------------------------------------- %%% @doc -%%% eSockd connection api. +%%% eSockd tcp/ssl connection that wraps transport and socket. %%% %%% @end %%%----------------------------------------------------------------------------- @@ -30,62 +30,237 @@ -include("esockd.hrl"). --export([start_link/2, ready/2, accept/1, transform/1]). +-export([new/3, start_link/2, go/2, wait/1, upgrade/1]). + +-export([transport/1, sock/1, opts/1, type/1, getopts/2, setopts/2, getstat/2, + controlling_process/2, peername/1, sockname/1]). + +-export([send/2, async_send/2, recv/2, recv/3, async_recv/2, async_recv/3, + shutdown/2, close/1, fast_close/1]). + +-type parameter() :: any(). + +-type connection() :: {?MODULE, list(parameter())}. + +-export_type([connection/0]). + +-define(CONN_MOD, {?MODULE, [_Sock, _SockFun, _Opts]}). + +-define(CONN_MOD(Sock), {?MODULE, [Sock, _SockFun, _Opts]}). + +-define(Transport, esockd_transport). %%------------------------------------------------------------------------------ -%% @doc Start a connection. +%% @doc Create a connection %% @end %%------------------------------------------------------------------------------ --spec start_link(SockArgs, MFArgs) -> {ok, pid()} | {error, any()} | ignore when - SockArgs :: esockd:sock_args(), - MFArgs :: esockd:mfargs(). -start_link(SockArgs, MFArgs) -> - case call(SockArgs, MFArgs) of - {ok, Pid} -> {ok, Pid}; - ignore -> ignore; - {error, Error} -> {error, Error} - end. +-spec new(Sock, SockFun, Opts) -> connection() when + Sock :: inet:socket(), + SockFun :: esockd:sock_fun(), + Opts :: list(atom()|tuple()). +new(Sock, SockFun, Opts) -> + {?MODULE, [Sock, SockFun, parse_opt(Opts)]}. + +parse_opt(Opts) -> + parse_opt(Opts, []). +parse_opt([], Acc) -> + Acc; +parse_opt([{rate_limit, Str} | Opts], Acc) -> + parse_opt(Opts, [{rate_limit, parse_rl(Str)}|Acc]); +parse_opt([Opt | Opts], Acc) -> + parse_opt(Opts, [Opt | Acc]). +parse_rl(Str) -> + Bps = fun(S) -> list_to_integer(string:strip(S)) * 1024 end, + [Burst, Rate] = [Bps(S) || S <- string:tokens(Str, ",")], + esockd_ratelimit:new(Burst, Rate). %%------------------------------------------------------------------------------ -%% @doc Tell the connection that socket is ready. Called by acceptor. +%% @doc Start the connection process. %% @end %%------------------------------------------------------------------------------ --spec ready(Conn, SockArgs) -> any() when - Conn :: pid(), - SockArgs :: esockd:sock_args(). -ready(Conn, SockArgs = {_Transport, _Sock, _SockFun}) -> - Conn ! {sock_ready, SockArgs}. +-spec start_link(esockd:mfargs(), connection()) -> {ok, pid()} + | {error, any()} + | ignore. +start_link(M, Conn = ?CONN_MOD) when is_atom(M) -> + M:start_link(Conn); + +start_link({M, F}, Conn = ?CONN_MOD) when is_atom(M), is_atom(F) -> + M:F(Conn); + +start_link({M, F, Args}, Conn = ?CONN_MOD) + when is_atom(M), is_atom(F), is_list(Args) -> + erlang:apply(M, F, [Conn|Args]). %%------------------------------------------------------------------------------ -%% @doc Connection accept the socket. Called by connection. +%% @doc Tell the connection proccess that socket is ready. +%% Called by acceptor. %% @end %%------------------------------------------------------------------------------ --spec accept(SockArgs) -> {ok, NewSock} when - SockArgs :: esockd:sock_args(), - NewSock :: inet:socket() | esockd:ssl_socket(). -accept(SockArgs) -> - receive - {sock_ready, SockArgs} -> transform(SockArgs) - end. +-spec go(pid(), connection()) -> any(). +go(Pid, Conn = ?CONN_MOD) -> + Pid ! {go, Conn}. + +%%------------------------------------------------------------------------------ +%% @doc Connection process wait for 'go' and upgrade self. +%% Called by connection process. +%% @end +%%------------------------------------------------------------------------------ +-spec wait(connection()) -> {ok, connection()}. +wait(Conn = ?CONN_MOD) -> + receive {go, Conn} -> upgrade(Conn) end. %%------------------------------------------------------------------------------ -%% @doc Transform Socket. Callbed by connection proccess. +%% @doc Upgrade Socket. +%% Called by connection proccess. %% @end %%------------------------------------------------------------------------------ -transform({_Transport, Sock, SockFun}) -> +-spec upgrade(connection()) -> {ok, connection()}. +upgrade({?MODULE, [Sock, SockFun, Opts]}) -> case SockFun(Sock) of {ok, NewSock} -> - {ok, NewSock}; + {ok, {?MODULE, [NewSock, SockFun, Opts]}}; {error, Error} -> - exit({shutdown, Error}) + erlang:error(Error) end. -call(SockArgs, M) when is_atom(M) -> - M:start_link(SockArgs); +%%------------------------------------------------------------------------------ +%% @doc Transport of the connection. +%% @end +%%------------------------------------------------------------------------------ +-spec transport(connection()) -> atom(). +transport({?MODULE, [_Sock, _SockFun, _Opts]}) -> + ?Transport. + +%%------------------------------------------------------------------------------ +%% @doc Socket of the connection. +%% @end +%%------------------------------------------------------------------------------ +-spec sock(connection()) -> inet:socket() | esockd:ssl_socket(). +sock({?MODULE, [Sock, _SockFun, _Opts]}) -> + Sock. + +%%------------------------------------------------------------------------------ +%% @doc Connection options +%% @end +%%------------------------------------------------------------------------------ +-spec opts(connection()) -> list(atom() | tuple()). +opts({?MODULE, [_Sock, _SockFun, Opts]}) -> + Opts. -call(SockArgs, {M, F}) when is_atom(M), is_atom(F) -> - M:F(SockArgs); +%%------------------------------------------------------------------------------ +%% @doc Socket type of the connection. +%% @end +%%------------------------------------------------------------------------------ +-spec type(connection()) -> tcp | ssl. +type(?CONN_MOD(Sock)) -> + ?Transport:type(Sock). + +%%------------------------------------------------------------------------------ +%% @doc Sockname of the connection. +%% @end +%%------------------------------------------------------------------------------ +-spec sockname(connection()) -> {ok, {Address, Port}} | {error, any()} when + Address :: inet:ip_address(), + Port :: inet:port_number(). +sockname(?CONN_MOD(Sock)) -> + ?Transport:sockname(Sock). + +%%------------------------------------------------------------------------------ +%% @doc Peername of the connection. +%% @end +%%------------------------------------------------------------------------------ +-spec peername(connection()) -> {ok, {Address, Port}} | {error, any()} when + Address :: inet:ip_address(), + Port :: inet:port_number(). +peername(?CONN_MOD(Sock)) -> + ?Transport:peername(Sock). + +%%------------------------------------------------------------------------------ +%% @doc Get socket options +%% @end +%%------------------------------------------------------------------------------ +getopts(Keys, ?CONN_MOD(Sock)) -> + ?Transport:getopts(Sock, Keys). -call(SockArgs, {M, F, Args}) when is_atom(M), is_atom(F) -> - erlang:apply(M, F, [SockArgs|Args]). +%%------------------------------------------------------------------------------ +%% @doc Set socket options +%% @end +%%------------------------------------------------------------------------------ +setopts(Options, ?CONN_MOD(Sock)) -> + ?Transport:setopts(Sock, Options). + +%%------------------------------------------------------------------------------ +%% @doc Get socket stats +%% @end +%%------------------------------------------------------------------------------ +getstat(Stats, ?CONN_MOD(Sock)) -> + ?Transport:getstat(Sock, Stats). + +%%------------------------------------------------------------------------------ +%% @doc Controlling Process of Connection +%% @end +%%------------------------------------------------------------------------------ +-spec controlling_process(pid(), connection()) -> any(). +controlling_process(Owner, ?CONN_MOD(Sock)) -> + ?Transport:controlling_process(Sock, Owner). + +%%------------------------------------------------------------------------------ +%% @doc Send data +%% @end +%%------------------------------------------------------------------------------ +-spec send(iolist(), connection()) -> ok. +send(Data, ?CONN_MOD(Sock)) -> + ?Transport:send(Sock, Data). + +%%------------------------------------------------------------------------------ +%% @doc Send data asynchronously by port_command/2 +%% @end +%%------------------------------------------------------------------------------ +async_send(Data, ?CONN_MOD(Sock)) -> + ?Transport:port_command(Sock, Data). + +%%------------------------------------------------------------------------------ +%% @doc Receive data +%% @end +%%------------------------------------------------------------------------------ +recv(Length, ?CONN_MOD(Sock)) -> + ?Transport:recv(Sock, Length). + +recv(Length, Timeout, ?CONN_MOD(Sock)) -> + ?Transport:recv(Sock, Length, Timeout). + +%%------------------------------------------------------------------------------ +%% @doc Receive data asynchronously +%% @end +%%------------------------------------------------------------------------------ +async_recv(Length, ?CONN_MOD(Sock)) -> + ?Transport:async_recv(Sock, Length, infinity). + +async_recv(Length, Timeout, ?CONN_MOD(Sock)) -> + ?Transport:async_recv(Sock, Length, Timeout). + +%%------------------------------------------------------------------------------ +%% @doc Shutdown connection +%% @end +%%------------------------------------------------------------------------------ +-spec shutdown(How, connection()) -> ok | {error, Reason :: any()} when + How :: read | write | read_write. +shutdown(How, ?CONN_MOD(Sock)) -> + ?Transport:shutdown(Sock, How). + +%%------------------------------------------------------------------------------ +%% @doc Close socket +%% @end +%%------------------------------------------------------------------------------ +-spec close(connection()) -> ok. +close(?CONN_MOD(Sock)) -> + ?Transport:close(Sock). + +%%------------------------------------------------------------------------------ +%% @doc Close socket by port_close +%% @end +%%------------------------------------------------------------------------------ +-spec fast_close(connection()) -> ok. +fast_close(?CONN_MOD(Sock)) -> + ?Transport:fast_close(Sock). diff --git a/src/esockd_connection_sup.erl b/src/esockd_connection_sup.erl index 7e18498..f3fc197 100644 --- a/src/esockd_connection_sup.erl +++ b/src/esockd_connection_sup.erl @@ -46,7 +46,7 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3]). -define(DICT, dict). -define(SETS, sets). @@ -54,8 +54,9 @@ -record(state, {curr_clients = 0, max_clients = ?MAX_CLIENTS, + conn_opts = [], access_rules = [], - shutdown = brutal_kill, + shutdown = brutal_kill, mfargs, logger}). @@ -79,13 +80,12 @@ start_link(Options, MFArgs, Logger) -> %% @end %%------------------------------------------------------------------------------ start_connection(Sup, Mod, Sock, SockFun) -> - SockArgs = {esockd_transport, Sock, SockFun}, - case call(Sup, {start_connection, SockArgs}) of - {ok, ConnPid} -> + case call(Sup, {start_connection, Sock, SockFun}) of + {ok, Pid, Conn} -> % transfer controlling from acceptor to connection - Mod:controlling_process(Sock, ConnPid), - esockd_connection:ready(ConnPid, SockArgs), - {ok, ConnPid}; + Mod:controlling_process(Sock, Pid), + Conn:go(Pid), + {ok, Pid}; {error, Error} -> {error, Error} end. @@ -120,31 +120,35 @@ call(Sup, Req) -> init([Options, MFArgs, Logger]) -> process_flag(trap_exit, true), - Shutdown = proplists:get_value(shutdown, Options, brutal_kill), - MaxClients = proplists:get_value(max_clients, Options, ?MAX_CLIENTS), - AccessRules = [esockd_access:rule(Rule) || - Rule <- proplists:get_value(access, Options, [{allow, all}])], + Shutdown = proplists:get_value(shutdown, Options, brutal_kill), + MaxClients = proplists:get_value(max_clients, Options, ?MAX_CLIENTS), + ConnOpts = proplists:get_value(connopts, Options, []), + RawRules = proplists:get_value(access, Options, [{allow, all}]), + AccessRules = [esockd_access:compile(Rule) || Rule <- RawRules], {ok, #state{max_clients = MaxClients, + conn_opts = ConnOpts, access_rules = AccessRules, shutdown = Shutdown, mfargs = MFArgs, logger = Logger}}. -handle_call({start_connection, _SockArgs}, _From, State = #state{curr_clients = CurrClients, - max_clients = MaxClients}) +handle_call({start_connection, _Sock, _SockFun}, _From, + State = #state{curr_clients = CurrClients, max_clients = MaxClients}) when CurrClients >= MaxClients -> {reply, {error, maxlimit}, State}; -handle_call({start_connection, SockArgs = {_, Sock, _SockFun}}, _From, - State = #state{mfargs = MFArgs, curr_clients = Count, access_rules = Rules}) -> +handle_call({start_connection, Sock, SockFun}, _From, + State = #state{conn_opts = ConnOpts, mfargs = MFArgs, + curr_clients = Count, access_rules = Rules}) -> case inet:peername(Sock) of {ok, {Addr, _Port}} -> case allowed(Addr, Rules) of true -> - case catch esockd_connection:start_link(SockArgs, MFArgs) of + Conn = esockd_connection:new(Sock, SockFun, ConnOpts), + case catch Conn:start_link(MFArgs) of {ok, Pid} when is_pid(Pid) -> put(Pid, true), - {reply, {ok, Pid}, State#state{curr_clients = Count+1}}; + {reply, {ok, Pid, Conn}, State#state{curr_clients = Count+1}}; ignore -> {reply, ignore, State}; {error, Reason} -> @@ -175,7 +179,7 @@ handle_call(access_rules, _From, State = #state{access_rules = Rules}) -> {reply, [raw(Rule) || Rule <- Rules], State}; handle_call({add_rule, RawRule}, _From, State = #state{access_rules = Rules}) -> - case catch esockd_access:rule(RawRule) of + case catch esockd_access:compile(RawRule) of {'EXIT', _Error} -> {reply, {error, bad_access_rule}, State}; Rule -> diff --git a/src/esockd_gen.erl b/src/esockd_gen.erl new file mode 100644 index 0000000..d6d9de4 --- /dev/null +++ b/src/esockd_gen.erl @@ -0,0 +1,44 @@ +%%%----------------------------------------------------------------------------- +%%% Copyright (c) 2014-2015 eMQTT.IO, All Rights Reserved. +%%% +%%% Permission is hereby granted, free of charge, to any person obtaining a copy +%%% of this software and associated documentation files (the "Software"), to deal +%%% in the Software without restriction, including without limitation the rights +%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +%%% copies of the Software, and to permit persons to whom the Software is +%%% furnished to do so, subject to the following conditions: +%%% +%%% The above copyright notice and this permission notice shall be included in all +%%% copies or substantial portions of the Software. +%%% +%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +%%% SOFTWARE. +%%%----------------------------------------------------------------------------- +%%% @doc +%%% eSockd general functions. +%%% +%%% @end +%%%----------------------------------------------------------------------------- +-module(esockd_gen). + +-export([send_fun/1, async_send_fun/1]). + +-spec send_fun(esockd_connection:connection()) -> fun(). +send_fun(Connection) -> + fun(Data) -> Connection:send(Data) end. + +-spec async_send_fun(esockd_connection:connection()) -> fun(). +async_send_fun(Connection) -> + fun(Data) -> + try Connection:async_send(Data) of + true -> ok + catch + error:Error -> exit({shutdown, Error}) + end + end. + diff --git a/src/esockd_keepalive.erl b/src/esockd_keepalive.erl index 0c12b85..e7d1775 100644 --- a/src/esockd_keepalive.erl +++ b/src/esockd_keepalive.erl @@ -30,10 +30,9 @@ -export([start/3, resume/1, cancel/1]). --record(keepalive, {transport, - socket, +-record(keepalive, {connection, recv_oct, - timeout_sec, + timeout_msec, timeout_msg, timer_ref}). @@ -43,18 +42,17 @@ %% @doc Start keepalive %% @end %%------------------------------------------------------------------------------ --spec start({Transport, Socket}, pos_integer(), any()) -> {ok, keepalive()} | {error, any()} when - Transport :: module(), - Socket :: inet:socket() | esockd:ssl_socket(). -start({Transport, Socket}, TimeoutSec, TimeoutMsg) when TimeoutSec > 0 -> - with_sock_stats(Transport, Socket, fun(RecvOct) -> - Ref = erlang:send_after(TimeoutSec*1000, self(), TimeoutMsg), - {ok, #keepalive {transport = Transport, - socket = Socket, - recv_oct = RecvOct, - timeout_sec = TimeoutSec, - timeout_msg = TimeoutMsg, - timer_ref = Ref}} +-spec start(esockd_connection:connection(), pos_integer(), any()) -> + {ok, keepalive()} | {error, any()}. +start(Connection, TimeoutSec, TimeoutMsg) when TimeoutSec > 0 -> + with_sock_stats(Connection, fun(RecvOct) -> + Ms = timer:seconds(TimeoutSec), + Ref = erlang:send_after(Ms, self(), TimeoutMsg), + {ok, #keepalive {connection = Connection, + recv_oct = RecvOct, + timeout_msec = Ms, + timeout_msg = TimeoutMsg, + timer_ref = Ref}} end). %%------------------------------------------------------------------------------ @@ -62,17 +60,16 @@ start({Transport, Socket}, TimeoutSec, TimeoutMsg) when TimeoutSec > 0 -> %% @end %%------------------------------------------------------------------------------ -spec resume(keepalive()) -> timeout | {resumed, keepalive()}. -resume(KeepAlive = #keepalive {transport = Transport, - socket = Socket, - recv_oct = RecvOct, - timeout_sec = TimeoutSec, - timeout_msg = TimeoutMsg, - timer_ref = Ref}) -> - with_sock_stats(Transport, Socket, fun(NewRecvOct) -> +resume(KeepAlive = #keepalive {connection = Connection, + recv_oct = RecvOct, + timeout_msec = Ms, + timeout_msg = TimeoutMsg, + timer_ref = Ref}) -> + with_sock_stats(Connection, fun(NewRecvOct) -> case NewRecvOct =:= RecvOct of false -> cancel(Ref), %% need? - NewRef = erlang:send_after(TimeoutSec*1000, self(), TimeoutMsg), + NewRef = erlang:send_after(Ms, self(), TimeoutMsg), {resumed, KeepAlive#keepalive{recv_oct = NewRecvOct, timer_ref = NewRef}}; true -> {error, timeout} @@ -91,8 +88,8 @@ cancel(undefined) -> cancel(Ref) -> catch erlang:cancel_timer(Ref). -with_sock_stats(Transport, Socket, SuccFun) -> - case Transport:getstat(Socket, [recv_oct]) of +with_sock_stats(Connection, SuccFun) -> + case Connection:getstat([recv_oct]) of {ok, [{recv_oct, RecvOct}]} -> SuccFun(RecvOct); {error, Error} -> diff --git a/src/esockd_net.erl b/src/esockd_net.erl index 5547027..1fb2379 100644 --- a/src/esockd_net.erl +++ b/src/esockd_net.erl @@ -30,7 +30,7 @@ -include_lib("kernel/include/inet.hrl"). --export([ntoab/1, tcp_host/1, hostname/0, format/2]). +-export([ntoab/1, tcp_host/1, hostname/0, format/1, format/2]). tcp_host({0,0,0,0}) -> hostname(); @@ -56,7 +56,7 @@ format(sockname, SockName) -> format(peername, PeerName) -> format(PeerName). format({Addr, Port}) -> - lists:flatten(io_lib:format("~s:~p", [maybe_ntoab(Addr), Port])). + io_lib:format("~s:~p", [maybe_ntoab(Addr), Port]). maybe_ntoab(Addr) when is_tuple(Addr) -> ntoab(Addr); @@ -76,4 +76,3 @@ ntoab(IP) -> _ -> "[" ++ Str ++ "]" end. - diff --git a/src/esockd_ratelimit.erl b/src/esockd_ratelimit.erl index d2ce29e..a445bee 100644 --- a/src/esockd_ratelimit.erl +++ b/src/esockd_ratelimit.erl @@ -41,30 +41,37 @@ -type bucket() :: #bucket{}. +-type ratelimit() :: {?MODULE, [bucket()]}. + +-export_type([ratelimit/0]). + %%------------------------------------------------------------------------------ %% @doc Create rate limiter bucket. %% @end %%------------------------------------------------------------------------------ --spec new(pos_integer(), pos_integer()) -> bucket(). +-spec new(pos_integer(), pos_integer()) -> ratelimit(). new(Capacity, Rate) when Capacity > Rate andalso Rate > 0 -> - #bucket{capacity = Capacity, remaining = Capacity, - limitrate = Rate/1000, lastime = now_ms()}. + Bucket = #bucket{capacity = Capacity, remaining = Capacity, + limitrate = Rate/1000, lastime = now_ms()}, + {?MODULE, [Bucket]}. %%------------------------------------------------------------------------------ %% @doc Check inflow bytes. %% @end %%------------------------------------------------------------------------------ --spec check(bucket(), pos_integer()) -> {non_neg_integer(), bucket()}. -check(Bucket = #bucket{capacity = Capacity, remaining = Remaining, - limitrate = Rate, lastime = Lastime}, Bytes) -> +-spec check(bucket(), pos_integer()) -> {non_neg_integer(), ratelimit()}. +check(Bytes, {?MODULE, [Bucket = #bucket{capacity = Capacity, remaining = Remaining, + limitrate = Rate, lastime = Lastime}]}) -> Tokens = lists:min([Capacity, Remaining + round(Rate * (now_ms() - Lastime))]), + {Pause1, NewBucket} = case Tokens >= Bytes of true -> %% Tokens available {0, Bucket#bucket{remaining = Tokens - Bytes, lastime = now_ms()}}; false -> %% Tokens not enough Pause = round((Bytes - Tokens)/Rate), {Pause, Bucket#bucket{remaining = 0, lastime = now_ms() + Pause}} - end. + end, + {Pause1, {?MODULE, [NewBucket]}}. now_ms() -> {MegaSecs, Secs, MicroSecs} = os:timestamp(), diff --git a/src/esockd_transport.erl b/src/esockd_transport.erl index 64c4587..845d7fa 100644 --- a/src/esockd_transport.erl +++ b/src/esockd_transport.erl @@ -34,7 +34,8 @@ -export([type/1]). -export([listen/2, send/2, port_command/2, - recv/2, recv/3, async_recv/3, + recv/2, recv/3, + async_recv/2, async_recv/3, controlling_process/2, close/1, fast_close/1]). @@ -173,6 +174,13 @@ recv(#ssl_socket{ssl = SslSock}, Length, Timeout) -> %% @doc Async Receive data %% @end %%------------------------------------------------------------------------------ +-spec async_recv(Sock, Length) -> {ok, Ref} when + Sock :: inet:socket() | esockd:ssl_socket(), + Length :: non_neg_integer(), + Ref :: reference(). +async_recv(Sock, Length) -> + async_recv(Sock, Length, infinity). + -spec async_recv(Sock, Length, Timeout) -> {ok, Ref} when Sock :: inet:socket() | esockd:ssl_socket(), Length :: non_neg_integer(), diff --git a/test/esockd_access_tests.erl b/test/esockd_access_tests.erl index a3605b1..22ed1b7 100644 --- a/test/esockd_access_tests.erl +++ b/test/esockd_access_tests.erl @@ -30,7 +30,7 @@ -include_lib("eunit/include/eunit.hrl"). --import(esockd_access, [atoi/1, itoa/1, rule/1, range/1, match/2, mask/1]). +-import(esockd_access, [atoi/1, itoa/1, compile/1, range/1, match/2, mask/1]). atoi_test() -> IpList = [{192,168,1,1}, {10,10,10,10}, {8, 8, 8,8}, {255,255,255,0}], @@ -52,21 +52,21 @@ range_test() -> ?assertEqual({10,10,255,255}, itoa(End1)). match_test() -> - Rules = [rule({deny, "192.168.1.1"}), - rule({allow, "192.168.1.0/24"}), - rule({deny, all})], + Rules = [compile({deny, "192.168.1.1"}), + compile({allow, "192.168.1.0/24"}), + compile({deny, all})], ?assertEqual({matched, deny}, match({192,168,1,1}, Rules)), ?assertEqual({matched, allow}, match({192,168,1,4}, Rules)), ?assertEqual({matched, allow}, match({192,168,1,60}, Rules)), ?assertEqual({matched, deny}, match({10,10,10,10}, Rules)). match_local_test() -> - Rules = [rule({allow, "127.0.0.1"}), rule({deny, all})], + Rules = [compile({allow, "127.0.0.1"}), compile({deny, all})], ?assertEqual({matched, allow}, match({127,0,0,1}, Rules)), ?assertEqual({matched, deny}, match({192,168,0,1}, Rules)). match_allow_test() -> - Rules = [rule({deny, "10.10.0.0/16"}), rule({allow, all})], + Rules = [compile({deny, "10.10.0.0/16"}), compile({allow, all})], ?assertEqual({matched, deny}, match({10,10,0,10}, Rules)), ?assertEqual({matched, allow}, match({127,0,0,1}, Rules)), ?assertEqual({matched, allow}, match({192,168,0,1}, Rules)). diff --git a/test/esockd_rate_limiter_tests.erl b/test/esockd_ratelimit_tests.erl similarity index 75% rename from test/esockd_rate_limiter_tests.erl rename to test/esockd_ratelimit_tests.erl index 664ea50..3c8a0fe 100644 --- a/test/esockd_rate_limiter_tests.erl +++ b/test/esockd_ratelimit_tests.erl @@ -1,15 +1,15 @@ --module(esockd_rate_limiter_tests). +-module(esockd_ratelimit_tests). -export([run/1]). run(Bytes) -> - Rl = esockd_rate_limiter:new(10, 1), + Rl = esockd_ratelimit:new(10, 1), timer:tc(fun run/2, [Rl, Bytes]). run(Rl, Bytes) -> lists:foldl(fun(_I, Rl0) -> - case esockd_rate_limiter:check(Rl0, Bytes) of + case Rl0:check(Bytes) of {0, Rl1} -> io:format("~p~n", [Rl1]), timer:sleep(1000), Rl1;