PageRenderTime 334ms CodeModel.GetById 321ms RepoModel.GetById 0ms app.codeStats 0ms

/src/middleware/ewgi_push_stream/ewgi_push_stream.erl

http://github.com/skarab/ewgi
Erlang | 126 lines | 74 code | 14 blank | 38 comment | 0 complexity | 286ca50e5ec064e1636f9545d7847400 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. %% @author Davide Marquês <nesrait@gmail.com>
  2. %% @copyright 2009 Davide Marquês <nesrait@gmail.com>
  3. %%
  4. %% @doc ewgi Push streams middleware.
  5. %% The difference for regular [pull-]streams is that those are synchronously polled
  6. %% by the ewgi server for more data. With pull streams the ewgi server is notified
  7. %% when there is more data available.
  8. %% @end
  9. %%
  10. %% Licensed under the MIT license:
  11. %% http://www.opensource.org/licenses/mit-license.php
  12. %%
  13. -module(ewgi_push_stream).
  14. -author('Davide Marquês <nesrait@gmail.com>').
  15. -define(STREAM_INIT_TIMEOUT, 5000).
  16. -define(EX_STREAM_DATA, ["ini\r\n", "mini\r\n", "mini\r\n", "mo\r\n"]).
  17. %% Ewgi Application API
  18. -export([run/2]).
  19. %% Usage examples
  20. -export([
  21. chunked_stream_example/1,
  22. non_chunked_stream_example/1,
  23. ewgi_free_stream_example/1
  24. ]).
  25. %% The body field is used to specify which process will be producing data.
  26. %% The various headers and data are all send asynchronously to the
  27. %% ewgi gateway when available.
  28. %% The given timeout defines how long to want before issuing a 504 response.
  29. run(Ctx, [StreamPid]) when is_pid(StreamPid) ->
  30. PS = {push_stream, StreamPid, ?STREAM_INIT_TIMEOUT},
  31. ewgi_api:response_message_body(PS, Ctx);
  32. run(Ctx, [StreamPid, Timeout]) when is_pid(StreamPid) ->
  33. PS = {push_stream, StreamPid, Timeout},
  34. ewgi_api:response_message_body(PS, Ctx).
  35. %%--------------------------------------------------------------------
  36. %% Chunked stream example
  37. %%--------------------------------------------------------------------
  38. %% N.B.: there's no point in setting headers in the ewgi_context
  39. %% passed to ewgi_push_stream:run/2. Those headers will be ignore!
  40. chunked_stream_example(Ctx) ->
  41. StreamPid = spawn(fun() -> chunked_stream(Ctx) end),
  42. ?MODULE:run(Ctx, [StreamPid]).
  43. %% The ewgi_context that is passed to ewgi_api:stream_process_init/2 is
  44. %% the one from where the status code and headers are read from.
  45. %% The default is a chunked response (see below for non-chunked responses).
  46. chunked_stream(Ctx0) ->
  47. Status = {200, "OK"},
  48. H = ewgi_api:response_headers(Ctx0),
  49. CTHeader = {"Content-type", "text/plain"},
  50. Ctx = ewgi_api:response_headers([CTHeader|H],
  51. ewgi_api:response_status(Status, Ctx0)),
  52. CSEnd = "chunked_stream_end",
  53. case ewgi_api:stream_process_init(Ctx, chunked) of
  54. {ok, Connection} ->
  55. lists:foreach(fun(Word) ->
  56. ewgi_api:stream_process_deliver_chunk(Connection, Word),
  57. timer:sleep(1000)
  58. end, ?EX_STREAM_DATA),
  59. ewgi_api:stream_process_deliver_final_chunk(Connection, CSEnd),
  60. ewgi_api:stream_process_end(Connection);
  61. _ -> ok
  62. end.
  63. %%--------------------------------------------------------------------
  64. %% Non-Chunked stream example
  65. %%--------------------------------------------------------------------
  66. %% N.B.: there's no point in setting headers in the ewgi_context
  67. %% passed to ewgi_push_stream:run/2. Those headers will be ignore!
  68. non_chunked_stream_example(Ctx) ->
  69. StreamPid = spawn(fun() -> non_chunked_stream(Ctx) end),
  70. ?MODULE:run(Ctx, [StreamPid]).
  71. %% The ewgi_context that is passed to ewgi_api:stream_process_init/2 is
  72. %% the one from where the status code and headers are read from.
  73. %% By passing a content-length we specify that this woun't be a chunked response.
  74. non_chunked_stream(Ctx0) ->
  75. Status = {200, "OK"},
  76. H = ewgi_api:response_headers(Ctx0),
  77. CTHeader = {"Content-type", "text/plain"},
  78. Ctx = ewgi_api:response_headers([CTHeader|H],
  79. ewgi_api:response_status(Status, Ctx0)),
  80. NCSEnd = "non_chunked_stream_end",
  81. ContentLength = iolist_size(?EX_STREAM_DATA) + iolist_size(NCSEnd),
  82. case ewgi_api:stream_process_init(Ctx, ContentLength) of
  83. {ok, Connection} ->
  84. lists:foreach(fun(Word) ->
  85. ewgi_api:stream_process_deliver(Connection, Word),
  86. timer:sleep(1000)
  87. end, ?EX_STREAM_DATA),
  88. ewgi_api:stream_process_deliver(Connection, NCSEnd),
  89. ewgi_api:stream_process_end(Connection);
  90. _ -> ok
  91. end.
  92. %%--------------------------------------------------------------------
  93. %% Ewgi-free stream
  94. %% Just showing off that any process can serve as a push stream
  95. %%--------------------------------------------------------------------
  96. ewgi_free_stream_example(Ctx) ->
  97. StreamPid = spawn(fun ewgi_free_stream/0),
  98. ?MODULE:run(Ctx, [StreamPid]).
  99. ewgi_free_stream() ->
  100. EFSEnd = "ewgi_free_stream_end",
  101. StatusCode = 200,
  102. Headers = [{"Content-type", "text/plain"}],
  103. case ewgi_api:stream_process_init(StatusCode, Headers, chunked) of
  104. {ok, Connection} ->
  105. lists:foreach(fun(Word) ->
  106. ewgi_api:stream_process_deliver_chunk(Connection, Word),
  107. timer:sleep(1000)
  108. end, ?EX_STREAM_DATA),
  109. ewgi_api:stream_process_deliver_final_chunk(Connection, EFSEnd),
  110. ewgi_api:stream_process_end(Connection);
  111. _ -> ok
  112. end.