mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Salvage the README and move parts into the guide
This commit is contained in:
parent
06e74355c0
commit
398d3503c4
3 changed files with 73 additions and 205 deletions
204
README.md
204
README.md
|
@ -31,207 +31,3 @@ Support
|
||||||
* Official IRC Channel: #ninenines on irc.freenode.net
|
* Official IRC Channel: #ninenines on irc.freenode.net
|
||||||
* [Mailing Lists](http://lists.ninenines.eu)
|
* [Mailing Lists](http://lists.ninenines.eu)
|
||||||
* [Commercial Support](http://ninenines.eu/support)
|
* [Commercial Support](http://ninenines.eu/support)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Old README (deprecated)
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
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
|
|
||||||
functions to start and stop Ranch listeners. The Ranch application
|
|
||||||
must always be started before Cowboy. The crypto application must
|
|
||||||
also be started.
|
|
||||||
|
|
||||||
The `cowboy:start_http/4` function will handle HTTP connections
|
|
||||||
using the TCP transport. Similarly, `cowboy:start_https/4` will
|
|
||||||
handle HTTP connections using the SSL transport.
|
|
||||||
|
|
||||||
You can start as many listeners as you need to. To allow this, you
|
|
||||||
are required to give a name to your listeners. It is the first
|
|
||||||
argument to the start functions. The name can be of any type.
|
|
||||||
|
|
||||||
You can stop listeners using `cowboy:stop_listener/1`, giving it
|
|
||||||
the name of the listener to be stopped.
|
|
||||||
|
|
||||||
The following example demonstrates the startup of a very simple
|
|
||||||
HTTP listener. It redirects all requests to the `my_handler`
|
|
||||||
module.
|
|
||||||
|
|
||||||
``` erlang
|
|
||||||
application:start(crypto),
|
|
||||||
application:start(ranch),
|
|
||||||
application:start(cowboy),
|
|
||||||
Dispatch = [
|
|
||||||
%% {URIHost, list({URIPath, Handler, Opts})}
|
|
||||||
{'_', [{'_', my_handler, []}]}
|
|
||||||
],
|
|
||||||
%% Name, NbAcceptors, TransOpts, ProtoOpts
|
|
||||||
cowboy:start_http(my_http_listener, 100, [{port, 8080}],
|
|
||||||
[{dispatch, Dispatch}]
|
|
||||||
).
|
|
||||||
```
|
|
||||||
|
|
||||||
This is not enough though, you must also write the `my_handler`
|
|
||||||
module to process the incoming HTTP requests. Of course Cowboy
|
|
||||||
comes with predefined handlers for specific tasks but most of
|
|
||||||
the time you'll need to write the handlers appropriate for your
|
|
||||||
application.
|
|
||||||
|
|
||||||
Following is an example of a "Hello World!" HTTP handler.
|
|
||||||
|
|
||||||
``` erlang
|
|
||||||
-module(my_handler).
|
|
||||||
-export([init/3, handle/2, 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.
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also write handlers that do not reply directly. Instead, such handlers
|
|
||||||
will wait for an Erlang message from another process and only reply when
|
|
||||||
receiving such message, or timeout if it didn't arrive in time.
|
|
||||||
|
|
||||||
This is especially useful for long-polling functionality, as Cowboy will handle
|
|
||||||
process hibernation and timeouts properly, preventing mistakes if you were to
|
|
||||||
write the code yourself. A handler of that kind can be defined like this:
|
|
||||||
|
|
||||||
``` erlang
|
|
||||||
-module(my_loop_handler).
|
|
||||||
-export([init/3, info/3, terminate/2]).
|
|
||||||
|
|
||||||
-define(TIMEOUT, 60000).
|
|
||||||
|
|
||||||
init({tcp, http}, Req, Opts) ->
|
|
||||||
{loop, Req, undefined_state, ?TIMEOUT, hibernate}.
|
|
||||||
|
|
||||||
info({reply, Body}, Req, State) ->
|
|
||||||
{ok, Req2} = cowboy_req:reply(200, [], Body, Req),
|
|
||||||
{ok, Req2, State};
|
|
||||||
info(Message, Req, State) ->
|
|
||||||
{loop, Req, State, hibernate}.
|
|
||||||
|
|
||||||
terminate(Req, State) ->
|
|
||||||
ok.
|
|
||||||
```
|
|
||||||
|
|
||||||
It is of course possible to combine both type of handlers together as long as
|
|
||||||
you return the proper tuple from init/3.
|
|
||||||
|
|
||||||
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 "ninenines.eu" domain name, you must specify
|
|
||||||
`[<<"ninenines">>, <<"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
|
|
||||||
"ninenines.eu" and "ninenines.fr" domains, you can use the match spec
|
|
||||||
`[<<"ninenines">>, '_']` to match any top level extension.
|
|
||||||
|
|
||||||
Finally, you can also match multiple leading segments of the domain name and
|
|
||||||
multiple trailing segments of the request path using the atom `'...'` (the atom
|
|
||||||
ellipsis) respectively as the first host token or the last path token. For
|
|
||||||
example, host rule `['...', <<"ninenines">>, <<"eu">>]` can match both
|
|
||||||
"cowboy.bugs.ninenines.eu" and "ninenines.eu" and path rule
|
|
||||||
`[<<"projects">>, '...']` can match both "/projects" and
|
|
||||||
"/projects/cowboy/issues/42". The host leading segments and the path trailing
|
|
||||||
segments can later be retrieved through `cowboy_req:host_info/1` and
|
|
||||||
`cowboy_req:path_info/1`.
|
|
||||||
|
|
||||||
Any other atom used as a token will bind the value to this atom when
|
|
||||||
matching. To follow on our hostnames example, `[<<"ninenines">>, ext]`
|
|
||||||
would bind the values `<<"eu">>` and `<<"fr">>` to the ext atom, that you
|
|
||||||
can later retrieve in your handler by calling `cowboy_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_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_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).
|
|
||||||
-export([init/3]).
|
|
||||||
-export([websocket_init/3, websocket_handle/3,
|
|
||||||
websocket_info/3, websocket_terminate/3]).
|
|
||||||
|
|
||||||
init({tcp, http}, Req, Opts) ->
|
|
||||||
{upgrade, protocol, cowboy_websocket}.
|
|
||||||
|
|
||||||
websocket_init(TransportName, Req, _Opts) ->
|
|
||||||
erlang:start_timer(1000, self(), <<"Hello!">>),
|
|
||||||
{ok, Req, undefined_state}.
|
|
||||||
|
|
||||||
websocket_handle({text, Msg}, Req, State) ->
|
|
||||||
{reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State};
|
|
||||||
websocket_handle(_Data, Req, State) ->
|
|
||||||
{ok, Req, State}.
|
|
||||||
|
|
||||||
websocket_info({timeout, _Ref, Msg}, Req, State) ->
|
|
||||||
erlang:start_timer(1000, self(), <<"How' you doin'?">>),
|
|
||||||
{reply, {text, Msg}, Req, State};
|
|
||||||
websocket_info(_Info, Req, State) ->
|
|
||||||
{ok, 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.
|
|
||||||
|
|
||||||
As the Websocket protocol is still a draft the API is subject to change
|
|
||||||
regularly when support to the most recent drafts gets added. Features may
|
|
||||||
be added, changed or removed before the protocol gets finalized. Cowboy
|
|
||||||
tries to implement all drafts transparently and give a single interface to
|
|
||||||
handle them all, however.
|
|
||||||
|
|
|
@ -12,6 +12,10 @@ a response.
|
||||||
They are most useful when performing long-polling operations or
|
They are most useful when performing long-polling operations or
|
||||||
when using server-sent events.
|
when using server-sent events.
|
||||||
|
|
||||||
|
While the same can be accomplished using plain HTTP handlers,
|
||||||
|
it is recommended to use loop handlers because they are well-tested
|
||||||
|
and allow using built-in features like hibernation and timeouts.
|
||||||
|
|
||||||
Callbacks
|
Callbacks
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -21,3 +25,30 @@ Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@todo Explain how to use them.
|
@todo Explain how to use them.
|
||||||
|
|
||||||
|
The following handler waits for a message `{reply, Body}` before
|
||||||
|
sending a response. If this message doesn't arrive within 60
|
||||||
|
seconds, it gives up and a `204 No Content` will be replied.
|
||||||
|
It also hibernates the process to save memory while waiting for
|
||||||
|
this message.
|
||||||
|
|
||||||
|
``` erlang
|
||||||
|
-module(my_loop_handler).
|
||||||
|
-behaviour(cowboy_loop_handler).
|
||||||
|
|
||||||
|
-export([init/3]).
|
||||||
|
-export([info/3]).
|
||||||
|
-export([terminate/2]).
|
||||||
|
|
||||||
|
init({tcp, http}, Req, Opts) ->
|
||||||
|
{loop, Req, undefined_state, 60000, hibernate}.
|
||||||
|
|
||||||
|
info({reply, Body}, Req, State) ->
|
||||||
|
{ok, Req2} = cowboy_req:reply(200, [], Body, Req),
|
||||||
|
{ok, Req2, State};
|
||||||
|
info(Message, Req, State) ->
|
||||||
|
{loop, Req, State, hibernate}.
|
||||||
|
|
||||||
|
terminate(Req, State) ->
|
||||||
|
ok.
|
||||||
|
```
|
||||||
|
|
|
@ -4,12 +4,18 @@ Websocket handlers
|
||||||
Purpose
|
Purpose
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Websockets are an extension to HTTP to emulate plain TCP connections
|
Websocket is an extension to HTTP to emulate plain TCP connections
|
||||||
between the user's browser and the server. Requests that are upgraded
|
between the user's browser and the server. Requests that are upgraded
|
||||||
are then handled by websocket handlers.
|
are then handled by websocket handlers.
|
||||||
|
|
||||||
Both sides of the socket can send data at any time asynchronously.
|
Both sides of the socket can send data at any time asynchronously.
|
||||||
|
|
||||||
|
Websocket is an IETF standard. Cowboy supports the standard and all
|
||||||
|
the drafts that were previously implemented by browsers. Websocket
|
||||||
|
is implemented by most browsers today, although for backward
|
||||||
|
compatibility reasons a solution like [Bullet](https://github.com/extend/bullet)
|
||||||
|
might be preferred.
|
||||||
|
|
||||||
Callbacks
|
Callbacks
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -19,3 +25,38 @@ Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@todo Explain how to use them.
|
@todo Explain how to use them.
|
||||||
|
|
||||||
|
The following handler sends a message every second. It also echoes
|
||||||
|
back what it receives.
|
||||||
|
|
||||||
|
``` erlang
|
||||||
|
-module(my_ws_handler).
|
||||||
|
-behaviour(cowboy_websocket_handler).
|
||||||
|
|
||||||
|
-export([init/3]).
|
||||||
|
-export([websocket_init/3]).
|
||||||
|
-export([websocket_handle/3]).
|
||||||
|
-export([websocket_info/3]).
|
||||||
|
-export([websocket_terminate/3]).
|
||||||
|
|
||||||
|
init({tcp, http}, Req, Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_websocket}.
|
||||||
|
|
||||||
|
websocket_init(TransportName, Req, _Opts) ->
|
||||||
|
erlang:start_timer(1000, self(), <<"Hello!">>),
|
||||||
|
{ok, Req, undefined_state}.
|
||||||
|
|
||||||
|
websocket_handle({text, Msg}, Req, State) ->
|
||||||
|
{reply, {text, << "That's what she said! ", Msg/binary >>}, Req, State};
|
||||||
|
websocket_handle(_Data, Req, State) ->
|
||||||
|
{ok, Req, State}.
|
||||||
|
|
||||||
|
websocket_info({timeout, _Ref, Msg}, Req, State) ->
|
||||||
|
erlang:start_timer(1000, self(), <<"How' you doin'?">>),
|
||||||
|
{reply, {text, Msg}, Req, State};
|
||||||
|
websocket_info(_Info, Req, State) ->
|
||||||
|
{ok, Req, State}.
|
||||||
|
|
||||||
|
websocket_terminate(_Reason, _Req, _State) ->
|
||||||
|
ok.
|
||||||
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue