1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
Cowboy
======
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.
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 [examples](https://github.com/extend/cowboy_examples)!
Getting Started
---------------
At heart, Cowboy is nothing more than an TCP acceptor pool. All it does is
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.
It just so happens that Cowboy also includes an HTTP protocol handler.
But Cowboy does nothing by default. You need to explicitly ask Cowboy
to listen on a port with your chosen transport and protocol handlers.
To do so, you must start a listener.
A listener is a special kind of supervisor that manages both the
acceptor pool and the request processes. It is named and can thus be
started and stopped at will.
An acceptor pool is a pool of processes whose only role is to accept
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/6` and
`cowboy:stop_listener/1` respectively.
The following example demonstrates the startup of a very simple listener.
``` erlang
application:start(cowboy),
Dispatch = [
%% {Host, list({Path, Handler, Opts})}
{'_', [{'_', my_handler, []}]}
],
%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{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
want to write your own handlers for your application.
Following is an example of a "Hello World!" HTTP handler.
``` erlang
-module(my_handler).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/2]).
init({tcp, http}, Req, Opts) ->
{ok, Req, undefined_state}.
handle(Req, State) ->
{ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello World!">>, Req),
{ok, Req2, State}.
terminate(Req, State) ->
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.
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 `['...', <<"dev-extend">>, <<"eu">>]` can match both
"cowboy.bugs.dev-extend.eu" and "dev-extend.eu" and path rule
`[<<"projects">>, '...']` can math both "/projects" and
"/projects/cowboy/issues/42". The host leading segments and the path trailing
segments can later be retrieved through `cowboy_http_req:host_info/1` and
`cowboy_http_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, `[<<"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_info/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({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.
Using Cowboy with other protocols
---------------------------------
One of the strengths 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/4
function, with arguments ListenerPid, Socket, Transport and Opts. ListenerPid
is the pid to the listener's gen_server, managing the connections. 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.
After initializing your protocol, it is recommended to wait to receive a message
containing the atom 'shoot', as it will ensure Cowboy has been able to fully
initialize the socket. Anything you do past this point is up to you!
If you need to change some socket options, like enabling raw mode for example,
you can call the <em>Transport:setopts/2</em> function. It is the protocol's
responsability to manage the socket usage, there should be no need for an user
to specify that kind of options while starting a listener.
You should definitely look at the cowboy_http_protocol module for a great
example of fast request 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/4 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 knows about the protocol
handler itself.
|