PageRenderTime 103ms CodeModel.GetById 60ms app.highlight 12ms RepoModel.GetById 28ms 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
  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
 16-define(STREAM_INIT_TIMEOUT, 5000).
 17
 18-define(EX_STREAM_DATA, ["ini\r\n", "mini\r\n", "mini\r\n", "mo\r\n"]).
 19
 20%% Ewgi Application API
 21-export([run/2]).
 22
 23%% Usage examples
 24-export([
 25		chunked_stream_example/1,
 26		non_chunked_stream_example/1,
 27		ewgi_free_stream_example/1
 28	]).
 29
 30%% The body field is used to specify which process will be producing data.
 31%% The various headers and data are all send asynchronously to the
 32%% ewgi gateway when available.
 33%% The given timeout defines how long to want before issuing a 504 response.
 34run(Ctx, [StreamPid]) when is_pid(StreamPid) ->
 35	PS = {push_stream, StreamPid, ?STREAM_INIT_TIMEOUT},
 36	ewgi_api:response_message_body(PS, Ctx);
 37	
 38run(Ctx, [StreamPid, Timeout]) when is_pid(StreamPid) ->
 39	PS = {push_stream, StreamPid, Timeout},
 40	ewgi_api:response_message_body(PS, Ctx).
 41
 42%%--------------------------------------------------------------------
 43%% Chunked stream example
 44%%--------------------------------------------------------------------
 45%% N.B.: there's no point in setting headers in the ewgi_context
 46%% passed to ewgi_push_stream:run/2. Those headers will be ignore!
 47chunked_stream_example(Ctx) ->
 48	StreamPid = spawn(fun() -> chunked_stream(Ctx) end),
 49    ?MODULE:run(Ctx, [StreamPid]).
 50
 51%% The ewgi_context that is passed to ewgi_api:stream_process_init/2 is 
 52%% the one from where the status code and headers are read from.
 53%% The default is a chunked response (see below for non-chunked responses).
 54chunked_stream(Ctx0) ->
 55	Status = {200, "OK"},
 56	H = ewgi_api:response_headers(Ctx0),
 57	CTHeader = {"Content-type", "text/plain"},
 58	Ctx = ewgi_api:response_headers([CTHeader|H],
 59					ewgi_api:response_status(Status, Ctx0)),
 60	
 61	CSEnd = "chunked_stream_end",
 62	case ewgi_api:stream_process_init(Ctx, chunked) of
 63	{ok, Connection} ->
 64		lists:foreach(fun(Word) ->
 65			ewgi_api:stream_process_deliver_chunk(Connection, Word),
 66			timer:sleep(1000)
 67			end, ?EX_STREAM_DATA),
 68		ewgi_api:stream_process_deliver_final_chunk(Connection, CSEnd),
 69		ewgi_api:stream_process_end(Connection);
 70	_ -> ok
 71	end.
 72	
 73%%--------------------------------------------------------------------
 74%% Non-Chunked stream example
 75%%--------------------------------------------------------------------
 76%% N.B.: there's no point in setting headers in the ewgi_context
 77%% passed to ewgi_push_stream:run/2. Those headers will be ignore!
 78non_chunked_stream_example(Ctx) ->
 79	StreamPid = spawn(fun() -> non_chunked_stream(Ctx) end),
 80    ?MODULE:run(Ctx, [StreamPid]).
 81
 82%% The ewgi_context that is passed to ewgi_api:stream_process_init/2 is 
 83%% the one from where the status code and headers are read from.
 84%% By passing a content-length we specify that this woun't be a chunked response.
 85non_chunked_stream(Ctx0) ->
 86	Status = {200, "OK"},
 87	H = ewgi_api:response_headers(Ctx0),
 88	CTHeader = {"Content-type", "text/plain"},
 89	Ctx = ewgi_api:response_headers([CTHeader|H],
 90					ewgi_api:response_status(Status, Ctx0)),
 91
 92	NCSEnd = "non_chunked_stream_end",
 93	ContentLength = iolist_size(?EX_STREAM_DATA) + iolist_size(NCSEnd),
 94	case ewgi_api:stream_process_init(Ctx, ContentLength) of
 95	{ok, Connection} ->
 96		lists:foreach(fun(Word) ->
 97			ewgi_api:stream_process_deliver(Connection, Word),
 98			timer:sleep(1000)
 99			end, ?EX_STREAM_DATA),
100		ewgi_api:stream_process_deliver(Connection, NCSEnd),
101		ewgi_api:stream_process_end(Connection);
102	_ -> ok
103	end.
104
105%%--------------------------------------------------------------------
106%% Ewgi-free stream
107%% Just showing off that any process can serve as a push stream
108%%--------------------------------------------------------------------
109ewgi_free_stream_example(Ctx) ->
110	StreamPid = spawn(fun ewgi_free_stream/0),
111    ?MODULE:run(Ctx, [StreamPid]).
112	
113ewgi_free_stream() ->
114	EFSEnd = "ewgi_free_stream_end",
115	StatusCode = 200,
116	Headers = [{"Content-type", "text/plain"}],
117	case ewgi_api:stream_process_init(StatusCode, Headers, chunked) of
118	{ok, Connection} ->
119		lists:foreach(fun(Word) ->
120			ewgi_api:stream_process_deliver_chunk(Connection, Word),
121			timer:sleep(1000)
122			end, ?EX_STREAM_DATA),
123		ewgi_api:stream_process_deliver_final_chunk(Connection, EFSEnd),
124		ewgi_api:stream_process_end(Connection);
125	_ -> ok
126	end.