Skip to content

Commit

Permalink
Anonymize and improve the cowboy supervision tree.
Browse files Browse the repository at this point in the history
* Cowboy isn't an OTP application anymore; just a supervisor.
* All processes started by Cowboy are now anonymous.
* All processes related to a listener are now part of its supervision tree.
  • Loading branch information
Loïc Hoguin committed Apr 2, 2011
1 parent e6e5b17 commit e4da695
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 86 deletions.
42 changes: 26 additions & 16 deletions README.md
Expand Up @@ -33,30 +33,40 @@ Embedding Cowboy
Getting Started
---------------

Cowboy can be started and stopped like any other application. However the
Cowboy application do not start any listener, those must be started manually.
Cowboy provides an anonymous listener supervisor that you can directly embed
in your application's supervision tree.

A listener is a special kind of supervisor that handles a pool of acceptor
processes. An acceptor simply accept connections and forward them to a
protocol module, for example HTTP. You must thus define the transport and
protocol module to use for the listener, their options and the number of
acceptors in the pool before you can start a listener supervisor.
processes. It also manages all its associated request processes. This allows
you to shutdown all processes related to a listener by stopping the supervisor.

An acceptor simply accepts connections and forwards them to a protocol module,
for example HTTP. You must thus define the transport and protocol module to
use for the listener, their options and the number of acceptors in the pool
before you can start a listener supervisor.

For HTTP applications the transport can be either TCP or SSL for HTTP and
HTTPS respectively. On the other hand, the protocol is of course HTTP.

Code speaks more than words:

application:start(cowboy),
Dispatch = [
%% {Host, list({Path, Handler, Opts})}
{'_', [{'_', my_handler, []}]}
],
%% NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
cowboy_listener_sup:start_link(100,
cowboy_tcp_transport, [{port, 8080}],
cowboy_http_protocol, [{dispatch, Dispatch}]
).
-module(my_app).
-behaviour(application).
-export([start/2, stop/1]).

start(_Type, _Args) ->
Dispatch = [
%% {Host, list({Path, Handler, Opts})}
{'_', [{'_', my_handler, []}]}
],
%% NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
cowboy_listener_sup:start_link(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
use one of the predefined handlers or write your own. An hello world HTTP
Expand Down
4 changes: 1 addition & 3 deletions src/cowboy.app.src
Expand Up @@ -20,7 +20,5 @@
{applications, [
kernel,
stdlib
]},
{mod, {cowboy_app, []}},
{env, []}
]}
]}.
19 changes: 10 additions & 9 deletions src/cowboy_acceptor.erl
Expand Up @@ -13,32 +13,33 @@
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_acceptor).
-export([start_link/4]). %% API.
-export([acceptor/4]). %% Internal.
-export([start_link/5]). %% API.
-export([acceptor/5]). %% Internal.

-include("include/types.hrl").

%% API.

-spec start_link(LSocket::socket(), Transport::module(),
Protocol::module(), Opts::term()) -> {ok, Pid::pid()}.
start_link(LSocket, Transport, Protocol, Opts) ->
Pid = spawn_link(?MODULE, acceptor, [LSocket, Transport, Protocol, Opts]),
Protocol::module(), Opts::term(), ReqsSup::pid()) -> {ok, Pid::pid()}.
start_link(LSocket, Transport, Protocol, Opts, ReqsSup) ->
Pid = spawn_link(?MODULE, acceptor,
[LSocket, Transport, Protocol, Opts, ReqsSup]),
{ok, Pid}.

%% Internal.

-spec acceptor(LSocket::socket(), Transport::module(),
Protocol::module(), Opts::term()) -> no_return().
acceptor(LSocket, Transport, Protocol, Opts) ->
Protocol::module(), Opts::term(), ReqsSup::pid()) -> no_return().
acceptor(LSocket, Transport, Protocol, Opts, ReqsSup) ->
case Transport:accept(LSocket) of
{ok, CSocket} ->
{ok, Pid} = supervisor:start_child(cowboy_protocols_sup,
{ok, Pid} = supervisor:start_child(ReqsSup,
[CSocket, Transport, Protocol, Opts]),
Transport:controlling_process(CSocket, Pid);
{error, _Reason} ->
%% @todo Probably do something here. If the socket was closed,
%% we may want to try and listen again on the port?
ignore
end,
?MODULE:acceptor(LSocket, Transport, Protocol, Opts).
?MODULE:acceptor(LSocket, Transport, Protocol, Opts, ReqsSup).
27 changes: 15 additions & 12 deletions src/cowboy_sup.erl → src/cowboy_acceptors_sup.erl
Expand Up @@ -12,26 +12,29 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_sup).
-module(cowboy_acceptors_sup).
-behaviour(supervisor).

-export([start_link/0]). %% API.
-export([start_link/6]). %% API.
-export([init/1]). %% supervisor.

-define(SUPERVISOR, ?MODULE).
-include("include/types.hrl").

%% API.

-spec start_link() -> {ok, Pid::pid()}.
start_link() ->
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []).
-spec start_link(NbAcceptors::non_neg_integer(), Transport::module(),
TransOpts::term(), Protocol::module(), ProtoOpts::term(), ReqsPid::pid())
-> {ok, Pid::pid()}.
start_link(LSocket, NbAcceptors, Transport, Protocol, ProtoOpts, ReqsPid) ->
supervisor:start_link(?MODULE, [LSocket, NbAcceptors,
Transport, Protocol, ProtoOpts, ReqsPid]).

%% supervisor.

-spec init([]) -> term(). %% @todo These specs should be improved.
init([]) ->
Procs = [
{cowboy_protocols_sup, {cowboy_protocols_sup, start_link, []},
permanent, 5000, supervisor, [cowboy_protocols_sup]}
],
-spec init(list(term())) -> term(). %% @todo These specs should be improved.
init([LSocket, NbAcceptors, Transport, Protocol, ProtoOpts, ReqsPid]) ->
Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, [
LSocket, Transport, Protocol, ProtoOpts, ReqsPid
]}, permanent, brutal_kill, worker, dynamic}
|| N <- lists:seq(1, NbAcceptors)],
{ok, {{one_for_one, 10, 10}, Procs}}.
30 changes: 0 additions & 30 deletions src/cowboy_app.erl

This file was deleted.

28 changes: 20 additions & 8 deletions src/cowboy_listener_sup.erl
Expand Up @@ -26,18 +26,30 @@
start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
case Transport:listen(TransOpts) of
{ok, LSocket} ->
supervisor:start_link(?MODULE, [LSocket,
NbAcceptors, Transport, Protocol, ProtoOpts]);
start_sup(LSocket, NbAcceptors, Transport, Protocol, ProtoOpts);
{error, Reason} ->
{error, Reason}
end.

%% supervisor.

%% @todo These specs should be improved.
-spec init(list(term())) -> term().
init([LSocket, NbAcceptors, Transport, Protocol, ProtoOpts]) ->
Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link,
[LSocket, Transport, Protocol, ProtoOpts]}, permanent,
brutal_kill, worker, dynamic} || N <- lists:seq(1, NbAcceptors)],
{ok, {{one_for_one, 10, 10}, Procs}}.
-spec init([]) -> term().
init([]) ->
{ok, {{one_for_one, 0, 1}, []}}.

%% Internal.

-spec start_sup(NbAcceptors::non_neg_integer(), Transport::module(),
TransOpts::term(), Protocol::module(), ProtoOpts::term())
-> {ok, Pid::pid()}.
start_sup(LSocket, NbAcceptors, Transport, Protocol, ProtoOpts) ->
{ok, SupPid} = supervisor:start_link(?MODULE, []),
{ok, ReqsPid} = supervisor:start_child(SupPid,
{cowboy_requests_sup, {cowboy_requests_sup, start_link, []},
permanent, 5000, supervisor, [cowboy_requests_sup]}),
{ok, _PoolPid} = supervisor:start_child(SupPid,
{cowboy_acceptors_sup, {cowboy_acceptors_sup, start_link, [
LSocket, NbAcceptors, Transport, Protocol, ProtoOpts, ReqsPid
]}, permanent, 5000, supervisor, [cowboy_acceptors_sup]}),
{ok, SupPid}.
14 changes: 6 additions & 8 deletions src/cowboy_protocols_sup.erl → src/cowboy_requests_sup.erl
Expand Up @@ -12,30 +12,28 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module(cowboy_protocols_sup).
-module(cowboy_requests_sup).
-behaviour(supervisor).

-export([start_link/0, start_protocol/4]). %% API.
-export([start_link/0, start_request/4]). %% API.
-export([init/1]). %% supervisor.

-include("include/types.hrl").

-define(SUPERVISOR, ?MODULE).

%% API.

-spec start_link() -> {ok, Pid::pid()}.
start_link() ->
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []).
supervisor:start_link(?MODULE, []).

-spec start_protocol(Socket::socket(), Transport::module(),
-spec start_request(Socket::socket(), Transport::module(),
Protocol::module(), Opts::term()) -> {ok, Pid::pid()}.
start_protocol(Socket, Transport, Protocol, Opts) ->
start_request(Socket, Transport, Protocol, Opts) ->
Protocol:start_link(Socket, Transport, Opts).

%% supervisor.

-spec init([]) -> term(). %% @todo These specs should be improved.
init([]) ->
{ok, {{simple_one_for_one, 0, 1}, [{?MODULE, {?MODULE, start_protocol, []},
{ok, {{simple_one_for_one, 0, 1}, [{?MODULE, {?MODULE, start_request, []},
temporary, brutal_kill, worker, [?MODULE]}]}}.

0 comments on commit e4da695

Please sign in to comment.