From 581b6954d344eaca2bfcdab663019796e852c778 Mon Sep 17 00:00:00 2001 From: Jan Uhlig Date: Wed, 1 Sep 2021 14:01:47 +0200 Subject: Update docs and modernize examples * Use the map form for transport options everywhere * Remove mentions of the list form for transport options * Use a state enter call instead of gen_statem:enter_loop/4 and proc_lib:start_link/3 in the tcp_reverse example * Take care of different EOLs in the tcp_reverse example * Mention state enter calls, the next_event action, and {continue, ...} in the docs for how to use gen_statem and gen_server --- doc/src/guide/listeners.asciidoc | 25 +++++++------- doc/src/guide/protocols.asciidoc | 30 ++++++++++++----- doc/src/guide/ssl_auth.asciidoc | 4 +-- doc/src/manual/ranch.asciidoc | 5 --- doc/src/manual/ranch.child_spec.asciidoc | 3 -- .../manual/ranch.get_transport_options.asciidoc | 5 --- examples/tcp_reverse/src/reverse_protocol.erl | 38 ++++++++++++++-------- 7 files changed, 61 insertions(+), 49 deletions(-) diff --git a/doc/src/guide/listeners.asciidoc b/doc/src/guide/listeners.asciidoc index 779497c..3e7254d 100644 --- a/doc/src/guide/listeners.asciidoc +++ b/doc/src/guide/listeners.asciidoc @@ -19,7 +19,6 @@ A listener can be started and stopped at will. When starting a listener, a number of different settings are required: * A name to identify it locally and be able to interact with it. -* The number of acceptors in the pool. * A transport handler and its associated options. * A protocol handler and its associated options. @@ -43,8 +42,8 @@ to the `echo_protocol` handler. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, [{port, 5555}], - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 5555}]}, + echo_protocol, [] ). You can try this out by compiling and running the `tcp_echo` example in the @@ -142,8 +141,8 @@ argument is the name of the listener you gave in `ranch:start_listener/5`. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, [{port, 0}], - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 0}]}, + echo_protocol, [] ). Port = ranch:get_port(tcp_echo). @@ -238,8 +237,8 @@ overloaded and ensuring all the connections are handled optimally. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, #{socket_opts => [{port, 5555}], max_connections => 100}, - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 5555}], max_connections => 100}, + echo_protocol, [] ). You can disable this limit by setting its value to the atom `infinity`. @@ -248,8 +247,8 @@ You can disable this limit by setting its value to the atom `infinity`. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, #{socket_opts => [{port, 5555}], max_connections => infinity}, - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 5555}], max_connections => infinity}, + echo_protocol, [] ). The maximum number of connections is a soft limit. In practice, it @@ -305,8 +304,8 @@ measure to find the best value for your application. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, [{port, 5555}, {num_acceptors, 42}], - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 5555}], num_acceptors => 42}, + echo_protocol, [] ). === Customizing the number of connection supervisors @@ -325,8 +324,8 @@ processes. [source,erlang] {ok, _} = ranch:start_listener(tcp_echo, - ranch_tcp, #{socket_opts => [{port, 5555}], num_conns_sups => 42}], - echo_protocol, [] + ranch_tcp, #{socket_opts => [{port, 5555}], num_conns_sups => 42}, + echo_protocol, [] ). === When running out of file descriptors diff --git a/doc/src/guide/protocols.asciidoc b/doc/src/guide/protocols.asciidoc index 73a0bf5..8f55cea 100644 --- a/doc/src/guide/protocols.asciidoc +++ b/doc/src/guide/protocols.asciidoc @@ -59,7 +59,7 @@ loop(Socket, Transport) -> end. ---- -=== Using gen_statem +=== Using gen_statem and gen_server Special processes like the ones that use the `gen_statem` or `gen_server` behaviours have the particularity of having their `start_link` call not @@ -67,12 +67,21 @@ return until the `init` function returns. This is problematic, because you won't be able to call `ranch:handshake/1,2` from the `init` callback as this would cause a deadlock to happen. -Use the `gen_statem:enter_loop/4` function. It allows you to start your process -normally (although it must be started with `proc_lib` like all special -processes), then perform any needed operations before falling back into -the normal `gen_statem` execution loop. +This problem can be addressed in several ways. -.Use a gen_statem for protocol handling +==== gen_statem + +* Use state enter calls and place the `ranch:handshake/1,2` call in the enter + clause of the initial state. Check the `tcp_reverse` example for a complete + example. +* Use a `next_event` action in the return from `init/1` and place the + `ranch:handshake/1,2` call in the clause handling the event in the initial + state. +* Use the `gen_statem:enter_loop/4` function and start your process with + `proc_lib:spawn_link/3` or `proc_lib:start_link/3,4,5`. See below for an + example. + +.Using gen_statem:enter_loop/4 to start a protocol [source,erlang] ---- @@ -87,7 +96,7 @@ the normal `gen_statem` execution loop. start_link(Ref, Transport, Opts) -> {ok, proc_lib:spawn_link(?MODULE, init, [{Ref, Transport, Opts}])}. -init({Ref, Transport, _Opts = []}) -> +init({Ref, Transport, _Opts}) -> %% Perform any required state initialization here. {ok, Socket} = ranch:handshake(Ref), ok = Transport:setopts(Socket, [{active, once}]), @@ -96,4 +105,9 @@ init({Ref, Transport, _Opts = []}) -> %% Other gen_statem callbacks here. ---- -Check the `tcp_reverse` example for a complete example. +==== gen_server + +* Use `{continue, Continue}` in the return from `init/1` and place the + `ranch:handshake/1,2` call in a corresponding `handle_continue/2` clause. +* Use the `gen_server:enter_loop/3` function and start your process with + `proc_lib:spawn_link/3` or `proc_lib:start_link/3,4,5`. diff --git a/doc/src/guide/ssl_auth.asciidoc b/doc/src/guide/ssl_auth.asciidoc index de16107..1ae91e4 100644 --- a/doc/src/guide/ssl_auth.asciidoc +++ b/doc/src/guide/ssl_auth.asciidoc @@ -50,12 +50,12 @@ the listener to enable this behavior. [source,erlang] {ok, _} = ranch:start_listener(my_ssl, - ranch_ssl, [ + ranch_ssl, #{socket_opts => [ {port, SSLPort}, {certfile, PathToCertfile}, {cacertfile, PathToCACertfile}, {verify, verify_peer} - ], + ]}, my_protocol, [] ). diff --git a/doc/src/manual/ranch.asciidoc b/doc/src/manual/ranch.asciidoc index 6dffb7f..26e0769 100644 --- a/doc/src/manual/ranch.asciidoc +++ b/doc/src/manual/ranch.asciidoc @@ -74,11 +74,6 @@ opts() = any() | transport_opts(any()) Transport or socket options. -It is possible to give the full transport options in a map -(see `transport_opts(SocketOpts)`), or only the socket options -(assuming they are not a map and no Ranch-specific option -needs to be given). - === ref() [source,erlang] diff --git a/doc/src/manual/ranch.child_spec.asciidoc b/doc/src/manual/ranch.child_spec.asciidoc index bd6c056..a904217 100644 --- a/doc/src/manual/ranch.child_spec.asciidoc +++ b/doc/src/manual/ranch.child_spec.asciidoc @@ -49,9 +49,6 @@ Transport options include the Ranch-specific options and the socket options. The listener's port number must be defined in the socket options. + -Socket options may be given directly if there are no -Ranch-specific options. -+ The available options for the built-in Ranch transports are documented in the link:man:ranch_tcp(3)[ranch_tcp(3)] and link:man:ranch_ssl(3)[ranch_ssl(3)] manuals. diff --git a/doc/src/manual/ranch.get_transport_options.asciidoc b/doc/src/manual/ranch.get_transport_options.asciidoc index 0214e0f..9b311da 100644 --- a/doc/src/manual/ranch.get_transport_options.asciidoc +++ b/doc/src/manual/ranch.get_transport_options.asciidoc @@ -24,11 +24,6 @@ The listener name. The current transport options are returned. -If the transport options were specified as only socket options (not a map) using -link:man:ranch:start_listener(3)[ranch:start_listener(3)] or -link:man:ranch:set_transport_options(3)[ranch:set_transport_options(3)], they -are returned as `#{socket_opts => SocketOpts}`. - == Examples .Get the current transport options diff --git a/examples/tcp_reverse/src/reverse_protocol.erl b/examples/tcp_reverse/src/reverse_protocol.erl index d274b3a..73d7aff 100644 --- a/examples/tcp_reverse/src/reverse_protocol.erl +++ b/examples/tcp_reverse/src/reverse_protocol.erl @@ -16,28 +16,29 @@ -define(TIMEOUT, 60000). --record(state, {socket, transport}). +-record(state, {ref, transport, socket}). %% API. start_link(Ref, Transport, Opts) -> - {ok, proc_lib:spawn_link(?MODULE, init, [{Ref, Transport, Opts}])}. + gen_statem:start_link(?MODULE, {Ref, Transport, Opts}, []). %% gen_statem. callback_mode() -> - state_functions. + [state_functions, state_enter]. init({Ref, Transport, _Opts = []}) -> + {ok, connected, #state{ref=Ref, transport=Transport}, ?TIMEOUT}. + +connected(enter, connected, StateData=#state{ + ref=Ref, transport=Transport}) -> {ok, Socket} = ranch:handshake(Ref), ok = Transport:setopts(Socket, [{active, once}, {packet, line}]), - gen_statem:enter_loop(?MODULE, [], connected, - #state{socket=Socket, transport=Transport}, - [?TIMEOUT]). - + {keep_state, StateData#state{socket=Socket}}; connected(info, {tcp, Socket, Data}, _StateData=#state{ socket=Socket, transport=Transport}) - when byte_size(Data) > 1 -> + when byte_size(Data) >= 1 -> Transport:setopts(Socket, [{active, once}]), Transport:send(Socket, reverse_binary(Data)), {keep_state_and_data, ?TIMEOUT}; @@ -57,7 +58,7 @@ connected(_EventType, _Msg, _StateData) -> terminate(Reason, StateName, StateData=#state{ socket=Socket, transport=Transport}) - when Socket=/=undefined andalso Transport=/=undefined -> + when Socket=/=undefined, Transport=/=undefined -> catch Transport:close(Socket), terminate(Reason, StateName, StateData#state{socket=undefined, transport=undefined}); @@ -69,7 +70,18 @@ code_change(_OldVsn, StateName, StateData, _Extra) -> %% Internal. -reverse_binary(B) when is_binary(B) -> - [list_to_binary(lists:reverse(binary_to_list( - binary:part(B, {0, byte_size(B)-2}) - ))), "\r\n"]. +reverse_binary(B0) when is_binary(B0) -> + Size = bit_size(B0), + <> = B0, + case <> of + %% Take care of different possible line terminators. + <<$\n, $\r, B2/binary>> -> + %% CR/LF (Windows) + <>; + <<$\n, B2/binary>> -> + %% LF (Linux, Mac OS X and later) + <>; + <<$\r, B2/binary>> -> + %% CR (Mac Classic, ie prior to Mac OS X) + <> + end. -- cgit v1.2.3