Cowboy works in combination with Ranch (which is a socket worker pool for tcp protocols) and Cowlib (library for message processing). For the scope of this article we focus primarily on Cowboy and Ranch because this is where most of the interesting stuff happens.

Since Cowboy is designed to build servers, the process tree starts after you initialize your server. Standalone Cowboy app just has a cowboy_clock process running. Following is a common way of starting a cowboy http server. (Note: you don't need to worry about the arguments as of now)

cowboy:start_http(http, 10, [{port, 80}], [{env, [{dispatch, Dispatch}]}]).

After this everything is pretty much handled by Ranch. One can think of Cowboy as an implementation of various protocols for eg. http etc. and these protocols are fed into Ranch which decides how the server will behave. So, Ranch is a template framework or as previously stated “a socket worker pool for tcp protocols” where Cowboy feeds in different tcp protocols and helps build different kinds of servers.

From hereon all the discussion will revolve around Ranch because it is what makes Cowboy “Cowboy”.

Ranch

The standalone Ranch app has a ranch_sup supervisor running under which there is ranch_server process which basically gets stuff in and out of an ETS table (ETS is ram storage db). The cowboy:start_http(…) function initializes a (ranch_listener_sup)supervision tree under ranch_sup. The image below shows a pictorial representation of the process tree.

Notice, there is a ranch_acceptor process running under ranch_acceptor_sup process. Recall the second argument in cowboy:start_http(…) , it corresponds to the number ranch_acceptor processes running under ranch_acceptor_sup, so in our case there would be 10 such processes running.

The ranch_acceptor processes monitor the socket for any incoming client connections and the moment it receives a new connection it transfers the control of this new connection to the ranch_conns_sup which in turn starts a new protocol process and transfers the control of this incoming connection to it. The protocol process receives the request from client and returns the response.

An important takeaway from here is that ranch_acceptor process does not return until a new connection handling process i.e. protocol process has been created under ranch_conns_sup and control of the socket has been transferred to it. This means that till that time this process cannot listen for new incoming connections. So, one should wisely choose the number of ranch_acceptor processes in accordance with traffic they expect to receive.

For programmers diving into Erlang this socket control transfer trick is something to keep in mind while developing real time scalable applications !

The protocol process based on the received request starts the corresponding request handler (which is usually defined the url rules or dispatch rules). Developers who have written some handler code in cowboy would know that the entry point in handler code is through an init() function which returns the behaviour of the handler for eg. cowboy_rest, cowboy_websocket . After the init() function protocol process switches to the returned callback module.