blob: f6f4e53baf8ed06ce51e92cda30942570de3effb [file] [log] [blame]
%% your web app can push data to clients using a technique called comet long
%% polling. browsers make a request and your server waits to send a
%% response until data is available. see wikipedia for a better explanation:
%% since the majority of your http handlers will be idle at any given moment,
%% you might consider making them hibernate while they wait for more data from
%% another process. however, since the execution stack is discarded when a
%% process hibernates, the handler would usually terminate after your response
%% code runs. this means http keep alives wouldn't work; the handler process
%% would terminate after each response and close its socket rather than
%% returning to the big @mochiweb_http@ loop and processing another request.
%% however, if mochiweb exposes a continuation that encapsulates the return to
%% the top of the big loop in @mochiweb_http@, we can call that after the
%% response. if you do that then control flow returns to the proper place,
%% and keep alives work like they would if you hadn't hibernated.
-export([loop/1, start/1]).
%% internal export (so hibernate can reach it)
-define(LOOP, {?MODULE, loop}).
start(Options = [{port, _Port}]) ->
mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP}
| Options]).
loop(Req) ->
Path = mochiweb_request:get(path, Req),
case string:tokens(Path, "/") of
["longpoll" | RestOfPath] ->
%% the "reentry" is a continuation -- what @mochiweb_http@
%% needs to do to start its loop back at the top
Reentry = mochiweb_http:reentry(?LOOP),
%% here we could send a message to some other process and hope
%% to get an interesting message back after a while. for
%% simplicity let's just send ourselves a message after a few
%% seconds
erlang:send_after(2000, self(), "honk honk"),
%% since we expect to wait for a long time before getting a
%% reply, let's hibernate. memory usage will be minimized, so
%% we won't be wasting memory just sitting in a @receive@
proc_lib:hibernate(?MODULE, resume,
[Req, RestOfPath, Reentry]),
%% we'll never reach this point, and this function @loop/1@
%% won't ever return control to @mochiweb_http@. luckily
%% @resume/3@ will take care of that.
io:format("not gonna happen~n", []);
_ ->
ok(Req, io_lib:format("some other page: ~p", [Path]))
io:format("restarting loop normally in ~p~n", [Path]),
%% this is the function that's called when a message arrives.
resume(Req, RestOfPath, Reentry) ->
Msg ->
Text =
io_lib:format("wake up message: ~p~nrest of path: ~p",
[Msg, RestOfPath]),
ok(Req, Text)
%% if we didn't call @Reentry@ here then the function would finish and the
%% process would exit. calling @Reentry@ takes care of returning control
%% to @mochiweb_http@
io:format("reentering loop via continuation in "
[mochiweb_request:get(path, Req)]),
ok(Req, Response) ->
mochiweb_request:ok({_ContentType = "text/plain",
_Headers = [], Response},