/src/middleware/ewgi_push_stream/ewgi_push_stream.erl
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.