Articles

Erlang Websocket Server using Cowboy


NOTE: This is the translation of the original article I wrote in spanish for the wiki of Erlang Argentina.

Why choose Cowboy as the HTTP Framework

Out of curiosity, and because some of my own projects needed it, I decided to go ahead and try writing a websocket server in erlang. After evaluating some of the available options (so I dont have to implement the whole websocket RFC -and their different drafts/versions- myself), I chose the Cowboy framework. This article tries to show my conclusions after the experience.

Note that I dont have that much experience using Cowboy, so I'd really like to know whatever comment you may think of that might make this article (or the code) any better. By the way, you can find the code for this article at GitHub.

Cowboy: The Most widely used HTTP/SPDY framework for Erlang

Well, it was the "last man standing" at the end of the tests. Up to now (05/2012), the available alternatives for writing a websocket server in erlang are:

  • Socket.io-Erlang: This was the most tempting option to me, because it would've been great to use socket.io on the client side. Socket.io is really popular (specially in NodeJS), and also because one of the authors (Frederic Trottier-Hebert) is also the author of LearnYouSomeErlang. Unfortunately, the authors decided to not add any more features beyond those provided by socket.io 0.6.x (socket.io is now in the 0.9.x version) due to not being ok with internal changes to the socket.io code. So this pretty much killed this one.
  • MochiWeb: This one is incredible popular, and allows you to quickly create HTTP servers and would've been a great opportunity to use it, but it does not support websockets natively.
  • Yaws: It's a complete HTTP server, designed as a robust and scalable solution to server dynamic and static content, and also supports websockets. It seemed to me this was kind of an overkill for my particular situation, but I would have definitely used it if all the other options failed.
  • Misultin: It's a library used to handle HTTP requests in an easy way. I didn't use this one because the author himself says (in the project README file), that he stopped further development because of the increased interest/attention and development on mochiweb and cowboy.
  • Cowboy: It has quite a different (and interesting) approach, because handling HTTP requests is just a part of the framework. Cowboy is actually more like a generic pool of connection accept()ors, that we can configure to handle normal TCP or SSL connections as transports, and handle protocols above them (like HTTP or any other we might like to). Out of the box, it supports HTTP, but it is essentially generic and extendable through behaviors (for example to support other protocols). A big drawback, though: documentation is scarce :( So you will need to browse through the source code to find the answers to your questions.

An example websocket server in Erlang using Cowboy

So to start trying Cowboy, I decided to create a rebar application, which I called "erws" (Erlang WebSocket, so much for my creative talent).

The main module in this application is erws_handler.erl, but I'll show all the necessary files to have the complete application.

When the application starts, it makes cowboy listens at port 10100 and dispatch connections to erws_handler as HTTP requests (you will see how this is done in erws_app).

erws_handler implements two behaviors (cowboy_http_handler and cowboy_http_websocket_handler). The first one is used to respond to common HTTP GET requests and start the websocket handshake on it, by sending the necessary headers to make the "upgrade" of the HTTP connection (as the protocol states). For us this is really quite easy, we just need to return a specific tuple in erws_handler:init/1 (see the code). The dirty work is actually done by cowboy through the module cowboy_http_websocket.

Once this is done, and through the api that the cowboy_http_websocket_handler behavior imposes, we will handle the incoming data as messages, very much like a common gen_server.

erws_handler.erl: The hearth and soul of our erlang websocket server

The main module, will handle and serve the requests.

index.html: The HTML5 and Javascript websocket client

This is the HTML page we're going to use as a websocket client. I've tried this in Chrome, but it should work on any browser with HTML5 capabilities. You can find the Websocket API here.

erws_app.src: The erlang application descriptor

Our erws_app.src will be used by rebar to generate the application descriptor.

NOTE: Be sure to add the "crypto" application. I spent quite a few minutes trying to figure out why the websocket connection was failing, and it turned out that I missed to include the crypto app, used by cowboy at the handshake stage of the websocket connection.

rebar.config

This is the project configuration file. Our dependencies are cowboy and lager (optional).

erws_app.erl: Configuring the Cowboy framework to listen and dispatch the HTTP requests

Our application. This code configures cowboy to listen for and accept connections in the given port (with the given protocol), and then dispatch the requests to the given modules.

erws_sup.erl: The top level supervisor of our erlang application

This is our top supervisor. In this case, it does absolutely nothing (pretty much like a human supervisor :D).

Run the example

To run the example, just do:

HTTP, Websockets, and Erlang: With Cowboy this is all too easy!

Although I'm not a big fan of dependencies (and/or using whatever frameworks are out there), Cowboy does a great job, and I'm sure that it will be even a better choice for more complex applications, where other kind of content could be served by HTTP(S) besides the websocket content.

Overall, Cowboy lets you focus on the real deal (the features you need) without having to worry about more mundane tasks, like coding a pool of workers to handle connections, or (like in this particular case), the implementation details for the websocket protocol. With just a few lines of code, we can really put some magic into those dull webapps :)