summaryrefslogtreecommitdiffstats
path: root/docs/en/ranch/2.2/guide/protocols.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'docs/en/ranch/2.2/guide/protocols.asciidoc')
-rw-r--r--docs/en/ranch/2.2/guide/protocols.asciidoc113
1 files changed, 113 insertions, 0 deletions
diff --git a/docs/en/ranch/2.2/guide/protocols.asciidoc b/docs/en/ranch/2.2/guide/protocols.asciidoc
new file mode 100644
index 00000000..8f55cea2
--- /dev/null
+++ b/docs/en/ranch/2.2/guide/protocols.asciidoc
@@ -0,0 +1,113 @@
+== Protocols
+
+A protocol handler starts a connection process and defines the
+protocol logic executed in this process.
+
+=== Writing a protocol handler
+
+All protocol handlers must implement the `ranch_protocol` behavior
+which defines a single callback, `start_link/3`. This callback is
+responsible for spawning a new process for handling the connection.
+It receives three arguments: the name of the listener, the
+transport handler being used and the protocol options defined in
+the call to `ranch:start_listener/5`. This callback must
+return `{ok, Pid}`, with `Pid` the pid of the new process.
+
+The newly started process can then freely initialize itself. However,
+it must call `ranch:handshake/1,2` before doing any socket operation.
+This will ensure the connection process is the owner of the socket.
+It expects the listener's name as argument.
+
+.Perform the socket handshake
+
+[source,erlang]
+{ok, Socket} = ranch:handshake(Ref).
+
+If your protocol code requires specific socket options, you should
+set them while initializing your connection process, after
+calling `ranch:handshake/1,2`. You can use `Transport:setopts/2`
+for that purpose.
+
+Following is the complete protocol code for the example found
+in `examples/tcp_echo/`.
+
+.Protocol module that echoes everything it receives
+
+[source,erlang]
+----
+-module(echo_protocol).
+-behaviour(ranch_protocol).
+
+-export([start_link/3]).
+-export([init/3]).
+
+start_link(Ref, Transport, Opts) ->
+ Pid = spawn_link(?MODULE, init, [Ref, Transport, Opts]),
+ {ok, Pid}.
+
+init(Ref, Transport, _Opts = []) ->
+ {ok, Socket} = ranch:handshake(Ref),
+ loop(Socket, Transport).
+
+loop(Socket, Transport) ->
+ case Transport:recv(Socket, 0, 5000) of
+ {ok, Data} ->
+ Transport:send(Socket, Data),
+ loop(Socket, Transport);
+ _ ->
+ ok = Transport:close(Socket)
+ end.
+----
+
+=== 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
+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.
+
+This problem can be addressed in several ways.
+
+==== 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]
+----
+-module(my_protocol).
+-behaviour(gen_statem).
+-behaviour(ranch_protocol).
+
+-export([start_link/3]).
+-export([init/1]).
+%% Exports of other gen_statem callbacks here.
+
+start_link(Ref, Transport, Opts) ->
+ {ok, proc_lib:spawn_link(?MODULE, 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}]),
+ gen_statem:enter_loop(?MODULE, [], state_name, {state_data, Socket, Transport}).
+
+%% Other gen_statem callbacks here.
+----
+
+==== 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`.