mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Greatly improve the README.
Forward me any more questions and I'll continue on improving it.
This commit is contained in:
parent
470baff61f
commit
5a7b7429ff
1 changed files with 181 additions and 38 deletions
219
README.md
219
README.md
|
@ -8,9 +8,10 @@ Goals
|
||||||
|
|
||||||
Cowboy aims to provide the following advantages:
|
Cowboy aims to provide the following advantages:
|
||||||
|
|
||||||
* **Small** codebase.
|
* **Small** code base.
|
||||||
* Damn **fast**.
|
* Damn **fast**.
|
||||||
* **Modular**: transport, protocol and handlers are replaceable. (see below)
|
* **Modular**: transport and protocol handlers are replaceable.
|
||||||
|
* **Binary HTTP** for greater speed and lower memory usage.
|
||||||
* Easy to **embed** inside another application.
|
* Easy to **embed** inside another application.
|
||||||
* Selectively **dispatch** requests to handlers, allowing you to send some
|
* Selectively **dispatch** requests to handlers, allowing you to send some
|
||||||
requests to your embedded code and others to a FastCGI application in
|
requests to your embedded code and others to a FastCGI application in
|
||||||
|
@ -19,69 +20,211 @@ Cowboy aims to provide the following advantages:
|
||||||
|
|
||||||
The server is currently in early development stage. Comments, suggestions are
|
The server is currently in early development stage. Comments, suggestions are
|
||||||
more than welcome. To contribute, either open bug reports, or fork the project
|
more than welcome. To contribute, either open bug reports, or fork the project
|
||||||
and send us pull requests with new or improved functionality. Of course you
|
and send us pull requests with new or improved functionality. You should
|
||||||
might want to discuss your plans with us before you do any serious work so
|
discuss your plans with us before doing any serious work, though, to avoid
|
||||||
we can share ideas and save everyone time.
|
duplicating efforts.
|
||||||
|
|
||||||
Embedding Cowboy
|
Quick start
|
||||||
----------------
|
-----------
|
||||||
|
|
||||||
* Add Cowboy as a rebar or agner dependency to your application.
|
* Add Cowboy as a rebar or agner dependency to your application.
|
||||||
* Start Cowboy and add one or more listeners.
|
* Start Cowboy and add one or more listeners.
|
||||||
* Write handlers.
|
* Write handlers for your application.
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Cowboy can be started and stopped like any other application. However, the
|
At heart, Cowboy is nothing more than an TCP acceptor pool. All it does is
|
||||||
Cowboy application does not start any listener, those must be started manually.
|
accept connections received on a given port and using a given transport,
|
||||||
|
like TCP or SSL, and forward them to a request handler for the given
|
||||||
|
protocol. Acceptors and request handlers are of course supervised
|
||||||
|
automatically.
|
||||||
|
|
||||||
A listener is a special kind of supervisor that handles a pool of acceptor
|
It just so happens that Cowboy also includes an HTTP protocol handler.
|
||||||
processes. It also manages all its associated request processes. This allows
|
But Cowboy does nothing by default. You need to explicitly ask Cowboy
|
||||||
you to shutdown all processes related to a listener by stopping the supervisor.
|
to listen on a port with your chosen transport and protocol handlers.
|
||||||
|
To do so, you must start a listener.
|
||||||
|
|
||||||
An acceptor simply accepts connections and forwards them to a protocol module,
|
A listener is a special kind of supervisor that manages both the
|
||||||
for example HTTP. You must thus define the transport and protocol module to
|
acceptor pool and the request processes. It is named and can thus be
|
||||||
use for the listener, their options and the number of acceptors in the pool
|
started and stopped at will.
|
||||||
before you can start a listener supervisor.
|
|
||||||
|
|
||||||
For HTTP applications the transport can be either TCP or SSL for HTTP and
|
An acceptor pool is a pool of processes whose only role is to accept
|
||||||
HTTPS respectively. On the other hand, the protocol is of course HTTP.
|
new connections. It's good practice to have many of these processes
|
||||||
|
as they are very cheap and allow much quicker response when you get
|
||||||
|
many connections. Of course, as with everything else, you should
|
||||||
|
**benchmark** before you decide what's best for you.
|
||||||
|
|
||||||
|
Cowboy includes a TCP transport handler for HTTP and an SSL transport
|
||||||
|
handler for HTTPS. The transport handlers can of course be reused for
|
||||||
|
other protocols like FTP or IRC.
|
||||||
|
|
||||||
|
The HTTP protocol requires one last thing to continue: dispatching rules.
|
||||||
|
Don't worry about it right now though and continue reading, it'll all
|
||||||
|
be explained.
|
||||||
|
|
||||||
You can start and stop listeners by calling cowboy:start_listener and
|
You can start and stop listeners by calling cowboy:start_listener and
|
||||||
cowboy:stop_listener respectively. It is your responsability to give each
|
cowboy:stop_listener respectively, as demonstrated in the following
|
||||||
listener a unique name.
|
example.
|
||||||
|
|
||||||
Code speaks more than words:
|
|
||||||
|
|
||||||
``` erlang
|
``` erlang
|
||||||
application:start(cowboy),
|
-module(my_app).
|
||||||
Dispatch = [
|
-behaviour(application).
|
||||||
%% {Host, list({Path, Handler, Opts})}
|
-export([start/2, stop/1]).
|
||||||
{'_', [{'_', my_handler, []}]}
|
|
||||||
],
|
start(_Type, _Args) ->
|
||||||
%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
|
application:start(cowboy),
|
||||||
cowboy:start_listener(http, 100,
|
Dispatch = [
|
||||||
cowboy_tcp_transport, [{port, 8080}],
|
%% {Host, list({Path, Handler, Opts})}
|
||||||
cowboy_http_protocol, [{dispatch, Dispatch}]
|
{'_', [{'_', my_handler, []}]}
|
||||||
).
|
],
|
||||||
|
%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
|
||||||
|
cowboy:start_listener(http, 100,
|
||||||
|
cowboy_tcp_transport, [{port, 8080}],
|
||||||
|
cowboy_http_protocol, [{dispatch, Dispatch}]
|
||||||
|
).
|
||||||
|
|
||||||
|
stop(_State) ->
|
||||||
|
ok.
|
||||||
```
|
```
|
||||||
|
|
||||||
You must also write the `my_handler` module to process requests. You can
|
This is not enough though, you must also write the my_handler module
|
||||||
use one of the predefined handlers or write your own. An hello world HTTP
|
to process the incoming HTTP requests. Of course Cowboy comes with
|
||||||
handler could be written like this:
|
predefined handlers for specific tasks but most of the time you'll
|
||||||
|
want to write your own handlers for your application.
|
||||||
|
|
||||||
|
Following is an example of an "Hello World!" HTTP handler.
|
||||||
|
|
||||||
``` erlang
|
``` erlang
|
||||||
-module(my_handler).
|
-module(my_handler).
|
||||||
|
-behaviour(cowboy_http_handler).
|
||||||
-export([init/3, handle/2, terminate/2]).
|
-export([init/3, handle/2, terminate/2]).
|
||||||
|
|
||||||
init({tcp, http}, Req, Opts) ->
|
init({tcp, http}, Req, Opts) ->
|
||||||
{ok, Req, undefined}.
|
{ok, Req, undefined_state}.
|
||||||
|
|
||||||
handle(Req, State) ->
|
handle(Req, State) ->
|
||||||
{ok, Req2} = cowboy_http_req:reply(200, [], "Hello World!", Req),
|
{ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello World!">>, Req),
|
||||||
{ok, Req2, State}.
|
{ok, Req2, State}.
|
||||||
|
|
||||||
terminate(Req, State) ->
|
terminate(Req, State) ->
|
||||||
ok.
|
ok.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Continue reading to learn how to dispatch rules and handle requests.
|
||||||
|
|
||||||
|
Dispatch rules
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Cowboy allows you to dispatch HTTP requests directly to a specific handler
|
||||||
|
based on the hostname and path information from the request. It also lets
|
||||||
|
you define static options for the handler directly in the rules.
|
||||||
|
|
||||||
|
To match the hostname and path, Cowboy requires a list of tokens. For
|
||||||
|
example, to match the "dev-extend.eu" domain name, you must specify
|
||||||
|
[<<"dev-extend">>, <<"eu">>]. Or, to match the "/path/to/my/resource"
|
||||||
|
you must use [<<"path">>, <<"to">>, <<"my">>, <<"resource">>]. All the
|
||||||
|
tokens must be given as binary.
|
||||||
|
|
||||||
|
You can use the special token '_' (the atom underscore) to indicate that
|
||||||
|
you accept anything in that position. For example if you have both
|
||||||
|
"dev-extend.eu" and "dev-extend.fr" domains, you can use the match spec
|
||||||
|
[<<"dev-extend">>, '_'] to match any top level extension.
|
||||||
|
|
||||||
|
Any other atom used as a token will bind the value to this atom when
|
||||||
|
matching. To follow on our hostnames example, [<<"dev-extend">>, ext]
|
||||||
|
would bind the values <<"eu">> and <<"fr">> to the ext atom, that you
|
||||||
|
can later retrieve in your handler by calling `cowboy_http_req:binding/{2,3}`.
|
||||||
|
|
||||||
|
You can also accept any match spec by using the atom '_' directly instead of
|
||||||
|
a list of tokens. Our hello world example above uses this to forward all
|
||||||
|
requests to a single handler.
|
||||||
|
|
||||||
|
There is currently no way to match multiple tokens at once.
|
||||||
|
|
||||||
|
Requests handling
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Requests are passed around in the Request variable. Although they are
|
||||||
|
defined as a record, it is recommended to access them only through the
|
||||||
|
cowboy_http_req module API.
|
||||||
|
|
||||||
|
You can retrieve the HTTP method, HTTP version, peer address and port,
|
||||||
|
host tokens, raw host, used port, path tokens, raw path, query string
|
||||||
|
values, bound values from the dispatch step, header values from the
|
||||||
|
request. You can also read the request body, if any, optionally parsing
|
||||||
|
it as a query string. Finally, the request allows you to send a response
|
||||||
|
to the client.
|
||||||
|
|
||||||
|
See the cowboy_http_req module for more information.
|
||||||
|
|
||||||
|
Websockets
|
||||||
|
----------
|
||||||
|
|
||||||
|
The Websocket protocol is built upon the HTTP protocol. It first sends
|
||||||
|
an HTTP request for an handshake, performs it and then switches
|
||||||
|
to Websocket. Therefore you need to write a standard HTTP handler to
|
||||||
|
confirm the handshake should be completed and then the Websocket-specific
|
||||||
|
callbacks.
|
||||||
|
|
||||||
|
A simple handler doing nothing but sending a repetitive message using
|
||||||
|
Websocket would look like this:
|
||||||
|
|
||||||
|
``` erlang
|
||||||
|
-module(my_ws_handler).
|
||||||
|
-behaviour(cowboy_http_handler).
|
||||||
|
-behaviour(cowboy_http_websocket_handler).
|
||||||
|
-export([init/3, handle/2, terminate/2]).
|
||||||
|
-export([websocket_init/3, websocket_handle/3, websocket_terminate/3]).
|
||||||
|
|
||||||
|
init({tcp, http}, Req, Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_http_websocket}.
|
||||||
|
|
||||||
|
handle(Req, State) ->
|
||||||
|
error(foo). %% Will never be called.
|
||||||
|
|
||||||
|
terminate(Req, State) ->
|
||||||
|
error(foo). %% Same for that one.
|
||||||
|
|
||||||
|
websocket_init(TransportName, Req, _Opts) ->
|
||||||
|
erlang:start_timer(1000, self(), <<"Hello!">>),
|
||||||
|
{ok, Req, undefined_state}.
|
||||||
|
|
||||||
|
websocket_handle({timeout, _Ref, Msg}, Req, State) ->
|
||||||
|
erlang:start_timer(1000, self(), <<"How' you doin'?">>),
|
||||||
|
{reply, Msg, Req, State};
|
||||||
|
websocket_handle({websocket, Msg}, Req, State) ->
|
||||||
|
{reply, <<"That's what she said! ", Msg/binary >>, Req, State}.
|
||||||
|
|
||||||
|
websocket_terminate(_Reason, _Req, _State) ->
|
||||||
|
ok.
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course you can have an HTTP handler doing both HTTP and Websocket
|
||||||
|
handling, but for the sake of this example we're ignoring the HTTP
|
||||||
|
part entirely.
|
||||||
|
|
||||||
|
Using Cowboy with other protocols
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
One of the strength of Cowboy is of course that you can use it with any
|
||||||
|
protocol you want. The only downside is that if it's not HTTP, you'll
|
||||||
|
probably have to write the protocol handler yourself.
|
||||||
|
|
||||||
|
The only exported function a protocol handler needs is the start_link/3
|
||||||
|
function, with arguments Socket, Transport and Opts. Socket is of course
|
||||||
|
the client socket; Transport is the module name of the chosen transport
|
||||||
|
handler and Opts is protocol options defined when starting the listener.
|
||||||
|
Anything you do past this point is up to you!
|
||||||
|
|
||||||
|
You should definitely look at the cowboy_http_protocol module for a great
|
||||||
|
example of fast requests handling if you need to. Otherwise it's probably
|
||||||
|
safe to use {active, once} mode and handle everything as it comes.
|
||||||
|
|
||||||
|
Note that while you technically can run a protocol handler directly as a
|
||||||
|
gen_server or a gen_fsm, it's probably not a good idea, as the only call
|
||||||
|
you'll ever receive from Cowboy is the start_link/3 call. On the other
|
||||||
|
hand, feel free to write a very basic protocol handler which then forwards
|
||||||
|
requests to a gen_server or gen_fsm. By doing so however you must take
|
||||||
|
care to supervise their processes as Cowboy only know about the protocol
|
||||||
|
handler itself.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue