Permalink
Browse files

Add documentation for the public interface.

This is probably not perfect yet but it should be better than
nothing. We'll improve things with feedback received from the
many users.
  • Loading branch information...
1 parent 2b3bfdd commit 108a491f5515fdc2a7fe3ce8c310a261d6be3230 Loïc Hoguin committed Jul 6, 2011
View
@@ -1,5 +1,9 @@
.cowboy_dialyzer.plt
.eunit
+doc/*.css
+doc/*.html
+doc/*.png
+doc/edoc-info
ebin
logs
test/*.beam
View
@@ -29,3 +29,6 @@ dialyze:
@$(DIALYZER) --src src --plt .cowboy_dialyzer.plt \
-Wbehaviours -Werror_handling \
-Wrace_conditions -Wunmatched_returns # -Wunderspecs
+
+docs:
+ @$(REBAR) doc
View
@@ -0,0 +1,4 @@
+@author Lo�c Hoguin <essen@dev-extend.eu>
+@copyright 2011 Lo�c Hoguin
+@version HEAD
+@title Small, fast, modular HTTP server.
View
@@ -12,11 +12,30 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc Cowboy API to start and stop listeners.
-module(cowboy).
--export([start_listener/6, stop_listener/1]). %% API.
-%% API.
+-export([start_listener/6, stop_listener/1]).
+%% @doc Start a listener for the given transport and protocol.
+%%
+%% A listener is effectively a pool of <em>NbAcceptors</em> acceptors.
+%% Acceptors accept connections on the given <em>Transport</em> and forward
+%% requests to the given <em>Protocol</em> handler. Both transport and protocol
+%% modules can be given options through the <em>TransOpts</em> and the
+%% <em>ProtoOpts</em> arguments. Available options are documented in the
+%% <em>listen</em> transport function and in the protocol module of your choice.
+%%
+%% All acceptor and request processes are supervised by the listener.
+%%
+%% It is recommended to set a large enough number of acceptors to improve
+%% performance. The exact number depends of course on your hardware, on the
+%% protocol used and on the number of expected simultaneous connections.
+%%
+%% Although Cowboy includes a <em>cowboy_http_protocol</em> handler, other
+%% handlers can be created for different protocols like IRC, FTP and more.
+%%
+%% <em>Ref</em> can be used to stop the listener later on.
-spec start_listener(any(), non_neg_integer(), module(), any(), module(), any())
-> {ok, pid()}.
start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
@@ -26,6 +45,8 @@ start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
]},
permanent, 5000, supervisor, [cowboy_listener_sup]}).
+%% @doc Stop a listener identified by <em>Ref</em>.
+%% @todo Currently request processes aren't terminated with the listener.
-spec stop_listener(any()) -> ok | {error, not_found}.
stop_listener(Ref) ->
case supervisor:terminate_child(cowboy_sup, {cowboy_listener_sup, Ref}) of
View
@@ -12,7 +12,9 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @private
-module(cowboy_acceptor).
+
-export([start_link/6]). %% API.
-export([acceptor/6]). %% Internal.
@@ -12,6 +12,7 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @private
-module(cowboy_acceptors_sup).
-behaviour(supervisor).
View
@@ -12,6 +12,7 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @private
-module(cowboy_app).
-behaviour(application).
View
@@ -12,6 +12,12 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc Date and time related functions.
+%%
+%% While a gen_server process runs in the background to update
+%% the cache of formatted dates every second, all API calls are
+%% local and directly read from the ETS cache table, providing
+%% fast time and date computations.
-module(cowboy_clock).
-behaviour(gen_server).
@@ -46,20 +52,26 @@
%% API.
+%% @private
-spec start_link() -> {ok, pid()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+%% @private
-spec stop() -> stopped.
stop() ->
gen_server:call(?SERVER, stop).
+%% @doc Return the current date and time formatted according to RFC-1123.
+%%
+%% This format is used in the <em>'Date'</em> header sent with HTTP responses.
-spec rfc1123() -> binary().
rfc1123() ->
ets:lookup_element(?TABLE, rfc1123, 2).
%% gen_server.
+%% @private
-spec init([]) -> {ok, #state{}}.
init([]) ->
?TABLE = ets:new(?TABLE, [set, protected,
@@ -70,6 +82,7 @@ init([]) ->
ets:insert(?TABLE, {rfc1123, B}),
{ok, #state{universaltime=T, rfc1123=B, tref=TRef}}.
+%% @private
-spec handle_call(_, _, State)
-> {reply, ignored, State} | {stop, normal, stopped, State}.
handle_call(stop, _From, State=#state{tref=TRef}) ->
@@ -78,10 +91,12 @@ handle_call(stop, _From, State=#state{tref=TRef}) ->
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
+%% @private
-spec handle_cast(_, State) -> {noreply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
+%% @private
-spec handle_info(_, State) -> {noreply, State}.
handle_info(update, #state{universaltime=Prev, rfc1123=B1, tref=TRef}) ->
T = erlang:universaltime(),
@@ -91,10 +106,12 @@ handle_info(update, #state{universaltime=Prev, rfc1123=B1, tref=TRef}) ->
handle_info(_Info, State) ->
{noreply, State}.
+%% @private
-spec terminate(_, _) -> ok.
terminate(_Reason, _State) ->
ok.
+%% @private
-spec code_change(_, State, _) -> {ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
View
@@ -13,12 +13,14 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc Dispatch requests according to a hostname and path.
-module(cowboy_dispatcher).
+
-export([split_host/1, split_path/1, match/3]). %% API.
-type bindings() :: list({atom(), binary()}).
-type path_tokens() :: list(binary()).
--type match_rule() :: '_' | '*' | list(binary() | '_' | atom()).
+-type match_rule() :: '_' | '*' | list(binary() | '_' | '...' | atom()).
-type dispatch_path() :: list({match_rule(), module(), any()}).
-type dispatch_rule() :: {Host::match_rule(), Path::dispatch_path()}.
-type dispatch_rules() :: list(dispatch_rule()).
@@ -29,6 +31,7 @@
%% API.
+%% @doc Split a hostname into a list of tokens.
-spec split_host(binary())
-> {path_tokens(), binary(), undefined | inet:ip_port()}.
split_host(<<>>) ->
@@ -42,6 +45,7 @@ split_host(Host) ->
list_to_integer(binary_to_list(Port))}
end.
+%% @doc Split a path into a list of tokens.
-spec split_path(binary()) -> {path_tokens(), binary(), binary()}.
split_path(Path) ->
case binary:split(Path, <<"?">>) of
@@ -57,6 +61,33 @@ do_split_path(RawPath, Separator) ->
Path -> Path
end.
+%% @doc Match hostname tokens and path tokens against dispatch rules.
+%%
+%% It is typically used for matching tokens for the hostname and path of
+%% the request against a global dispatch rule for your listener.
+%%
+%% Dispatch rules are a list of <em>{Hostname, PathRules}</em> tuples, with
+%% <em>PathRules</em> being a list of <em>{Path, HandlerMod, HandlerOpts}</em>.
+%%
+%% <em>Hostname</em> and <em>Path</em> are match rules and can be either the
+%% atom <em>'_'</em>, which matches everything for a single token, the atom
+%% <em>'*'</em>, which matches everything for the rest of the tokens, or a
+%% list of tokens. Each token can be either a binary, the atom <em>'_'</em>,
+%% the atom '...' or a named atom. A binary token must match exactly,
+%% <em>'_'</em> matches everything for a single token, <em>'...'</em> matches
+%% everything for the rest of the tokens and a named atom will bind the
+%% corresponding token value and return it.
+%%
+%% The list of hostname tokens is reversed before matching. For example, if
+%% we were to match "www.dev-extend.eu", we would first match "eu", then
+%% "dev-extend", then "www". This means that in the context of hostnames,
+%% the <em>'...'</em> atom matches properly the lower levels of the domain
+%% as would be expected.
+%%
+%% When a result is found, this function will return the handler module and
+%% options found in the dispatch list, a key-value list of bindings and
+%% the tokens that were matched by the <em>'...'</em> atom for both the
+%% hostname and path.
-spec match(Host::path_tokens(), Path::path_tokens(), dispatch_rules())
-> {ok, module(), any(), bindings(),
HostInfo::undefined | path_tokens(),
@@ -12,9 +12,34 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc Handler for HTTP requests.
+%%
+%% HTTP handlers must implement three callbacks: <em>init/3</em>,
+%% <em>handle/2</em> and <em>terminate/2</em>, called one after another in
+%% that order.
+%%
+%% <em>init/3</em> is meant for initialization. It receives information about
+%% the transport and protocol used, along with the handler options from the
+%% dispatch list, and allows you to upgrade the protocol if needed. You can
+%% define a request-wide state here.
+%%
+%% <em>handle/2</em> is meant for handling the request. It receives the
+%% request and the state previously defined.
+%%
+%% <em>terminate/2</em> is meant for cleaning up. It also receives the
+%% request and the state previously defined.
+%%
+%% You do not have to read the request body or even send a reply if you do
+%% not need to. Cowboy will properly handle these cases and clean-up afterwards.
+%% In doubt it'll simply close the connection.
+%%
+%% Note that when upgrading the connection to WebSocket you do not need to
+%% define the <em>handle/2</em> and <em>terminate/2</em> callbacks.
-module(cowboy_http_handler).
+
-export([behaviour_info/1]).
+%% @private
-spec behaviour_info(_)
-> undefined | [{handle, 2} | {init, 3} | {terminate, 2}, ...].
behaviour_info(callbacks) ->
@@ -13,7 +13,24 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc HTTP protocol handler.
+%%
+%% The available options are:
+%% <dl>
+%% <dt>dispatch</dt><dd>The dispatch list for this protocol.</dd>
+%% <dt>max_empty_lines</dt><dd>Max number of empty lines before a request.
+%% Defaults to 5.</dd>
+%% <dt>timeout</dt><dd>Time in milliseconds before an idle keep-alive
+%% connection is closed. Defaults to 5000 milliseconds.</dd>
+%% </dl>
+%%
+%% Note that there is no need to monitor these processes when using Cowboy as
+%% an application as it already supervises them under the listener supervisor.
+%%
+%% @see cowboy_dispatcher
+%% @see cowboy_http_handler
-module(cowboy_http_protocol).
+
-export([start_link/3]). %% API.
-export([init/3, parse_request/1]). %% FSM.
@@ -33,13 +50,15 @@
%% API.
+%% @doc Start an HTTP protocol process.
-spec start_link(inet:socket(), module(), any()) -> {ok, pid()}.
start_link(Socket, Transport, Opts) ->
Pid = spawn_link(?MODULE, init, [Socket, Transport, Opts]),
{ok, Pid}.
%% FSM.
+%% @private
-spec init(inet:socket(), module(), any()) -> ok.
init(Socket, Transport, Opts) ->
Dispatch = proplists:get_value(dispatch, Opts, []),
@@ -48,6 +67,7 @@ init(Socket, Transport, Opts) ->
wait_request(#state{socket=Socket, transport=Transport,
dispatch=Dispatch, max_empty_lines=MaxEmptyLines, timeout=Timeout}).
+%% @private
-spec parse_request(#state{}) -> ok.
%% @todo Use decode_packet options to limit length?
parse_request(State=#state{buffer=Buffer}) ->
Oops, something went wrong.

0 comments on commit 108a491

Please sign in to comment.