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:
- Start a HTTP server
- Wait for requestsp
- Extract the IP address from the
X-Forwarded-For
header - Return the address
- 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.