aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md47
-rw-r--r--guide/introduction.md114
-rw-r--r--guide/toc.md47
-rw-r--r--src/cowboy.app.src3
-rw-r--r--src/cowboy_rest.erl44
-rw-r--r--src/cowboy_static.erl18
6 files changed, 221 insertions, 52 deletions
diff --git a/README.md b/README.md
index e6556be..4ff5ea4 100644
--- a/README.md
+++ b/README.md
@@ -6,35 +6,40 @@ Cowboy is a small, fast and modular HTTP server written in Erlang.
Goals
-----
-Cowboy aims to provide the following advantages:
-
-* **Small** code base.
-* Damn **fast**.
-* **Modular**: transport and protocol handlers are replaceable.
-* **Binary HTTP** for greater speed and lower memory usage.
-* Easy to **embed** inside another application.
-* Selectively **dispatch** requests to handlers, allowing you to send some
- requests to your embedded code and others to a FastCGI application in
- PHP or Ruby.
-* No parameterized module. No process dictionary. **Clean** Erlang code.
-
-The server is currently in early development. Comments and suggestions are
-more than welcome. To contribute, either open bug reports, or fork the project
-and send us pull requests with new or improved functionality. You should
-discuss your plans with us before doing any serious work, though, to avoid
-duplicating efforts.
+Cowboy aims to provide a **complete** HTTP stack in a **small** code base.
+It is optimized for **low latency** and **low memory usage**, in parts
+because it uses **binary strings**.
+
+Cowboy provides **routing** capabilities, selectively dispatching requests
+to handlers written in Erlang.
+
+Because it uses Ranch for managing connections, Cowboy can easily be
+**embedded** in any other application.
+
+No parameterized module. No process dictionary. **Clean** Erlang code.
Quick start
-----------
-* Add Cowboy as a rebar or agner dependency to your application.
-* Start Cowboy and add one or more listeners.
-* Write handlers for your application.
-* Check out the `examples/` directory!
+ * Add Cowboy as a rebar dependency to your application.
+ * Start Cowboy and add one or more listeners.
+ * Write handlers for your application.
Getting Started
---------------
+ * [Read the guide](http://ninenines.eu/docs/en/cowboy/HEAD/guide/introduction)
+ * Look at the examples in the `examples/` directory
+ * Build API documentation with `make docs`; open `doc/index.html`
+
+
+
+Old README
+----------
+
+This and all following sections will be removed as soon as their
+equivalent appear in the Cowboy guide.
+
Cowboy does nothing by default.
Cowboy uses Ranch for handling connections, and provides convenience
diff --git a/guide/introduction.md b/guide/introduction.md
new file mode 100644
index 0000000..871e243
--- /dev/null
+++ b/guide/introduction.md
@@ -0,0 +1,114 @@
+Introduction
+============
+
+Purpose
+-------
+
+Cowboy is a small, fast and modular HTTP server written in Erlang.
+
+Cowboy aims to provide a complete HTTP stack, including its derivatives
+SPDY, Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1,
+Websocket (all implemented drafts + standard) and Webmachine-based REST.
+
+Cowboy is a high quality project. It has a small code base, is very
+efficient (both in latency and memory use) and can easily be embedded
+in another application.
+
+Cowboy is clean Erlang code. It bans the use of parameterized modules
+and the process dictionary. It includes documentation and typespecs
+for all public interfaces.
+
+Prerequisites
+-------------
+
+It is assumed the developer already knows Erlang and has basic knowledge
+about the HTTP protocol.
+
+In order to run the examples available in this user guide, you will need
+Erlang and rebar installed and in your $PATH.
+
+Please see the [rebar repository](https://github.com/basho/rebar) for
+downloading and building instructions. Please look up the environment
+variables documentation of your system for details on how to update the
+$PATH information.
+
+Conventions
+-----------
+
+In the HTTP protocol, the method name is case sensitive. All standard
+method names are uppercase.
+
+Header names are case insensitive. Cowboy converts all the request
+header names to lowercase, and expects your application to provide
+lowercase header names in the response.
+
+Getting started
+---------------
+
+Cowboy does nothing by default.
+
+Cowboy requires the `crypto` and `ranch` applications to be started.
+
+``` erlang
+ok = application:start(crypto).
+ok = application:start(ranch).
+ok = application:start(cowboy).
+```
+
+Cowboy uses Ranch for handling the connections and provides convenience
+functions to start Ranch listeners.
+
+The `cowboy:start_http/4` function starts a listener for HTTP connections
+using the TCP transport. The `cowboy:start_https/4` function starts a
+listener for HTTPS connections using the SSL transport.
+
+Listeners are named. They spawn a given number of acceptors, listen for
+connections using the given transport options and pass along the protocol
+options to the connection processes. The protocol options must include
+the dispatch list for routing requests to handlers.
+
+The dispatch list is explained in greater details in the Routing section
+of the guide.
+
+``` erlang
+Dispatch = [
+ %% {URIHost, list({URIPath, Handler, Opts})}
+ {'_', [{'_', my_handler, []}]}
+],
+%% Name, NbAcceptors, TransOpts, ProtoOpts
+cowboy:start_http(my_http_listener, 100,
+ [{port, 8080}],
+ [{dispatch, Dispatch}]
+).
+```
+
+Cowboy features many kinds of handlers. It has plain HTTP handlers, loop
+handlers, Websocket handlers, REST handlers and static handlers. Their
+usage is documented in the respective sections of the guide.
+
+Most applications use the plain HTTP handler, which has three callback
+functions: init/3, handle/2 and terminate/2. Following is an example of
+a simple handler module.
+
+``` erlang
+-module(my_handler).
+-behaviour(cowboy_http_handler).
+
+-export([init/3]).
+-export([handle/2]).
+-export([terminate/2]).
+
+init({tcp, http}, Req, Opts) ->
+ {ok, Req, undefined_state}.
+
+handle(Req, State) ->
+ {ok, Req2} = cowboy_req:reply(200, [], <<"Hello World!">>, Req),
+ {ok, Req2, State}.
+
+terminate(Req, State) ->
+ ok.
+```
+
+The `Req` variable above is the Req object, which allows the developer
+to obtain informations about the request and to perform a reply. Its usage
+is explained in its respective section of the guide.
diff --git a/guide/toc.md b/guide/toc.md
new file mode 100644
index 0000000..ea543d1
--- /dev/null
+++ b/guide/toc.md
@@ -0,0 +1,47 @@
+Cowboy User Guide
+=================
+
+ * [Introduction](introduction.md)
+ * Purpose
+ * Prerequisites
+ * Conventions
+ * Getting started
+ * Routing
+ * Purpose
+ * Dispatch rule
+ * Match rules
+ * Bindings
+ * Handlers
+ * Purpose
+ * Protocol upgrades
+ * HTTP handlers
+ * Purpose
+ * Callbacks
+ * Usage
+ * Loop handlers
+ * Purpose
+ * Callbacks
+ * Usage
+ * Websocket handlers
+ * Purpose
+ * Callbacks
+ * Usage
+ * REST handlers
+ * Purpose
+ * Flow diagram
+ * Callbacks
+ * Usage
+ * Static handlers
+ * Purpose
+ * Usage
+ * Request object
+ * Purpose
+ * Request
+ * Request body
+ * Reply
+ * Hooks
+ * On request
+ * On response
+ * Internals
+ * Architecture
+ * Efficiency considerations
diff --git a/src/cowboy.app.src b/src/cowboy.app.src
index b68ef14..d32262e 100644
--- a/src/cowboy.app.src
+++ b/src/cowboy.app.src
@@ -13,7 +13,10 @@
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
{application, cowboy, [
+ {id, "Cowboy"},
{description, "Small, fast, modular HTTP server."},
+ {sub_description, "Cowboy is also a socket acceptor pool, "
+ "able to accept connections for any kind of TCP protocol."},
{vsn, "0.7.0"},
{modules, []},
{registered, [cowboy_clock, cowboy_sup]},
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index c0decbd..1c0554a 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -73,7 +73,7 @@ upgrade(_ListenerPid, Handler, Opts, Req) ->
catch Class:Reason ->
PLReq = cowboy_req:to_list(Req),
error_logger:error_msg(
- "** Handler ~p terminating in rest_init/3~n"
+ "** Handler ~p terminating in rest_init/2~n"
" for the reason ~p:~p~n** Options were ~p~n"
"** Request was ~p~n** Stacktrace: ~p~n~n",
[Handler, Class, Reason, Opts, PLReq, erlang:get_stacktrace()]),
@@ -84,7 +84,7 @@ upgrade(_ListenerPid, Handler, Opts, Req) ->
service_available(Req, State) ->
expect(Req, State, service_available, true, fun known_methods/2, 503).
-%% known_methods/2 should return a list of atoms or binary methods.
+%% known_methods/2 should return a list of binary methods.
known_methods(Req, State=#state{method=Method}) ->
case call(Req, State, known_methods) of
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
@@ -107,7 +107,7 @@ known_methods(Req, State=#state{method=Method}) ->
uri_too_long(Req, State) ->
expect(Req, State, uri_too_long, false, fun allowed_methods/2, 414).
-%% allowed_methods/2 should return a list of atoms or binary methods.
+%% allowed_methods/2 should return a list of binary methods.
allowed_methods(Req, State=#state{method=Method}) ->
case call(Req, State, allowed_methods) of
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> ->
@@ -126,7 +126,7 @@ allowed_methods(Req, State=#state{method=Method}) ->
method_not_allowed(Req, State, Methods) ->
Req2 = cowboy_req:set_resp_header(
- <<"Allow">>, method_not_allowed_build(Methods, []), Req),
+ <<"allow">>, method_not_allowed_build(Methods, []), Req),
respond(Req2, State, 405).
method_not_allowed_build([], []) ->
@@ -153,7 +153,7 @@ is_authorized(Req, State) ->
forbidden(Req2, State#state{handler_state=HandlerState});
{{false, AuthHead}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Www-Authenticate">>, AuthHead, Req2),
+ <<"www-authenticate">>, AuthHead, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 401)
end.
@@ -166,7 +166,7 @@ valid_content_headers(Req, State) ->
known_content_type(Req, State) ->
expect(Req, State, known_content_type, true,
- fun valid_entity_length/2, 413).
+ fun valid_entity_length/2, 415).
valid_entity_length(Req, State) ->
expect(Req, State, valid_entity_length, true, fun options/2, 413).
@@ -351,7 +351,7 @@ match_language(Req, State, Accept, [Provided|Tail],
end.
set_language(Req, State=#state{language_a=Language}) ->
- Req2 = cowboy_req:set_resp_header(<<"Content-Language">>, Language, Req),
+ Req2 = cowboy_req:set_resp_header(<<"content-language">>, Language, Req),
charsets_provided(cowboy_req:set_meta(language, Language, Req2), State).
%% charsets_provided should return a list of binary values indicating
@@ -415,7 +415,7 @@ set_content_type(Req, State=#state{
undefined -> ContentType;
Charset -> [ContentType, <<"; charset=">>, Charset]
end,
- Req2 = cowboy_req:set_resp_header(<<"Content-Type">>, ContentType2, Req),
+ Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
encodings_provided(cowboy_req:set_meta(charset, Charset, Req2), State).
set_content_type_build_params([], []) ->
@@ -446,17 +446,17 @@ variances(Req, State=#state{content_types_p=CTP,
Variances = case CTP of
[] -> [];
[_] -> [];
- [_|_] -> [<<"Accept">>]
+ [_|_] -> [<<"accept">>]
end,
Variances2 = case LP of
[] -> Variances;
[_] -> Variances;
- [_|_] -> [<<"Accept-Language">>|Variances]
+ [_|_] -> [<<"accept-language">>|Variances]
end,
Variances3 = case CP of
[] -> Variances2;
[_] -> Variances2;
- [_|_] -> [<<"Accept-Charset">>|Variances2]
+ [_|_] -> [<<"accept-charset">>|Variances2]
end,
{Variances4, Req3, State2} = case call(Req, State, variances) of
no_call ->
@@ -470,13 +470,13 @@ variances(Req, State=#state{content_types_p=CTP,
resource_exists(Req3, State2);
[[<<", ">>, H]|Variances5] ->
Req4 = cowboy_req:set_resp_header(
- <<"Vary">>, [H|Variances5], Req3),
+ <<"vary">>, [H|Variances5], Req3),
resource_exists(Req4, State2)
end.
resource_exists(Req, State) ->
expect(Req, State, resource_exists, true,
- fun if_match_exists/2, fun if_match_musnt_exist/2).
+ fun if_match_exists/2, fun if_match_must_not_exist/2).
if_match_exists(Req, State) ->
case cowboy_req:parse_header(<<"if-match">>, Req) of
@@ -496,7 +496,7 @@ if_match(Req, State, EtagsList) ->
false -> precondition_failed(Req2, State2)
end.
-if_match_musnt_exist(Req, State) ->
+if_match_must_not_exist(Req, State) ->
case cowboy_req:header(<<"if-match">>, Req) of
{undefined, Req2} -> is_put_to_missing_resource(Req2, State);
{_Any, Req2} -> precondition_failed(Req2, State)
@@ -577,7 +577,7 @@ if_modified_since(Req, State, IfModifiedSince) ->
end.
not_modified(Req, State) ->
- Req2 = cowboy_req:delete_resp_header(<<"Content-Type">>, Req),
+ Req2 = cowboy_req:delete_resp_header(<<"content-type">>, Req),
{Req3, State2} = set_resp_etag(Req2, State),
{Req4, State3} = set_resp_expires(Req3, State2),
respond(Req4, State3, 304).
@@ -596,7 +596,7 @@ moved_permanently(Req, State, OnFalse) ->
case call(Req, State, moved_permanently) of
{{true, Location}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Location">>, Location, Req2),
+ <<"location">>, Location, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 301);
{false, Req2, HandlerState} ->
OnFalse(Req2, State#state{handler_state=HandlerState});
@@ -617,7 +617,7 @@ moved_temporarily(Req, State) ->
case call(Req, State, moved_temporarily) of
{{true, Location}, Req2, HandlerState} ->
Req3 = cowboy_req:set_resp_header(
- <<"Location">>, Location, Req2),
+ <<"location">>, Location, Req2),
respond(Req3, State#state{handler_state=HandlerState}, 307);
{false, Req2, HandlerState} ->
is_post_to_missing_resource(Req2, State#state{handler_state=HandlerState}, 410);
@@ -670,7 +670,7 @@ create_path(Req, State) ->
{HostURL, Req3} = cowboy_req:host_url(Req2),
State2 = State#state{handler_state=HandlerState},
Req4 = cowboy_req:set_resp_header(
- <<"Location">>, << HostURL/binary, Path/binary >>, Req3),
+ <<"location">>, << HostURL/binary, Path/binary >>, Req3),
put_resource(cowboy_req:set_meta(put_path, Path, Req4),
State2, 303)
end.
@@ -744,7 +744,7 @@ choose_content_type(Req, State, OnTrue, ContentType, [_Any|Tail]) ->
%% This is easily testable because we would have set the Location
%% header by this point if we did so.
is_new_resource(Req, State) ->
- case cowboy_req:has_resp_header(<<"Location">>, Req) of
+ case cowboy_req:has_resp_header(<<"location">>, Req) of
true -> respond(Req, State, 201);
false -> has_resp_body(Req, State)
end.
@@ -767,7 +767,7 @@ set_resp_body(Req, State=#state{content_type_a={_Type, Fun}}) ->
LastModified ->
LastModifiedStr = httpd_util:rfc1123_date(LastModified),
Req4 = cowboy_req:set_resp_header(
- <<"Last-Modified">>, LastModifiedStr, Req3)
+ <<"last-modified">>, LastModifiedStr, Req3)
end,
{Req5, State4} = set_resp_expires(Req4, State3),
case call(Req5, State4, Fun) of
@@ -796,7 +796,7 @@ set_resp_etag(Req, State) ->
{Req2, State2};
Etag ->
Req3 = cowboy_req:set_resp_header(
- <<"ETag">>, encode_etag(Etag), Req2),
+ <<"etag">>, encode_etag(Etag), Req2),
{Req3, State2}
end.
@@ -812,7 +812,7 @@ set_resp_expires(Req, State) ->
Expires ->
ExpiresStr = httpd_util:rfc1123_date(Expires),
Req3 = cowboy_req:set_resp_header(
- <<"Expires">>, ExpiresStr, Req2),
+ <<"expires">>, ExpiresStr, Req2),
{Req3, State2}
end.
diff --git a/src/cowboy_static.erl b/src/cowboy_static.erl
index 5450115..724bf33 100644
--- a/src/cowboy_static.erl
+++ b/src/cowboy_static.erl
@@ -81,9 +81,9 @@
%% {<<".js">>, [<<"application/javascript">>]}]}]}
%%
%% %% Use the default database in the mimetypes application.
-%% {[<<"static">>, '...', cowboy_static,
+%% {[<<"static">>, '...'], cowboy_static,
%% [{directory, {priv_dir, cowboy, []}},
-%% {mimetypes, {fun mimetypes:path_to_mimes/2, default}}]]}
+%% {mimetypes, {fun mimetypes:path_to_mimes/2, default}}]}
%% '''
%%
%% == ETag Header Function ==
@@ -110,19 +110,19 @@
%% ==== Examples ====
%% ```
%% %% A value of default is equal to not specifying the option.
-%% {[<<"static">>, '...', cowboy_static,
+%% {[<<"static">>, '...'], cowboy_static,
%% [{directory, {priv_dir, cowboy, []}},
-%% {etag, default}]]}
+%% {etag, default}]}
%%
%% %% Use all avaliable ETag function arguments to generate a header value.
-%% {[<<"static">>, '...', cowboy_static,
+%% {[<<"static">>, '...'], cowboy_static,
%% [{directory, {priv_dir, cowboy, []}},
-%% {etag, {attributes, [filepath, filesize, inode, mtime]}}]]}
+%% {etag, {attributes, [filepath, filesize, inode, mtime]}}]}
%%
%% %% Use a user defined function to generate a strong ETag header value.
-%% {[<<"static">>, '...', cowboy_static,
+%% {[<<"static">>, '...'], cowboy_static,
%% [{directory, {priv_dir, cowboy, []}},
-%% {etag, {fun generate_strong_etag/2, strong_etag_extra}}]]}
+%% {etag, {fun generate_strong_etag/2, strong_etag_extra}}]}
%%
%% generate_strong_etag(Arguments, strong_etag_extra) ->
%% {_, Filepath} = lists:keyfind(filepath, 1, Arguments),
@@ -451,7 +451,7 @@ path_to_mimetypes(Filepath, Extensions) when is_binary(Filepath) ->
-spec path_to_mimetypes_(binary(), [{binary(), [mimedef()]}]) -> [mimedef()].
path_to_mimetypes_(Ext, Extensions) ->
- case lists:keyfind(Ext, 1, Extensions) of
+ case lists:keyfind(cowboy_bstr:to_lower(Ext), 1, Extensions) of
{_, MTs} -> MTs;
_Unknown -> default_mimetype()
end.