Skip to content
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
4 .gitignore
@@ -1,5 +1,9 @@
.cowboy_dialyzer.plt
.eunit
+doc/*.css
+doc/*.html
+doc/*.png
+doc/edoc-info
ebin
logs
test/*.beam
View
3 Makefile
@@ -29,3 +29,6 @@ dialyze:
@$(DIALYZER) --src src --plt .cowboy_dialyzer.plt \
-Wbehaviours -Werror_handling \
-Wrace_conditions -Wunmatched_returns # -Wunderspecs
+
+docs:
+ @$(REBAR) doc
View
4 doc/overview.edoc
@@ -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
25 src/cowboy.erl
@@ -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
2 src/cowboy_acceptor.erl
@@ -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.
View
1 src/cowboy_acceptors_sup.erl
@@ -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
1 src/cowboy_app.erl
@@ -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
17 src/cowboy_clock.erl
@@ -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
33 src/cowboy_dispatcher.erl
@@ -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(),
View
25 src/cowboy_http_handler.erl
@@ -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) ->
View
20 src/cowboy_http_protocol.erl
@@ -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}) ->
View
53 src/cowboy_http_req.erl
@@ -13,6 +13,12 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc HTTP request manipulation API.
+%%
+%% Almost all functions in this module return a new <em>Req</em> variable.
+%% It should always be used instead of the one used in your function call
+%% because it keeps the state of the request. It also allows Cowboy to do
+%% some lazy evaluation and cache results where possible.
-module(cowboy_http_req).
-export([
@@ -42,57 +48,71 @@
%% Request API.
+%% @doc Return the HTTP method of the request.
-spec method(#http_req{}) -> {http_method(), #http_req{}}.
method(Req) ->
{Req#http_req.method, Req}.
+%% @doc Return the HTTP version used for the request.
-spec version(#http_req{}) -> {http_version(), #http_req{}}.
version(Req) ->
{Req#http_req.version, Req}.
+%% @doc Return the peer address and port number of the remote host.
-spec peer(#http_req{}) -> {{inet:ip_address(), inet:ip_port()}, #http_req{}}.
peer(Req=#http_req{socket=Socket, transport=Transport, peer=undefined}) ->
{ok, Peer} = Transport:peername(Socket),
{Peer, Req#http_req{peer=Peer}};
peer(Req) ->
{Req#http_req.peer, Req}.
+%% @doc Return the tokens for the hostname requested.
-spec host(#http_req{}) -> {cowboy_dispatcher:path_tokens(), #http_req{}}.
host(Req) ->
{Req#http_req.host, Req}.
+%% @doc Return the extra host information obtained from partially matching
+%% the hostname using <em>'...'</em>.
-spec host_info(#http_req{})
-> {cowboy_dispatcher:path_tokens() | undefined, #http_req{}}.
host_info(Req) ->
{Req#http_req.host_info, Req}.
+%% @doc Return the raw host directly taken from the request.
-spec raw_host(#http_req{}) -> {binary(), #http_req{}}.
raw_host(Req) ->
{Req#http_req.raw_host, Req}.
+%% @doc Return the port used for this request.
-spec port(#http_req{}) -> {inet:ip_port(), #http_req{}}.
port(Req) ->
{Req#http_req.port, Req}.
+%% @doc Return the tokens for the path requested.
-spec path(#http_req{}) -> {cowboy_dispatcher:path_tokens(), #http_req{}}.
path(Req) ->
{Req#http_req.path, Req}.
+%% @doc Return the extra path information obtained from partially matching
+%% the patch using <em>'...'</em>.
-spec path_info(#http_req{})
-> {cowboy_dispatcher:path_tokens() | undefined, #http_req{}}.
path_info(Req) ->
{Req#http_req.path_info, Req}.
+%% @doc Return the raw path directly taken from the request.
-spec raw_path(#http_req{}) -> {binary(), #http_req{}}.
raw_path(Req) ->
{Req#http_req.raw_path, Req}.
+%% @equiv qs_val(Name, Req, undefined)
-spec qs_val(binary(), #http_req{})
-> {binary() | true | undefined, #http_req{}}.
-%% @equiv qs_val(Name, Req, undefined)
qs_val(Name, Req) ->
qs_val(Name, Req, undefined).
+%% @doc Return the query string value for the given key, or a default if
+%% missing.
-spec qs_val(binary(), #http_req{}, Default)
-> {binary() | true | Default, #http_req{}} when Default::any().
qs_val(Name, Req=#http_req{raw_qs=RawQs, qs_vals=undefined}, Default) ->
@@ -104,22 +124,26 @@ qs_val(Name, Req, Default) ->
false -> {Default, Req}
end.
+%% @doc Return the full list of query string values.
-spec qs_vals(#http_req{}) -> {list({binary(), binary() | true}), #http_req{}}.
qs_vals(Req=#http_req{raw_qs=RawQs, qs_vals=undefined}) ->
QsVals = parse_qs(RawQs),
qs_vals(Req#http_req{qs_vals=QsVals});
qs_vals(Req=#http_req{qs_vals=QsVals}) ->
{QsVals, Req}.
+%% @doc Return the raw query string directly taken from the request.
-spec raw_qs(#http_req{}) -> {binary(), #http_req{}}.
raw_qs(Req) ->
{Req#http_req.raw_qs, Req}.
--spec binding(atom(), #http_req{}) -> {binary() | undefined, #http_req{}}.
%% @equiv binding(Name, Req, undefined)
+-spec binding(atom(), #http_req{}) -> {binary() | undefined, #http_req{}}.
binding(Name, Req) ->
binding(Name, Req, undefined).
+%% @doc Return the binding value for the given key obtained when matching
+%% the host and path against the dispatch list, or a default if missing.
-spec binding(atom(), #http_req{}, Default)
-> {binary() | Default, #http_req{}} when Default::any().
binding(Name, Req, Default) ->
@@ -128,16 +152,18 @@ binding(Name, Req, Default) ->
false -> {Default, Req}
end.
+%% @doc Return the full list of binding values.
-spec bindings(#http_req{}) -> {list({atom(), binary()}), #http_req{}}.
bindings(Req) ->
{Req#http_req.bindings, Req}.
+%% @equiv header(Name, Req, undefined)
-spec header(atom() | binary(), #http_req{})
-> {binary() | undefined, #http_req{}}.
-%% @equiv header(Name, Req, undefined)
header(Name, Req) ->
header(Name, Req, undefined).
+%% @doc Return the header value for the given key, or a default if missing.
-spec header(atom() | binary(), #http_req{}, Default)
-> {binary() | Default, #http_req{}} when Default::any().
header(Name, Req, Default) ->
@@ -146,12 +172,15 @@ header(Name, Req, Default) ->
false -> {Default, Req}
end.
+%% @doc Return the full list of headers.
-spec headers(#http_req{}) -> {http_headers(), #http_req{}}.
headers(Req) ->
{Req#http_req.headers, Req}.
%% Request Body API.
+%% @doc Return the full body sent with the request, or <em>{error, badarg}</em>
+%% if no <em>Content-Length</em> is available.
%% @todo We probably want to allow a max length.
-spec body(#http_req{}) -> {ok, binary(), #http_req{}} | {error, atom()}.
body(Req) ->
@@ -163,6 +192,11 @@ body(Req) ->
body(Length2, Req2)
end.
+%% @doc Return <em>Length</em> bytes of the request body.
+%%
+%% You probably shouldn't be calling this function directly, as it expects the
+%% <em>Length</em> argument to be the full size of the body, and will consider
+%% the body to be fully read from the socket.
%% @todo We probably want to configure the timeout.
-spec body(non_neg_integer(), #http_req{})
-> {ok, binary(), #http_req{}} | {error, atom()}.
@@ -177,13 +211,16 @@ body(Length, Req=#http_req{socket=Socket, transport=Transport,
{error, Reason} -> {error, Reason}
end.
+%% @doc Return the full body sent with the reqest, parsed as an
+%% application/x-www-form-urlencoded string. Essentially a POST query string.
-spec body_qs(#http_req{}) -> {list({binary(), binary() | true}), #http_req{}}.
body_qs(Req) ->
{ok, Body, Req2} = body(Req),
{parse_qs(Body), Req2}.
%% Response API.
+%% @doc Send a reply to the client.
-spec reply(http_status(), http_headers(), iodata(), #http_req{})
-> {ok, #http_req{}}.
reply(Code, Headers, Body, Req=#http_req{socket=Socket,
@@ -199,6 +236,8 @@ reply(Code, Headers, Body, Req=#http_req{socket=Socket,
Transport:send(Socket, [Head, Body]),
{ok, Req#http_req{resp_state=done}}.
+%% @doc Initiate the sending of a chunked reply to the client.
+%% @see cowboy_http_req:chunk/2
-spec chunked_reply(http_status(), http_headers(), #http_req{})
-> {ok, #http_req{}}.
chunked_reply(Code, Headers, Req=#http_req{socket=Socket, transport=Transport,
@@ -212,13 +251,21 @@ chunked_reply(Code, Headers, Req=#http_req{socket=Socket, transport=Transport,
Transport:send(Socket, Head),
{ok, Req#http_req{resp_state=chunks}}.
+%% @doc Send a chunk of data.
+%%
+%% A chunked reply must have been initiated before calling this function.
-spec chunk(iodata(), #http_req{}) -> ok.
chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
<<"\r\n">>, Data, <<"\r\n">>]).
%% Misc API.
+%% @doc Compact the request data by removing all non-system information.
+%%
+%% This essentially removes the host, path, query string, bindings and headers.
+%% Use it when you really need to save up memory, for example when having
+%% many concurrent long-running connections.
-spec compact(#http_req{}) -> #http_req{}.
compact(Req) ->
Req#http_req{host=undefined, host_info=undefined, path=undefined,
View
16 src/cowboy_http_websocket.erl
@@ -12,7 +12,17 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc WebSocket protocol draft hixie-76 implementation.
+%%
+%% Known to work with the following browsers:
+%% <ul>
+%% <li>Mozilla Firefox 4.0 (disabled by default)</li>
+%% <li>Google Chrome 6+</li>
+%% <li>Safari 5.0.1+</li>
+%% <li>Opera 11.00+ (disabled by default)</li>
+%% </ul>
-module(cowboy_http_websocket).
+
-export([upgrade/3]). %% API.
-export([handler_loop/4]). %% Internal.
@@ -30,6 +40,11 @@
hibernate = false :: boolean()
}).
+%% @doc Upgrade a HTTP request to the WebSocket protocol.
+%%
+%% You do not need to call this function manually. To upgrade to the WebSocket
+%% protocol, you simply need to return <em>{upgrade, protocol, {@module}}</em>
+%% in your <em>cowboy_http_handler:init/3</em> handler function.
-spec upgrade(module(), any(), #http_req{}) -> ok.
upgrade(Handler, Opts, Req) ->
EOP = binary:compile_pattern(<< 255 >>),
@@ -128,6 +143,7 @@ handler_before_loop(State, Req=#http_req{socket=Socket, transport=Transport},
Transport:setopts(Socket, [{active, once}]),
handler_loop(State, Req, HandlerState, SoFar).
+%% @private
-spec handler_loop(#state{}, #http_req{}, any(), binary()) -> ok.
handler_loop(State=#state{messages={OK, Closed, Error}, timeout=Timeout},
Req=#http_req{socket=Socket}, HandlerState, SoFar) ->
View
26 src/cowboy_http_websocket_handler.erl
@@ -12,9 +12,35 @@
%% 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 WebSocket requests.
+%%
+%% WebSocket handlers must implement three callbacks: <em>websocket_init/3</em>,
+%% <em>websocket_handle/3</em> and <em>websocket_terminate/3</em>. These
+%% callbacks will only be called if the connection is upgraded to WebSocket
+%% in the HTTP handler's <em>init/3</em> callback. They are then called in that
+%% order, although <em>websocket_handle/3</em> will be called multiple time,
+%% one time for each message or packet received.
+%%
+%% <em>websocket_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. You can define a request-wide state here.
+%% If you are going to want to compact the request, you should probably do it
+%% here.
+%%
+%% <em>websocket_handle/3</em> receives messages sent to the process and
+%% also the data sent to the socket. In the later case the information is
+%% given as a tuple <em>{websocket, Data}</em>. It can reply something, do
+%% nothing or close the connection. You can choose to hibernate the process
+%% by returning <em>hibernate</em> to save memory and CPU.
+%%
+%% <em>websocket_terminate/3</em> is meant for cleaning up. It also receives
+%% the request and the state previously defined, along with a reason for
+%% termination.
-module(cowboy_http_websocket_handler).
+
-export([behaviour_info/1]).
+%% @private
-spec behaviour_info(_) -> undefined | [{websocket_handle, 3}
| {websocket_init, 3} | {websocket_terminate, 3}, ...].
behaviour_info(callbacks) ->
View
1 src/cowboy_listener_sup.erl
@@ -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_listener_sup).
-behaviour(supervisor).
View
1 src/cowboy_requests_sup.erl
@@ -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_requests_sup).
-behaviour(supervisor).
View
57 src/cowboy_ssl_transport.erl
@@ -12,18 +12,50 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc SSL transport API.
+%%
+%% Wrapper around <em>ssl</em> implementing the Cowboy transport API.
+%%
+%% This transport requires the <em>crypto</em>, <em>public_key</em>
+%% and <em>ssl</em> applications to be started. If they aren't started,
+%% it will try to start them itself before opening a port to listen.
+%% Applications aren't stopped when the listening socket is closed, though.
+%%
+%% @see ssl
-module(cowboy_ssl_transport).
-export([name/0, messages/0, listen/1, accept/2, recv/3, send/2, setopts/2,
- controlling_process/2, peername/1, close/1]). %% API.
-
-%% API.
+ controlling_process/2, peername/1, close/1]).
+%% @doc Name of this transport API, <em>ssl</em>.
-spec name() -> ssl.
name() -> ssl.
+%% @doc Atoms used in the process messages sent by this API.
+%%
+%% They identify incoming data, closed connection and errors when receiving
+%% data in active mode.
-spec messages() -> {ssl, ssl_closed, ssl_error}.
messages() -> {ssl, ssl_closed, ssl_error}.
+%% @doc Setup a socket to listen on the given port on the local host.
+%%
+%% The available options are:
+%% <dl>
+%% <dt>port</dt><dd>Mandatory. TCP port number to open.</dd>
+%% <dt>backlog</dt><dd>Maximum length of the pending connections queue.
+%% Defaults to 1024.</dd>
+%% <dt>ip</dt><dd>Interface to listen on. Listen on all interfaces
+%% by default.</dd>
+%% <dt>certfile</dt><dd>Mandatory. Path to a file containing the user's
+%% certificate.</dd>
+%% <dt>keyfile</dt><dd>Mandatory. Path to the file containing the user's
+%% private PEM encoded key.</dd>
+%% <dt>password</dt><dd>Mandatory. String containing the user's password.
+%% All private keyfiles must be password protected currently.</dd>
+%% </dl>
+%%
+%% @see ssl:listen/2
+%% @todo The password option shouldn't be mandatory.
-spec listen([{port, inet:ip_port()} | {certfile, string()}
| {keyfile, string()} | {password, string()}
| {ip, inet:ip_address()}])
@@ -45,6 +77,13 @@ listen(Opts) ->
end,
ssl:listen(Port, ListenOpts).
+%% @doc Accept an incoming connection on a listen socket.
+%%
+%% Note that this function does both the transport accept and
+%% the SSL handshake.
+%%
+%% @see ssl:transport_accept/2
+%% @see ssl:ssl_accept/2
-spec accept(ssl:sslsocket(), timeout())
-> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}.
accept(LSocket, Timeout) ->
@@ -55,29 +94,41 @@ accept(LSocket, Timeout) ->
{error, Reason}
end.
+%% @doc Receive a packet from a socket in passive mode.
+%% @see ssl:recv/3
-spec recv(ssl:sslsocket(), non_neg_integer(), timeout())
-> {ok, any()} | {error, closed | atom()}.
recv(Socket, Length, Timeout) ->
ssl:recv(Socket, Length, Timeout).
+%% @doc Send a packet on a socket.
+%% @see ssl:send/2
-spec send(ssl:sslsocket(), iolist()) -> ok | {error, atom()}.
send(Socket, Packet) ->
ssl:send(Socket, Packet).
+%% @doc Set one or more options for a socket.
+%% @see ssl:setopts/2
-spec setopts(ssl:sslsocket(), list()) -> ok | {error, atom()}.
setopts(Socket, Opts) ->
ssl:setopts(Socket, Opts).
+%% @doc Assign a new controlling process <em>Pid</em> to <em>Socket</em>.
+%% @see ssl:controlling_process/2
-spec controlling_process(ssl:sslsocket(), pid())
-> ok | {error, closed | not_owner | atom()}.
controlling_process(Socket, Pid) ->
ssl:controlling_process(Socket, Pid).
+%% @doc Return the address and port for the other end of a connection.
+%% @see ssl:peername/1
-spec peername(ssl:sslsocket())
-> {ok, {inet:ip_address(), inet:ip_port()}} | {error, atom()}.
peername(Socket) ->
ssl:peername(Socket).
+%% @doc Close a TCP socket.
+%% @see ssl:close/1
-spec close(ssl:sslsocket()) -> ok.
close(Socket) ->
ssl:close(Socket).
View
1 src/cowboy_sup.erl
@@ -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_sup).
-behaviour(supervisor).
View
41 src/cowboy_tcp_transport.erl
@@ -12,18 +12,39 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%% @doc TCP transport API.
+%%
+%% Wrapper around <em>gen_tcp</em> implementing the Cowboy transport API.
+%%
+%% @see gen_tcp
-module(cowboy_tcp_transport).
--export([name/0, messages/0, listen/1, accept/2, recv/3, send/2, setopts/2,
- controlling_process/2, peername/1, close/1]). %% API.
-%% API.
+-export([name/0, messages/0, listen/1, accept/2, recv/3, send/2, setopts/2,
+ controlling_process/2, peername/1, close/1]).
+%% @doc Name of this transport API, <em>tcp</em>.
-spec name() -> tcp.
name() -> tcp.
+%% @doc Atoms used in the process messages sent by this API.
+%%
+%% They identify incoming data, closed connection and errors when receiving
+%% data in active mode.
-spec messages() -> {tcp, tcp_closed, tcp_error}.
messages() -> {tcp, tcp_closed, tcp_error}.
+%% @doc Setup a socket to listen on the given port on the local host.
+%%
+%% The available options are:
+%% <dl>
+%% <dt>port</dt><dd>Mandatory. TCP port number to open.</dd>
+%% <dt>backlog</dt><dd>Maximum length of the pending connections queue.
+%% Defaults to 1024.</dd>
+%% <dt>ip</dt><dd>Interface to listen on. Listen on all interfaces
+%% by default.</dd>
+%% </dl>
+%%
+%% @see gen_tcp:listen/2
-spec listen([{port, inet:ip_port()} | {ip, inet:ip_address()}])
-> {ok, inet:socket()} | {error, atom()}.
listen(Opts) ->
@@ -38,34 +59,48 @@ listen(Opts) ->
end,
gen_tcp:listen(Port, ListenOpts).
+%% @doc Accept an incoming connection on a listen socket.
+%% @see gen_tcp:accept/2
-spec accept(inet:socket(), timeout())
-> {ok, inet:socket()} | {error, closed | timeout | atom()}.
accept(LSocket, Timeout) ->
gen_tcp:accept(LSocket, Timeout).
+%% @doc Receive a packet from a socket in passive mode.
+%% @see gen_tcp:recv/3
-spec recv(inet:socket(), non_neg_integer(), timeout())
-> {ok, any()} | {error, closed | atom()}.
recv(Socket, Length, Timeout) ->
gen_tcp:recv(Socket, Length, Timeout).
+%% @doc Send a packet on a socket.
+%% @see gen_tcp:send/2
-spec send(inet:socket(), iolist()) -> ok | {error, atom()}.
send(Socket, Packet) ->
gen_tcp:send(Socket, Packet).
+%% @doc Set one or more options for a socket.
+%% @see inet:setopts/2
-spec setopts(inet:socket(), list()) -> ok | {error, atom()}.
setopts(Socket, Opts) ->
inet:setopts(Socket, Opts).
+%% @doc Assign a new controlling process <em>Pid</em> to <em>Socket</em>.
+%% @see gen_tcp:controlling_process/2
-spec controlling_process(inet:socket(), pid())
-> ok | {error, closed | not_owner | atom()}.
controlling_process(Socket, Pid) ->
gen_tcp:controlling_process(Socket, Pid).
+%% @doc Return the address and port for the other end of a connection.
+%% @see inet:peername/1
-spec peername(inet:socket())
-> {ok, {inet:ip_address(), inet:ip_port()}} | {error, atom()}.
peername(Socket) ->
inet:peername(Socket).
+%% @doc Close a TCP socket.
+%% @see gen_tcp:close/1
-spec close(inet:socket()) -> ok.
close(Socket) ->
gen_tcp:close(Socket).

0 comments on commit 108a491

Please sign in to comment.
Something went wrong with that request. Please try again.