Joseph Kain bio photo

Joseph Kain

Professional Software Engineer learning Elixir.

Twitter LinkedIn Github

Last week’s post was dedicated to Ranch and learning OTP application design. This week I will continue learning Elixir / Erlang application design by studying Cowboy. As promised, instead of looking at supervision trees we will take a step back and look at how Cowboy composes multiple OTP applications in order to implement a small, fast, modular HTTP server.

Setup

As in the past two posts, I’m using IdahoEv/cowboy-elixir-example to load cowboy and the examining the structure using the Erlang observer. If you are just starting with this series you can follow along by cloning the repo and running the commands from README.md:

mix deps.get
mix deps.compile
iex -S mix

Connect to http://localhost:8080 to see the example. Then, from within iex, load the observer:

:observer.start

Application List

As seen in observer there are several applications running:

  • cowboy
  • cowboy-elixir-example
  • elixir
  • inets
  • kernel
  • mix
  • ranch
  • ssl

Of course the applications “elixir”, and “mix” are part of the Elixir ecosystem and are pulled in only by the example program so I won’t consider them in this post (though I previously covered Mix design).

Start at the Top

I’ll start with mix.exs for cowboy-elixir-example

def application do
  [
    mod: { CowboyElixirExample, [] },
    applications: [:cowboy, :ranch] 
  ]
end

So the example depends on cowboy and ranch. Based on what I know so far, it shouldn’t be necessary to explicitly list ranch as a dependency. Searching the sources, I found that cowboy-elixir-example doesn’t reference ranch directly. So I removed ranch from the list of dependencies and the app still runs.

def application do
  [
    mod: { CowboyElixirExample, [] },
    applications: [:cowboy] 
  ]
end

So, here we have a simple composition - cowboy-elixir-example depends on cowboy.

Note, if we look at the deps section we see:

defp deps do
  [ { :cowboy, "1.0.0" },
    { :jsex, "~> 2.0.0" } ]    
end

The difference here is that jsex is not an application. It’s a library of functions.

Working Downward

Working down the application tree, I look at cowboy’s cowboy.app.src. I should note that there have been some changes to cowboy’s dependencies at different versions. So I must be carefully about versions. Thankfully, Mix copies the source for all dependencies into the deps directory of our project. So I can use these sources for my analysis.

{application, cowboy, [
	{description, "Small, fast, modular HTTP server."},
	{vsn, "1.0.0"},
	{modules, []},
	{registered, [cowboy_clock, cowboy_sup]},
	{applications, [
		kernel,
		stdlib,
		ranch,
		cowlib,
		crypto
	]},
	{mod, {cowboy_app, []}},
	{env, []}
]}.

Here we see that Cowboy depends on:

  • kernel
  • stdlib
  • ranch
  • cowlib
  • crypto

It is interesting that we don’t see cowlib or crypto in observer. Could they be loaded lazily? Or could they have been unloaded?

Looking next at ranch.app.src in the ranch application I see that ranch doesn’t include any new dependencies:

{applications, [
	kernel,
	stdlib
]},

Similarly for cowlib, there are no new dependencies:

{applications, [
	kernel,
	stdlib,
	crypto
]}

So this gives us the following dependency graph (omitting stdlib and kernel)

Cowboy Graph

Cowboy Dependency Graph

Design

From the graph we can see that Cowboy is nicely encapsulated. It depends on ranch, cowlib, and crypto but it hides these applications from its users (cowboy-elixir-example in this case).

From cowboy-elixir-example’s point of view it uses the cowboy application and the jsex library. jsex is used to build up JSON and then serve it out via HTTP using Cowboy.

Something’s Missing

It is interesting that we don’t see cowlib or crypto in observer. Could they be loaded lazily? And the observer shows the inets application loaded but I didn’t find a dependency for it. How was inets loaded? Based on the application list I was expecting the dependency graph looks more like this:

Cowboy Graph

Expected Cowboy Dependency Graph

Next week I will explore application loading in more detail and will answer the questions we’ve been left with.