Updated documentation.
authorStanislaw Klekot <dozzie@jarowit.net>
Sun, 19 Apr 2015 20:07:48 +0000 (22:07 +0200)
committerStanislaw Klekot <dozzie@jarowit.net>
Sun, 19 Apr 2015 20:07:48 +0000 (22:07 +0200)
src/gen_sse_server.erl
src/mod_sse.erl
src/mod_sse_app.erl
src/mod_sse_sup.erl
src/mod_sse_worker.erl
src/overview.edoc

index e04c1af..bb111f4 100644 (file)
 %%%---------------------------------------------------------------------------
 %%% @doc
 %%%   Behaviour for a callback module to use with {@link mod_sse}.
+%%%
+%%%   == Expected callbacks ==
+%%%
+%%%   The callbacks are very similar to the ones from {@link gen_server},
+%%%   except that `handle_*()' return one more element in tuples, which is
+%%%   a list of events to send to the client (atom `nothing' in the place has
+%%%   the same result as returning empty list).
+%%%
+%%%   <ul>
+%%%     <li>
+%%%       `init(RootURI :: uri(), URI :: uri(), Headers :: [http_header()],
+%%%             Args :: term())'
+%%%       <ul>
+%%%         <li>{@type @{ok, term()@}}</li>
+%%%         <li>{@type @{ok, term(), timeout()@}}</li>
+%%%         <li>{@type @{ok, term(), hibernate@}}</li>
+%%%         <li>{@type @{error, term()@}}</li>
+%%%       </ul>
+%%%     </li>
+%%%     <li>
+%%%       `terminate(Reason :: normal | shutdown | {shutdown, term()} | term(),
+%%%                  State :: term())'
+%%%       <ul>
+%%%         <li>returned value is ignored</li>
+%%%       </ul>
+%%%     </li>
+%%%     <li>
+%%%       `handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+%%%                    State :: term())'
+%%%       <ul>
+%%%         <li>{@type @{reply, Reply :: term(), nothing | [event()], NewState :: term()@}}</li>
+%%%         <li>{@type @{reply, Reply :: term(), nothing | [event()], NewState :: term(), timeout()@}}</li>
+%%%         <li>{@type @{reply, Reply :: term(), nothing | [event()], NewState :: term(), hibernate@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), timeout()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), hibernate@}}</li>
+%%%         <li>{@type @{stop, Reason :: term(), Reply :: term(), nothing | [event()], NewState :: term()@}}</li>
+%%%         <li>{@type @{stop, Reason :: term(), nothing | [event()], NewState :: term()@}}</li>
+%%%       </ul>
+%%%     </li>
+%%%     <li>
+%%%       `handle_cast(Request :: term(), State :: term())'
+%%%       <ul>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), timeout()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), hibernate@}}</li>
+%%%         <li>{@type @{stop, Reason :: term(), nothing | [event()], NewState :: term()@}}</li>
+%%%       </ul>
+%%%     </li>
+%%%     <li>
+%%%       `handle_info(Message :: term(), State :: term())'
+%%%       <ul>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), timeout()@}}</li>
+%%%         <li>{@type @{noreply, nothing | [event()], NewState :: term(), hibernate@}}</li>
+%%%         <li>{@type @{stop, Reason :: term(), nothing | [event()], NewState :: term()@}}</li>
+%%%       </ul>
+%%%     </li>
+%%%     <li>
+%%%       `code_change(OldVsn :: term() | {down, term()}, State :: term(),
+%%%                    Extra :: term())'
+%%%       <ul>
+%%%         <li>{@type @{ok, NewState :: term()@}}</li>
+%%%         <li>{@type @{error, term()@}}</li>
+%%%       </ul>
+%%%       <b>Note</b>: since I don't have much experience with Erlang
+%%%       releases, this function most probably doesn't work correctly (this
+%%%       is a working example of cargo cult programming).
+%%%     </li>
+%%%   </ul>
+%%%
 %%% @end
 %%%---------------------------------------------------------------------------
 
 -module(gen_sse_server).
 
+-export_type([event/0, uri/0, http_header/0]).
+
 %%%---------------------------------------------------------------------------
 
+-type uri() :: string().
+%% Path from the request (or module mountpoint).
+
 -type event() :: iolist().
+%% Event to be sent. This should be a bytestring ready to pass through
+%% network, so `unicode:characters_to_binary/2' could be handy.
+
+-type http_header() :: {term(), term()}.
+%% Single HTTP request header.
 
 %%%---------------------------------------------------------------------------
 
--callback init(RootURI :: string(), URI :: string(),
-               Headers :: [{term(), term()}], Args :: term()) ->
-    {ok, term()}
-  | {ok, term(), timeout()}
-  | {ok, term(), hibernate}
-  | {error, term()}.
-
--callback terminate(Reason :: normal | shutdown | {shutdown, term()} | term(),
-                    State :: term()) ->
-  term().
-
--callback handle_call(Request :: term(), From :: {pid(), Tag :: term()},
-                      State :: term()) ->
-    {reply, Reply :: term(), nothing | [event()], NewState :: term()}
-  | {reply, Reply :: term(), nothing | [event()], NewState :: term(), timeout()}
-  | {reply, Reply :: term(), nothing | [event()], NewState :: term(), hibernate}
-  | {noreply, nothing | [event()], NewState :: term()}
-  | {noreply, nothing | [event()], NewState :: term(), timeout()}
-  | {noreply, nothing | [event()], NewState :: term(), hibernate}
-  | {stop, Reason :: term(), Reply :: term(), nothing | [event()], NewState :: term()}
-  | {stop, Reason :: term(), nothing | [event()], NewState :: term()}.
-
--callback handle_cast(Request :: term(), State :: term()) ->
-    {noreply, nothing | [event()], NewState :: term()}
-  | {noreply, nothing | [event()], NewState :: term(), timeout()}
-  | {noreply, nothing | [event()], NewState :: term(), hibernate}
-  | {stop, Reason :: term(), nothing | [event()], NewState :: term()}.
-
--callback handle_info(Message :: term(), State :: term()) ->
-    {noreply, nothing | [event()], NewState :: term()}
-  | {noreply, nothing | [event()], NewState :: term(), timeout()}
-  | {noreply, nothing | [event()], NewState :: term(), hibernate}
-  | {stop, Reason :: term(), nothing | [event()], NewState :: term()}.
-
--callback code_change(OldVsn :: term() | {down, term()}, State :: term(),
-                      Extra :: term()) ->
-    {ok, NewState :: term()}
-  | {error, term()}.
+behaviour_info(callbacks) ->
+  [{init, 4}, {terminate, 2},
+    {handle_call, 3}, {handle_cast, 2}, {handle_info, 2},
+    {code_change, 3}];
+behaviour_info(_Any) ->
+  undefined.
 
 %%%---------------------------------------------------------------------------
 %%% vim:ft=erlang:foldmethod=marker:nowrap
index 1653480..30efc6f 100644 (file)
@@ -1,6 +1,33 @@
 %%%---------------------------------------------------------------------------
 %%% @doc
 %%%   `inets'/`httpd' request handler module.
+%%%
+%%%   == httpd configuration ==
+%%%
+%%%   Of course you need to include `mod_sse' in `modules' section in `httpd'
+%%%   config. With this done, you specify 3-tuples with URIs that are to be
+%%%   handled by `mod_sse':
+%%%
+%%%   ```
+%%%     HTTPDConfig = [
+%%%       % ...
+%%%       {modules, [mod_sse, ...]},
+%%%       % ...
+%%%       {sse, "/events", sse_handler},
+%%%       % ...
+%%%     ],
+%%%   '''
+%%%
+%%%   Such tuple has `sse' atom as the first element, then URI under which
+%%%   `mod_sse' operates, and a {@link gen_sse_server. callback module} that
+%%%   produces (or, most probably, receives) messages to be sent to connected
+%%%   clients.
+%%%
+%%%   The callback module is specified as either
+%%%   {@type @{module(), Args :: [term()]@}} or
+%%%   {@type module()} (`Args' default to `[]' in such case).
+%%%
+%%% @see gen_sse_server
 %%% @end
 %%%---------------------------------------------------------------------------
 
@@ -21,6 +48,9 @@
 
 %%%---------------------------------------------------------------------------
 
+%% @private
+%% @doc `httpd' handler.
+
 do(_ModData = #mod{config_db = ConfigTable, request_uri = URI,
                    data = RequestData, socket = Socket}) ->
   case find_prefix(ConfigTable, URI) of
index e32cca4..d1cfd8a 100644 (file)
@@ -1,4 +1,5 @@
 %%%---------------------------------------------------------------------------
+%%% @private
 %%% @doc
 %%%   `mod_sse' application entry point.
 %%% @end
index 69b042b..9e8f52c 100644 (file)
@@ -1,4 +1,5 @@
 %%%---------------------------------------------------------------------------
+%%% @private
 %%% @doc
 %%%   Supervisor for workers that receive events and pass them to appropriate
 %%%   httpd process.
index 3c93c59..328a406 100644 (file)
@@ -1,4 +1,5 @@
 %%%---------------------------------------------------------------------------
+%%% @private
 %%% @doc
 %%%   Process that receives events and passes them to httpd connection
 %%%   handler.
index 7336159..08fdb98 100644 (file)
@@ -4,7 +4,7 @@
 @title Server-Sent Event server implementation for inets/httpd.
 @doc
 
-mod_sse is a module implementing server side of
+`mod_sse' is a module implementing server side of
 <a href="http://www.w3.org/TR/eventsource/">Server-Sent Events protocol</a>
 for inets httpd server.
 
@@ -14,7 +14,17 @@ be run behind nginx 1.2, later extended to be a little more generic
 
 == Architecture ==
 
-<b>TODO</b>
+`mod_sse' comes as a module for `httpd', the HTTP server that comes with OTP
+and runs as `inets' service. You configure it in a typical way for `httpd'
+modules, adding `{sse, "/...", CallbackModule}' tuple to configuration list
+(see {@link mod_sse} for details). 
+
+`mod_sse' spawns {@link gen_sse_server} process for every connection, and this
+additional process is responsible for subscribing to event source of some
+sort and then receiving events. Those will be passed back to `mod_sse' and
+sent to the client. Note that events produced need to be ready to be sent
+through the wire, so any unicode string needs to be converted to bytes
+(possibly using `unicode:characters_to_binary(Data, unicode)').
 
 == Example usage ==