0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 20:30:23 +00:00

Anonymize and improve the cowboy supervision tree.

* 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.
This commit is contained in:
Loïc Hoguin 2011-04-02 20:27:16 +02:00
parent e6e5b1767f
commit e4da6956fc
7 changed files with 78 additions and 86 deletions

View file

@ -33,21 +33,28 @@ Embedding Cowboy
Getting Started Getting Started
--------------- ---------------
Cowboy can be started and stopped like any other application. However the Cowboy provides an anonymous listener supervisor that you can directly embed
Cowboy application do not start any listener, those must be started manually. in your application's supervision tree.
A listener is a special kind of supervisor that handles a pool of acceptor 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 processes. It also manages all its associated request processes. This allows
protocol module, for example HTTP. You must thus define the transport and you to shutdown all processes related to a listener by stopping the supervisor.
protocol module to use for the listener, their options and the number of
acceptors in the pool before you can start a listener 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 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. HTTPS respectively. On the other hand, the protocol is of course HTTP.
Code speaks more than words: Code speaks more than words:
application:start(cowboy), -module(my_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, _Args) ->
Dispatch = [ Dispatch = [
%% {Host, list({Path, Handler, Opts})} %% {Host, list({Path, Handler, Opts})}
{'_', [{'_', my_handler, []}]} {'_', [{'_', my_handler, []}]}
@ -58,6 +65,9 @@ Code speaks more than words:
cowboy_http_protocol, [{dispatch, Dispatch}] cowboy_http_protocol, [{dispatch, Dispatch}]
). ).
stop(_State) ->
ok.
You must also write the `my_handler` module to process requests. You can 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 use one of the predefined handlers or write your own. An hello world HTTP
handler could be written like this: handler could be written like this:

View file

@ -20,7 +20,5 @@
{applications, [ {applications, [
kernel, kernel,
stdlib stdlib
]}, ]}
{mod, {cowboy_app, []}},
{env, []}
]}. ]}.

View file

@ -13,27 +13,28 @@
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(cowboy_acceptor). -module(cowboy_acceptor).
-export([start_link/4]). %% API. -export([start_link/5]). %% API.
-export([acceptor/4]). %% Internal. -export([acceptor/5]). %% Internal.
-include("include/types.hrl"). -include("include/types.hrl").
%% API. %% API.
-spec start_link(LSocket::socket(), Transport::module(), -spec start_link(LSocket::socket(), Transport::module(),
Protocol::module(), Opts::term()) -> {ok, Pid::pid()}. Protocol::module(), Opts::term(), ReqsSup::pid()) -> {ok, Pid::pid()}.
start_link(LSocket, Transport, Protocol, Opts) -> start_link(LSocket, Transport, Protocol, Opts, ReqsSup) ->
Pid = spawn_link(?MODULE, acceptor, [LSocket, Transport, Protocol, Opts]), Pid = spawn_link(?MODULE, acceptor,
[LSocket, Transport, Protocol, Opts, ReqsSup]),
{ok, Pid}. {ok, Pid}.
%% Internal. %% Internal.
-spec acceptor(LSocket::socket(), Transport::module(), -spec acceptor(LSocket::socket(), Transport::module(),
Protocol::module(), Opts::term()) -> no_return(). Protocol::module(), Opts::term(), ReqsSup::pid()) -> no_return().
acceptor(LSocket, Transport, Protocol, Opts) -> acceptor(LSocket, Transport, Protocol, Opts, ReqsSup) ->
case Transport:accept(LSocket) of case Transport:accept(LSocket) of
{ok, CSocket} -> {ok, CSocket} ->
{ok, Pid} = supervisor:start_child(cowboy_protocols_sup, {ok, Pid} = supervisor:start_child(ReqsSup,
[CSocket, Transport, Protocol, Opts]), [CSocket, Transport, Protocol, Opts]),
Transport:controlling_process(CSocket, Pid); Transport:controlling_process(CSocket, Pid);
{error, _Reason} -> {error, _Reason} ->
@ -41,4 +42,4 @@ acceptor(LSocket, Transport, Protocol, Opts) ->
%% we may want to try and listen again on the port? %% we may want to try and listen again on the port?
ignore ignore
end, end,
?MODULE:acceptor(LSocket, Transport, Protocol, Opts). ?MODULE:acceptor(LSocket, Transport, Protocol, Opts, ReqsSup).

View file

@ -12,26 +12,29 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(cowboy_sup). -module(cowboy_acceptors_sup).
-behaviour(supervisor). -behaviour(supervisor).
-export([start_link/0]). %% API. -export([start_link/6]). %% API.
-export([init/1]). %% supervisor. -export([init/1]). %% supervisor.
-define(SUPERVISOR, ?MODULE). -include("include/types.hrl").
%% API. %% API.
-spec start_link() -> {ok, Pid::pid()}. -spec start_link(NbAcceptors::non_neg_integer(), Transport::module(),
start_link() -> TransOpts::term(), Protocol::module(), ProtoOpts::term(), ReqsPid::pid())
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []). -> {ok, Pid::pid()}.
start_link(LSocket, NbAcceptors, Transport, Protocol, ProtoOpts, ReqsPid) ->
supervisor:start_link(?MODULE, [LSocket, NbAcceptors,
Transport, Protocol, ProtoOpts, ReqsPid]).
%% supervisor. %% supervisor.
-spec init([]) -> term(). %% @todo These specs should be improved. -spec init(list(term())) -> term(). %% @todo These specs should be improved.
init([]) -> init([LSocket, NbAcceptors, Transport, Protocol, ProtoOpts, ReqsPid]) ->
Procs = [ Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, [
{cowboy_protocols_sup, {cowboy_protocols_sup, start_link, []}, LSocket, Transport, Protocol, ProtoOpts, ReqsPid
permanent, 5000, supervisor, [cowboy_protocols_sup]} ]}, permanent, brutal_kill, worker, dynamic}
], || N <- lists:seq(1, NbAcceptors)],
{ok, {{one_for_one, 10, 10}, Procs}}. {ok, {{one_for_one, 10, 10}, Procs}}.

View file

@ -1,30 +0,0 @@
%% Copyright (c) 2011, Loïc Hoguin <essen@dev-extend.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(cowboy_app).
-behaviour(application).
-export([start/2, stop/1]). %% API.
-include("include/types.hrl").
%% API.
-spec start(Type::application_start_type(), Args::term()) -> {ok, Pid::pid()}.
start(_Type, _Args) ->
cowboy_sup:start_link().
-spec stop(State::term()) -> ok.
stop(_State) ->
ok.

View file

@ -26,8 +26,7 @@
start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
case Transport:listen(TransOpts) of case Transport:listen(TransOpts) of
{ok, LSocket} -> {ok, LSocket} ->
supervisor:start_link(?MODULE, [LSocket, start_sup(LSocket, NbAcceptors, Transport, Protocol, ProtoOpts);
NbAcceptors, Transport, Protocol, ProtoOpts]);
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end. end.
@ -35,9 +34,22 @@ start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
%% supervisor. %% supervisor.
%% @todo These specs should be improved. %% @todo These specs should be improved.
-spec init(list(term())) -> term(). -spec init([]) -> term().
init([LSocket, NbAcceptors, Transport, Protocol, ProtoOpts]) -> init([]) ->
Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, {ok, {{one_for_one, 0, 1}, []}}.
[LSocket, Transport, Protocol, ProtoOpts]}, permanent,
brutal_kill, worker, dynamic} || N <- lists:seq(1, NbAcceptors)], %% Internal.
{ok, {{one_for_one, 10, 10}, Procs}}.
-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}.

View file

@ -12,30 +12,28 @@
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(cowboy_protocols_sup). -module(cowboy_requests_sup).
-behaviour(supervisor). -behaviour(supervisor).
-export([start_link/0, start_protocol/4]). %% API. -export([start_link/0, start_request/4]). %% API.
-export([init/1]). %% supervisor. -export([init/1]). %% supervisor.
-include("include/types.hrl"). -include("include/types.hrl").
-define(SUPERVISOR, ?MODULE).
%% API. %% API.
-spec start_link() -> {ok, Pid::pid()}. -spec start_link() -> {ok, Pid::pid()}.
start_link() -> 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()}. 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). Protocol:start_link(Socket, Transport, Opts).
%% supervisor. %% supervisor.
-spec init([]) -> term(). %% @todo These specs should be improved. -spec init([]) -> term(). %% @todo These specs should be improved.
init([]) -> 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]}]}}. temporary, brutal_kill, worker, [?MODULE]}]}}.