loop_handlers.asciidoc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. [[loop_handlers]]
  2. == Loop handlers
  3. Loop handlers are a special kind of HTTP handlers used when the
  4. response can not be sent right away. The handler enters instead
  5. a receive loop waiting for the right message before it can send
  6. a response.
  7. Loop handlers are used for requests where a response might not
  8. be immediately available, but where you would like to keep the
  9. connection open for a while in case the response arrives. The
  10. most known example of such practice is known as long polling.
  11. Loop handlers can also be used for requests where a response is
  12. partially available and you need to stream the response body
  13. while the connection is open. The most known example of such
  14. practice is server-sent events, but it also applies to any
  15. response that takes a long time to send.
  16. While the same can be accomplished using plain HTTP handlers,
  17. it is recommended to use loop handlers because they are well-tested
  18. and allow using built-in features like hibernation and timeouts.
  19. Loop handlers essentially wait for one or more Erlang messages
  20. and feed these messages to the `info/3` callback. It also features
  21. the `init/2` and `terminate/3` callbacks which work the same as
  22. for plain HTTP handlers.
  23. === Initialization
  24. The `init/2` function must return a `cowboy_loop` tuple to enable
  25. loop handler behavior. This tuple may optionally contain
  26. the atom `hibernate` to make the process enter hibernation
  27. until a message is received.
  28. This snippet enables the loop handler:
  29. [source,erlang]
  30. ----
  31. init(Req, State) ->
  32. {cowboy_loop, Req, State}.
  33. ----
  34. This also makes the process hibernate:
  35. [source,erlang]
  36. ----
  37. init(Req, State) ->
  38. {cowboy_loop, Req, State, hibernate}.
  39. ----
  40. === Receive loop
  41. Once initialized, Cowboy will wait for messages to arrive
  42. in the process' mailbox. When a message arrives, Cowboy
  43. calls the `info/3` function with the message, the Req object
  44. and the handler's state.
  45. The following snippet sends a reply when it receives a
  46. `reply` message from another process, or waits for another
  47. message otherwise.
  48. [source,erlang]
  49. ----
  50. info({reply, Body}, Req, State) ->
  51. cowboy_req:reply(200, #{}, Body, Req),
  52. {stop, Req, State};
  53. info(_Msg, Req, State) ->
  54. {ok, Req, State, hibernate}.
  55. ----
  56. Do note that the `reply` tuple here may be any message
  57. and is simply an example.
  58. This callback may perform any necessary operation including
  59. sending all or parts of a reply, and will subsequently
  60. return a tuple indicating if more messages are to be expected.
  61. The callback may also choose to do nothing at all and just
  62. skip the message received.
  63. If a reply is sent, then the `stop` tuple should be returned.
  64. This will instruct Cowboy to end the request.
  65. Otherwise an `ok` tuple should be returned.
  66. === Streaming loop
  67. Another common case well suited for loop handlers is
  68. streaming data received in the form of Erlang messages.
  69. This can be done by initiating a chunked reply in the
  70. `init/2` callback and then using `cowboy_req:chunk/2`
  71. every time a message is received.
  72. The following snippet does exactly that. As you can see
  73. a chunk is sent every time an `event` message is received,
  74. and the loop is stopped by sending an `eof` message.
  75. [source,erlang]
  76. ----
  77. init(Req, State) ->
  78. Req2 = cowboy_req:stream_reply(200, Req),
  79. {cowboy_loop, Req2, State}.
  80. info(eof, Req, State) ->
  81. {stop, Req, State};
  82. info({event, Data}, Req, State) ->
  83. cowboy_req:stream_body(Data, nofin, Req),
  84. {ok, Req, State};
  85. info(_Msg, Req, State) ->
  86. {ok, Req, State}.
  87. ----
  88. === Cleaning up
  89. Please refer to the xref:handlers[Handlers chapter]
  90. for general instructions about cleaning up.
  91. === Hibernate
  92. To save memory, you may hibernate the process in between
  93. messages received. This is done by returning the atom
  94. `hibernate` as part of the `loop` tuple callbacks normally
  95. return. Just add the atom at the end and Cowboy will hibernate
  96. accordingly.