aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src/guide/loop_handlers.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/guide/loop_handlers.asciidoc')
-rw-r--r--doc/src/guide/loop_handlers.asciidoc146
1 files changed, 146 insertions, 0 deletions
diff --git a/doc/src/guide/loop_handlers.asciidoc b/doc/src/guide/loop_handlers.asciidoc
new file mode 100644
index 0000000..58c4223
--- /dev/null
+++ b/doc/src/guide/loop_handlers.asciidoc
@@ -0,0 +1,146 @@
+[[loop_handlers]]
+== Loop handlers
+
+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.
+
+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
+most known example of such practice is known as long polling.
+
+Loop handlers can also be used for requests where a response is
+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/2` and `terminate/3` callbacks which work the same as
+for plain HTTP handlers.
+
+=== Initialization
+
+The `init/2` function must return a `cowboy_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.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ {cowboy_loop, Req, #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.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ {cowboy_loop, Req, #state{}, 30000, hibernate}.
+----
+
+=== Receive loop
+
+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.
+
+[source,erlang]
+----
+info({reply, Body}, Req, State) ->
+ Req2 = cowboy_req:reply(200, [], Body, Req),
+ {stop, Req2, State};
+info(_Msg, Req, State) ->
+ {ok, 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 `stop` tuple should be returned.
+This will instruct Cowboy to end the request.
+
+Otherwise an `ok` 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/2` 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.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ Req2 = cowboy_req:chunked_reply(200, [], Req),
+ {cowboy_loop, Req2, #state{}}.
+
+info(eof, Req, State) ->
+ {stop, Req, State};
+info({chunk, Chunk}, Req, State) ->
+ cowboy_req:chunk(Chunk, Req),
+ {ok, Req, State};
+info(_Msg, Req, State) ->
+ {ok, 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 xref:handlers[Handlers chapter]
+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.