~/blog

Heroku with C

Whenever I want to know my external IP address, I use ifconfig.me. It's a small service that returns your IP address along with some additional information. The only downside is that it's relatively slow. In this post I'll describe how I implemented a subset of ifconfig.me in C and deployed it to Heroku.

Is it possible?

When I first got the idea I didn't knew if Heroku would be able to run C applications. I remembered that I managed to run a Haskell application on Heroku a while ago. Officially Heroku neither supports Haskell nor C. It simply doesn't know how to prepare the code and do things like dependency management.

For my Haskell project I was able to resolve this by compiling a binary on my machine and deploying it directly to Heroku. Heroku was then able to execute the binary and thus serve the web-pages. I decided to use this method with my C application, again.

How Heroku works

In order to know how I would build my idea, I had to read a bit of the well-written and extensive Heroku documentation.

If you draw a rough picture, Heroku consists of two components: An intelligent load-balancer and one or more application "servers", also known as dynos. The load-balancer maps a DNS name, like foobar.herokuapp.com, to one or more dynos.

Each application server (or dyno) has to run a HTTP server, that receives requests from the and responds to them.

Returning the IP address

In addition to routing the requests, the Heroku load-balancer will also append some headers before passing the request to the application. The X-Forwarded-For header contains the IP address of the client that does the request. This is the external IP address of the client and the only information that my application should return.

The resulting application is very simple:

  1. Start a HTTP server
  2. Wait for requestsp
  3. Extract the IP address from the X-Forwarded-For header
  4. Return the address
  5. Goto 2.

To minimize the effort I implemented the application single-threaded and managed to only use 135 lines of C code. I think this is not bad if you consider that it also includes a minimal HTTP server 😃.

You can test it on your local machine by executing the binary (you might have to re-compile it using make). The only thing that you have to keep in mind is that you have to simulate the Heroku load-balancer by sending the X-Forwared-For header on your own:

./main 8080
curl --header 'X-Forwarded-For: 8.8.8.8' http://127.0.0.1:8080/

After creating the binary, my next step was to deploy it to Heroku.

Heroku Deployment

What I really like about Heroku is how easy it is to deploy your application. All you need to know is how to use git. When you do a git push, Heroku tries to determine what language your project uses. It then takes the matching buildpack to install dependencies, compile the code and run the application. Heroku comes with a number of pre-configured buildpacks for languages like Java, Ruby, Node.js and so on.

Hijacking Node.js

For my Haskell project I hijacked the existing Node.js buildpack. I learned this trick from the Yesod wiki during my attempt to deploy the Haskell application. The idea behind this approach is relatively simple: Because you already have a binary, the buildpack doesn't need to do anything, so you have to find a buildpack that can easily be fooled into doing nothing.

For the Node.js buildpack this works perfectly: You only have to create a package.json file, state that there are no dependencies, give it a fake-name and a fake-version. That's it!

The reason why this works, is that the Node.js buildpack has a simple detect mechanism that only checks for the package.json file. With the detect script, Heroku verifies that the buildpack is responsible for the current project.

Procfile

After I had tricked the Node.js buildpack into accepting the code, the last missing piece was the Procfile. In the Procfile you state what kind of processes your application provides. In my case it's simple, because it's only a web-server in form of the binary.

Pushing the code

The disadvantage of shipping binaries is that you have to compile them before you deploy. In contrast to more conventional Heroku application, you have to run make, add the binary to git and then push the code:

make
git add main
git commit -m "new release"
git push heroku master

You can play with the application on Heroku. I was able to secure the domain ipconfig.herokuapp.com. You can either open it in your browser or use your favorite terminal program:

curl http://ipconfig.herokuapp.com/

If the application isn't used in a while, Heroku will shutdown it's dyno and has to start a new one for you. In my experience this takes around 40 seconds, so don't be surprised if your first request is way too slow.

What is it good for?

If you compare the time invested, with the achieved functionality, I'd say there is nothing to discuss. Writing programs in C is slow. But on the other hand it was interesting to learn all those details about Heroku.

I did this small project just for fun. Nevertheless it's interesting to know how well it "performs". Since it's a single-threaded application I did three tests: 100 requests in sequence, 10 requests in parallel and 100 requests in parallel.

The Heroku load-balancer is able to queue requests until the dyno is available again. That's the reason why the response time is increasing, if more requests are executed in parallel. You could solve this issue by starting more dynos, but that would cost a lot of money.

The numbers

"I only believe in statistics that I doctored myself" -- Winston Churchill

For my benchmark I used ApacheBench. Luckily ab has a switch that produces HTML tables, so here are the hard numbers:

100 requests in sequence

  min avg max
Connect: 48 52 69
Processing: 55 58 65
Total: 103 110 134
ab -n 100 http://ipconfig.herokuapp.com/

100 requests, 10 in parallel

  min avg max
Connect: 49 54 65
Processing: 54 60 63
Total: 103 114 128
ab -c 10 -n 100 http://ipconfig.herokuapp.com/

100 requests in parallel

  min avg max
Connect: 58 142 237
Processing: 186 188 156
Total: 244 330 393
ab -c 100 -n 100 http://ipconfig.herokuapp.com/

I hope you enjoyed this experiment and I'd love to hear your thoughts. You can find my PGP key at Keybase.io.

about

Hi, I'm Jan Ahrens. In this blog you can read about my thoughts on various technical topics.

As you might have already guessed: My opinions are my own and don't necessarily represent those of my employer.

If you want to contact me you can use my PGP key. Its fingerprint is 3762 1152 E099 AB27 04E8 3FD1 B911 E6A2 2B4F 3B5F.

This blog is built with Jekyll. You can find its source code on GitHub.