aboutsummaryrefslogtreecommitdiffstats
path: root/guide/loop_handlers.md
diff options
context:
space:
mode:
Diffstat (limited to 'guide/loop_handlers.md')
-rw-r--r--guide/loop_handlers.md142
1 files changed, 114 insertions, 28 deletions
diff --git a/guide/loop_handlers.md b/guide/loop_handlers.md
index c3d1891..8689453 100644
--- a/guide/loop_handlers.md
+++ b/guide/loop_handlers.md
@@ -1,24 +1,11 @@
Loop handlers
=============
-Purpose
--------
-
Loop handlers are a special kind of HTTP handlers used when the
response can not be sent right away. The handler enters instead
a receive loop waiting for the right message before it can send
a response.
-They are most useful when performing long-polling operations or
-when using server-sent events.
-
-While the same can be accomplished using plain HTTP handlers,
-it is recommended to use loop handlers because they are well-tested
-and allow using built-in features like hibernation and timeouts.
-
-Usage
------
-
Loop handlers are used for requests where a response might not
be immediately available, but where you would like to keep the
connection open for a while in case the response arrives. The
@@ -29,34 +16,133 @@ partially available and you need to stream the response body
while the connection is open. The most known example of such
practice is known as server-sent events.
+While the same can be accomplished using plain HTTP handlers,
+it is recommended to use loop handlers because they are well-tested
+and allow using built-in features like hibernation and timeouts.
+
Loop handlers essentially wait for one or more Erlang messages
and feed these messages to the `info/3` callback. It also features
the `init/3` and `terminate/3` callbacks which work the same as
for plain HTTP handlers.
-The following handler waits for a message `{reply, Body}` before
-sending a response. If this message doesn't arrive within 60
-seconds, it gives up and a `204 No Content` will be replied.
-It also hibernates the process to save memory while waiting for
-this message.
+Initialization
+--------------
+
+The `init/3` function must return a `loop` tuple to enable
+loop handler behavior. This tuple may optionally contain
+a timeout value and/or the atom `hibernate` to make the
+process enter hibernation until a message is received.
+
+This snippet enables the loop handler.
+
+``` erlang
+init(_Type, Req, _Opts) ->
+ {loop, Req, undefined_state}.
+```
+
+However it is largely recommended that you set a timeout
+value. The next example sets a timeout value of 30s and
+also makes the process hibernate.
``` erlang
--module(my_loop_handler).
--behaviour(cowboy_loop_handler).
+init(_Type, Req, _Opts) ->
+ {loop, Req, undefined_state, 30000, hibernate}.
+```
--export([init/3]).
--export([info/3]).
--export([terminate/3]).
+Receive loop
+------------
-init({tcp, http}, Req, Opts) ->
- {loop, Req, undefined_state, 60000, hibernate}.
+Once initialized, Cowboy will wait for messages to arrive
+in the process' mailbox. When a message arrives, Cowboy
+calls the `info/3` function with the message, the Req object
+and the handler's state.
+The following snippet sends a reply when it receives a
+`reply` message from another process, or waits for another
+message otherwise.
+
+``` erlang
info({reply, Body}, Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], Body, Req),
{ok, Req2, State};
-info(Message, Req, State) ->
+info(_Msg, Req, State) ->
{loop, Req, State, hibernate}.
+```
+
+Do note that the `reply` tuple here may be any message
+and is simply an example.
+
+This callback may perform any necessary operation including
+sending all or parts of a reply, and will subsequently
+return a tuple indicating if more messages are to be expected.
+
+The callback may also choose to do nothing at all and just
+skip the message received.
+
+If a reply is sent, then the `ok` tuple should be returned.
+This will instruct Cowboy to end the request.
+
+Otherwise a `loop` tuple should be returned.
+
+Streaming loop
+--------------
+
+Another common case well suited for loop handlers is
+streaming data received in the form of Erlang messages.
+This can be done by initiating a chunked reply in the
+`init/3` callback and then using `cowboy_req:chunk/2`
+every time a message is received.
+
+The following snippet does exactly that. As you can see
+a chunk is sent every time a `chunk` message is received,
+and the loop is stopped by sending an `eof` message.
+
+``` erlang
+init(_Type, Req, _Opts) ->
+ {ok, Req2} = cowboy_req:chunked_reply(200, [], Req),
+ {loop, Req2, undefined_state}.
-terminate(Reason, Req, State) ->
- ok.
+info(eof, Req, State) ->
+ {ok, Req, State};
+info({chunk, Chunk}, Req, State) ->
+ ok = cowboy_req:chunk(Chunk, Req),
+ {loop, Req, State};
+info(_Msg, Req, State) ->
+ {loop, Req, State}.
```
+
+Cleaning up
+-----------
+
+It is recommended that you set the connection header to
+`close` when replying, as this process may be reused for
+a subsequent request.
+
+Please refer to the [HTTP handlers chapter](http_handlers.md)
+for general instructions about cleaning up.
+
+Timeout
+-------
+
+By default Cowboy will not attempt to close the connection
+if there is no activity from the client. This is not always
+desirable, which is why you can set a timeout. Cowboy will
+close the connection if no data was received from the client
+after the configured time. The timeout only needs to be set
+once and can't be modified afterwards.
+
+Because the request may have had a body, or may be followed
+by another request, Cowboy is forced to buffer all data it
+receives. This data may grow to become too large though,
+so there is a configurable limit for it. The default buffer
+size is of 5000 bytes, but it may be changed by setting the
+`loop_max_buffer` middleware environment value.
+
+Hibernate
+---------
+
+To save memory, you may hibernate the process in between
+messages received. This is done by returning the atom
+`hibernate` as part of the `loop` tuple callbacks normally
+return. Just add the atom at the end and Cowboy will hibernate
+accordingly.