Skip to content

Commit

Permalink
Switch to Ranch for connection handling
Browse files Browse the repository at this point in the history
This is the first of many API incompatible changes.

You have been warned.
  • Loading branch information
essen committed Aug 27, 2012
1 parent 50e5a61 commit e4124de
Show file tree
Hide file tree
Showing 35 changed files with 135 additions and 1,068 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ all: app

# Application.

deps:
deps/ranch:
@$(REBAR) get-deps

app: deps
app: deps/ranch
@$(REBAR) compile

clean:
Expand Down Expand Up @@ -52,7 +52,7 @@ intct:

build-plt:
@$(DIALYZER) --build_plt --output_plt .$(PROJECT).plt \
--apps kernel stdlib sasl inets crypto public_key ssl
--apps kernel stdlib sasl inets crypto public_key ssl deps/*

dialyze:
@$(DIALYZER) --src src --plt .$(PROJECT).plt --no_native \
Expand Down
114 changes: 27 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ Cowboy

Cowboy is a small, fast and modular HTTP server written in Erlang.

Cowboy is also a socket acceptor pool, able to accept connections
for any kind of TCP protocol.

Goals
-----

Expand Down Expand Up @@ -33,62 +30,50 @@ 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)!
* Check out the `examples/` directory!

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.
Cowboy does nothing by default.

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.
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.

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.
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.

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.
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.

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 stop listeners using `cowboy:stop_listener/1`, giving it
the name of the listener to be stopped.

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.
The following example demonstrates the startup of a very simple
HTTP listener. It redirects all requests to the `my_handler`
module.

``` erlang
application:start(ranch),
application:start(cowboy),
Dispatch = [
%% {Host, list({Path, Handler, Opts})}
%% {URIHost, list({URIPath, Handler, Opts})}
{'_', [{'_', my_handler, []}]}
],
%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
cowboy:start_listener(my_http_listener, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
%% 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
want to write your own handlers for your application.
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.

Expand All @@ -113,7 +98,7 @@ 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. An handler of that kind can be defined like this:
write the code yourself. A handler of that kind can be defined like this:

``` erlang
-module(my_loop_handler).
Expand All @@ -137,16 +122,6 @@ terminate(Req, State) ->
It is of course possible to combine both type of handlers together as long as
you return the proper tuple from init/3.

**Note**: versions prior to `0.4.0` used the
[quoted](https://github.com/klaar/quoted.erl) library instead of the built in
`cowboy_http:urldecode/2` function. If you want to retain this you must add it
as a dependency to your application and add the following cowboy_http_protocol
option:

``` erlang
{urldecode, {fun quoted:from_url/2, quoted:make([])}}
```

Continue reading to learn how to dispatch rules and handle requests.

Dispatch rules
Expand Down Expand Up @@ -253,38 +228,3 @@ 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 call the
function cowboy:accept_ack/1 with the ListenerPid as argument,
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.
1 change: 1 addition & 0 deletions examples/chunked_hello_world/src/chunked_hello_world.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(chunked_hello_world).
7 changes: 3 additions & 4 deletions examples/chunked_hello_world/src/chunked_hello_world_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ start(_Type, _Args) ->
{[], toppage_handler, []}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
chunked_hello_world_sup:start_link().

stop(_State) ->
Expand Down
1 change: 1 addition & 0 deletions examples/echo_get/src/echo_get.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(echo_get).
7 changes: 3 additions & 4 deletions examples/echo_get/src/echo_get_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ start(_Type, _Args) ->
{[], toppage_handler, []}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
echo_get_sup:start_link().

stop(_State) ->
Expand Down
1 change: 1 addition & 0 deletions examples/echo_post/src/echo_post.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(echo_post).
7 changes: 3 additions & 4 deletions examples/echo_post/src/echo_post_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ start(_Type, _Args) ->
{[], toppage_handler, []}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
echo_post_sup:start_link().

stop(_State) ->
Expand Down
2 changes: 1 addition & 1 deletion examples/echo_post/start.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/sh
erl -pa ebin deps/*/ebin -s echo_post \
-eval "io:format(\"Run ./curl_post.sh STRING_TO_ECHO\")."
-eval "io:format(\"Run ./curl_post.sh STRING_TO_ECHO~n\")."
1 change: 1 addition & 0 deletions examples/hello_world/src/hello_world.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(hello_world).
7 changes: 3 additions & 4 deletions examples/hello_world/src/hello_world_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ start(_Type, _Args) ->
{[], toppage_handler, []}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
hello_world_sup:start_link().

stop(_State) ->
Expand Down
1 change: 1 addition & 0 deletions examples/rest_hello_world/src/rest_hello_world.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(rest_hello_world).
7 changes: 3 additions & 4 deletions examples/rest_hello_world/src/rest_hello_world_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ start(_Type, _Args) ->
{[], toppage_handler, []}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
rest_hello_world_sup:start_link().

stop(_State) ->
Expand Down
1 change: 1 addition & 0 deletions examples/static/src/static.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
%% API.

start() ->
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(static).
7 changes: 3 additions & 4 deletions examples/static/src/static_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ start(_Type, _Args) ->
]}
]}
],
{ok, _} = cowboy:start_listener(http, 100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{dispatch, Dispatch}
]),
static_sup:start_link().

stop(_State) ->
Expand Down
3 changes: 3 additions & 0 deletions rebar.config
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{deps, [
{ranch, "0\\.4\\.0.*", {git, "git://github.com/extend/ranch.git", "0.4.0"}}
]}.
{erl_opts, [
%% bin_opt_info,
%% warn_missing_spec,
Expand Down
5 changes: 3 additions & 2 deletions rebar.tests.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{cover_enabled, true}.
{deps, [
{proper, "1.0",
{git, "git://github.com/manopapad/proper.git", {tag, "v1.0"}}}
{proper, ".*",
{git, "git://github.com/manopapad/proper.git", "master"}},
{ranch, "0\\.4\\.0.*", {git, "git://github.com/extend/ranch.git", "0.4.0"}}
]}.
{eunit_opts, [verbose, {report, {eunit_surefire, [{dir, "."}]}}]}.
{erl_opts, []}.
5 changes: 3 additions & 2 deletions src/cowboy.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@

{application, cowboy, [
{description, "Small, fast, modular HTTP server."},
{vsn, "0.6.1"},
{vsn, "0.7.0"},
{modules, []},
{registered, [cowboy_clock, cowboy_sup]},
{applications, [
kernel,
stdlib
stdlib,
ranch
]},
{mod, {cowboy_app, []}},
{env, []}
Expand Down
Loading

0 comments on commit e4124de

Please sign in to comment.