aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile3
-rw-r--r--doc/overview.edoc4
-rw-r--r--src/cowboy.erl25
-rw-r--r--src/cowboy_acceptor.erl2
-rw-r--r--src/cowboy_acceptors_sup.erl1
-rw-r--r--src/cowboy_app.erl1
-rw-r--r--src/cowboy_clock.erl17
-rw-r--r--src/cowboy_dispatcher.erl33
-rw-r--r--src/cowboy_http_handler.erl25
-rw-r--r--src/cowboy_http_protocol.erl20
-rw-r--r--src/cowboy_http_req.erl53
-rw-r--r--src/cowboy_http_websocket.erl16
-rw-r--r--src/cowboy_http_websocket_handler.erl26
-rw-r--r--src/cowboy_listener_sup.erl1
-rw-r--r--src/cowboy_requests_sup.erl1
-rw-r--r--src/cowboy_ssl_transport.erl57
-rw-r--r--src/cowboy_sup.erl1
-rw-r--r--src/cowboy_tcp_transport.erl41
19 files changed, 319 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
index d38fc51..64a028f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,9 @@
.cowboy_dialyzer.plt
.eunit
+doc/*.css
+doc/*.html
+doc/*.png
+doc/edoc-info
ebin
logs
test/*.beam
diff --git a/Makefile b/Makefile
index 04f12d7..e96d5de 100644
--- a/Makefile
+++ b/Makefile
@@ -29,3 +29,6 @@ dialyze:
@$(DIALYZER) --src src --plt .cowboy_dialyzer.plt \
-Wbehaviours -Werror_handling \
-Wrace_conditions -Wunmatched_returns # -Wunderspecs
+
+docs:
+ @$(REBAR) doc
diff --git a/doc/overview.edoc b/doc/overview.edoc
new file mode 100644
index 0000000..56648c4
--- /dev/null
+++ b/doc/overview.edoc
@@ -0,0 +1,4 @@
+@author Lo�c Hoguin <[email protected]>
+@copyright 2011 Lo�c Hoguin
+@version HEAD
+@title Small, fast, modular HTTP server.
diff --git a/src/cowboy.erl b/src/cowboy.erl
index a1e8063..dbc64d0 100644
--- a/src/cowboy.erl
+++ b/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
diff --git a/src/cowboy_acceptor.erl b/src/cowboy_acceptor.erl
index 830828e..cc8dfa3 100644
--- a/src/cowboy_acceptor.erl
+++ b/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.
diff --git a/src/cowboy_acceptors_sup.erl b/src/cowboy_acceptors_sup.erl
index d0ab77e..c12aeb5 100644
--- a/src/cowboy_acceptors_sup.erl
+++ b/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).
diff --git a/src/cowboy_app.erl b/src/cowboy_app.erl
index 114eb9a..0ff08f0 100644
--- a/src/cowboy_app.erl
+++ b/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).
diff --git a/src/cowboy_clock.erl b/src/cowboy_clock.erl
index cc824ed..e028559 100644
--- a/src/cowboy_clock.erl
+++ b/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}.
diff --git a/src/cowboy_dispatcher.erl b/src/cowboy_dispatcher.erl
index e34a4eb..718b13e 100644
--- a/src/cowboy_dispatcher.erl
+++ b/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(),
diff --git a/src/cowboy_http_handler.erl b/src/cowboy_http_handler.erl
index 67f07bc..b220b09 100644
--- a/src/cowboy_http_handler.erl
+++ b/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) ->
diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl
index 51be028..779866a 100644
--- a/src/cowboy_http_protocol.erl
+++ b/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,6 +50,7 @@
%% 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]),
@@ -40,6 +58,7 @@ start_link(Socket, Transport, Opts) ->
%% 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}) ->
diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl
index c9aa5c7..601f839 100644
--- a/src/cowboy_http_req.erl
+++ b/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,14 +48,17 @@
%% 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),
@@ -57,42 +66,53 @@ peer(Req=#http_req{socket=Socket, transport=Transport, peer=undefined}) ->
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,6 +124,7 @@ 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),
@@ -111,15 +132,18 @@ qs_vals(Req=#http_req{raw_qs=RawQs, qs_vals=undefined}) ->
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,6 +211,8 @@ 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),
@@ -184,6 +220,7 @@ body_qs(Req) ->
%% 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,6 +251,9 @@ 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),
@@ -219,6 +261,11 @@ chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
%% 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,
diff --git a/src/cowboy_http_websocket.erl b/src/cowboy_http_websocket.erl
index 8463ff5..da1622c 100644
--- a/src/cowboy_http_websocket.erl
+++ b/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) ->
diff --git a/src/cowboy_http_websocket_handler.erl b/src/cowboy_http_websocket_handler.erl
index 30cb2e1..b02c28d 100644
--- a/src/cowboy_http_websocket_handler.erl
+++ b/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) ->
diff --git a/src/cowboy_listener_sup.erl b/src/cowboy_listener_sup.erl
index 248d7df..0ed662f 100644
--- a/src/cowboy_listener_sup.erl
+++ b/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).
diff --git a/src/cowboy_requests_sup.erl b/src/cowboy_requests_sup.erl
index 1ad3a52..a50ee8a 100644
--- a/src/cowboy_requests_sup.erl
+++ b/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).
diff --git a/src/cowboy_ssl_transport.erl b/src/cowboy_ssl_transport.erl
index 8e569ec..098d409 100644
--- a/src/cowboy_ssl_transport.erl
+++ b/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).
diff --git a/src/cowboy_sup.erl b/src/cowboy_sup.erl
index 13977e7..9c52486 100644
--- a/src/cowboy_sup.erl
+++ b/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).
diff --git a/src/cowboy_tcp_transport.erl b/src/cowboy_tcp_transport.erl
index 1597b88..c1dad62 100644
--- a/src/cowboy_tcp_transport.erl
+++ b/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).