Creating an HTTP Server using pure OTP
By: Tyler Pachal
Tags: Inets, Erlang, Otp, Http, Elixir

These are the steps needed to get a HTTP server working in your application

Step 1 — start inets

In you mix.exs file, add inets to the extra_applications value inside the application function so that it will be started when your application starts. You probably already do this for the logger application:

The mix.exs file with inets started via the extra_applications

Step 2 — start httpd from supervisor

In the supervisor, we need to start httpd and supervise it. Since mine is for a healthcheck, I started it last, and used a rest-for-one supervision strategy, so that the server would always be restarted (and be down) whenever one of the other processes fails.

Here is some of the code:

My supervisor which starts the Api

Step 3 — create Api module

Now we need to define the Api module that we are using in the modules option for httpd , it will handle incoming requests to any URI, do the healthcheck or return a 404:

The Api module for handling incoming requests

There is a lot talk about here. The child_spec/1 function is really just returning the args to make the :inets.start/2 function. You can test that seperately in IEx by doing the following:

Starting inets and httpd from IEx

None of the options that we passed were optional, and you can read more about them in the httpd documentation. The cool thing is that httpd by default is a document server, so after you start it in IEx you can open up your browser and look around at what is in your /tmp folder (specified by the document_root option):

The contents of my /tmp folder, served by httpd

Of course, we are not trying to make a document server. That is why we passed in the extra modules option in our application code (we omitted it while testing in IEx ). There is a default list of modules that provide extra functionality for the web server, and one of those defaults is the mod_dir which generated the Apache-style directory browser in the previous screenshot.

By passing our own list of a single module, we will remove any of the default functionality (like the directory browser) and will be responsible for implementing all of the functionality we need ourselves. You can also re-use the default modules by adding them back to your list, they are listed here. We pass in our module, where our “router” is defined further down.

Note that all of the options used 'single_quotes' and not "double_quotes" ; this is because we are dealing with an Erlang module that is expecting charlists, which are represented with the single quotes in Elixir.

There are some other uncommon things going on here to talk about.

Record is used for interfacing Erlang records, which are kind of like Elixir structs. In our case, you can see the Record that is defined in inets which represents an incoming request to our server here. You also could achieve the same thing by doing regular pattern matching on the nested data value.

def unquote is for defining our callback function. httpd is expecting our module to have a do/1 function, but we cannot define a function like that in the “regular” way because do is already defined in Elixir! Here is an example in Erlang.