PageRenderTime 87ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/keepalive/keepalive.erl

http://github.com/basho/mochiweb
Erlang | 81 lines | 33 code | 14 blank | 34 comment | 0 complexity | 9a69f47aa85b971305d40ee1486069d4 MD5 | raw file
Possible License(s): MIT
  1. -module(keepalive).
  2. %% your web app can push data to clients using a technique called comet long
  3. %% polling. browsers make a request and your server waits to send a
  4. %% response until data is available. see wikipedia for a better explanation:
  5. %% http://en.wikipedia.org/wiki/Comet_(programming)#Ajax_with_long_polling
  6. %%
  7. %% since the majority of your http handlers will be idle at any given moment,
  8. %% you might consider making them hibernate while they wait for more data from
  9. %% another process. however, since the execution stack is discarded when a
  10. %% process hibernates, the handler would usually terminate after your response
  11. %% code runs. this means http keep alives wouldn't work; the handler process
  12. %% would terminate after each response and close its socket rather than
  13. %% returning to the big @mochiweb_http@ loop and processing another request.
  14. %%
  15. %% however, if mochiweb exposes a continuation that encapsulates the return to
  16. %% the top of the big loop in @mochiweb_http@, we can call that after the
  17. %% response. if you do that then control flow returns to the proper place,
  18. %% and keep alives work like they would if you hadn't hibernated.
  19. -export([ start/1, loop/1
  20. ]).
  21. %% internal export (so hibernate can reach it)
  22. -export([ resume/3
  23. ]).
  24. -define(LOOP, {?MODULE, loop}).
  25. start(Options = [{port, _Port}]) ->
  26. mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP} | Options]).
  27. loop(Req) ->
  28. Path = Req:get(path),
  29. case string:tokens(Path, "/") of
  30. ["longpoll" | RestOfPath] ->
  31. %% the "reentry" is a continuation -- what @mochiweb_http@
  32. %% needs to do to start its loop back at the top
  33. Reentry = mochiweb_http:reentry(?LOOP),
  34. %% here we could send a message to some other process and hope
  35. %% to get an interesting message back after a while. for
  36. %% simplicity let's just send ourselves a message after a few
  37. %% seconds
  38. erlang:send_after(2000, self(), "honk honk"),
  39. %% since we expect to wait for a long time before getting a
  40. %% reply, let's hibernate. memory usage will be minimized, so
  41. %% we won't be wasting memory just sitting in a @receive@
  42. proc_lib:hibernate(?MODULE, resume, [Req, RestOfPath, Reentry]),
  43. %% we'll never reach this point, and this function @loop/1@
  44. %% won't ever return control to @mochiweb_http@. luckily
  45. %% @resume/3@ will take care of that.
  46. io:format("not gonna happen~n", []);
  47. _ ->
  48. ok(Req, io_lib:format("some other page: ~p", [Path]))
  49. end,
  50. io:format("restarting loop normally in ~p~n", [Path]),
  51. ok.
  52. %% this is the function that's called when a message arrives.
  53. resume(Req, RestOfPath, Reentry) ->
  54. receive
  55. Msg ->
  56. Text = io_lib:format("wake up message: ~p~nrest of path: ~p", [Msg, RestOfPath]),
  57. ok(Req, Text)
  58. end,
  59. %% if we didn't call @Reentry@ here then the function would finish and the
  60. %% process would exit. calling @Reentry@ takes care of returning control
  61. %% to @mochiweb_http@
  62. io:format("reentering loop via continuation in ~p~n", [Req:get(path)]),
  63. Reentry(Req).
  64. ok(Req, Response) ->
  65. Req:ok({_ContentType = "text/plain",
  66. _Headers = [],
  67. Response}).