The state of WebSockets, SSL and Sticky Sessions in Amazon Web Services
WebSockets, SSL and Sticky Sessions are “Three Amigos” among all the Cloud providers these days, it would be nice to have them together but they won’t play nice in Amazon Web Services, at least not with Elastic Load Balancing. You can get Client Identity(IP Address) and Sticky Sessions(also known as Session Affinity) but no WebSockets, or you can get WebSockets with no Session Affinity or Client Identity. Let’s find out why.
Amazon ELB can work as a reverse proxy and a network balancer by supporting two families of listeners: The “TCP” family that includes TCP and SSL and the “HTTP” family that includes HTTP and HTTPS. For now (and nobody knows for how long) ELB doesn’t support WebSockets protocol upgrade, and is this where the complications arrives.
So let’s examine some scenarios that could helps understand the issue:
Scenario 1: ELB with HTTP Listeners
This is the most usual setup, an ELB instance in front of your EC2 Instances with HTTP/HTTPS listeners. As described in the documentation, ELB will do a Layer 7 request forwarding just like any reverse proxy like Nginx would do:
The setup works just fine, it’s exactly what ELB was meant for.
- Client Identity via X-Forward Headers.
- Session Affinity.
- SSL Encryption offload from your servers.
- No WebSockets!
- No custom protocols, only HTTP.
Since the listeners will parse HTTP messages sent over the TCP connection, it can inject special headers like ‘X-Forward-IP’ to preserve the Client Identity and inspect cookies to ensure the Session Affinity.
What happens with WebSockets? As you might know, WebSockets is a protocol that works as an upgrade for established HTTP connections, so the server(ELB in this case) must implement this special ‘upgrade’ mechanism. ELB doesn’t support it right now, so no WebSockets for us with this setup.
Scenario 2: ELB with TCP Listeners
In this setup, an ELB instance will be in front of your EC2 Instances doing Layer 4 request forwarding. We use TCP listeners so the backend instances can get the raw stream from the client, this way your backend instances that support WebSockets(let’s say Node.js) can process HTTP request and perform WebSocket upgrades as well. With no HTTP parsing at ELB level, no WebSockets limitation by ELB either. It looks like a win.
This setup allows the backends to process HTTP requests normally because ELB is just forwarding the raw TCP stream from the client.
- You can use WebSockets
- You can use additional protocols over TCP.
- No Session Affinity
- No Client Identity
- Your application instances will consume more CPU by doing SSL Encryption.
We can not have Session Affinity nor X-Forward headers with this setup because ELB is not parsing the HTTP messages, so its impossible to match the cookies to ensure Session Affinity nor Inject special X-Forward headers.
I personally like Nginx, so a combination of http://nginx.org/ + http://code.google.com/p/nginx-sticky-module + https://github.com/yaoweibin/nginx_tcp_proxy_module will do the trick:
Of course there are other solutions that could work for you, for example, I heard that HAproxy can do WebSockets out of the box.
Also there is Hipache, a Reverse Proxy + Websockets implemented in Node.js. I’d love to hear alternatives, anyone?
Ryan Dahl’s libuv video Tutorial Revisited
In 2010, Ryan Dahl, the Author of Node.js, created a 53 minutes video to get you started with the libuv Async IO library, it’s an amazing tutorial.
Even when the video was created with a early version of libuv, you can follow the tutorial fluently. I wanted to highlight some of the differences that I noticed to get it working by Apr 2012:
EV Multiplicity Flag Use EV_MULTIPLICITY set to 1 instead of 0
gcc ... -DEV_MULTIPLICITY=1
**Header Files* Includes are in uv/include, compiler arg -Iuv/include should be used and #include “uv.h” instead of “uv/uv.h”
uv_tcp_init now requires uv_loop_t* as first parameter and the handle as the second parameter, so you should use it as: uv_tcp_init(uv_default_loop(), &server);
uv_bind is not generic anymore, in the video Ry is using TCP so you should use the TCP version of bind, that would be uv_tcp_bind, no argument changes.
uv_accept no longer accepts a close callback, you will pass the callback when you invoke uv_close. uv_close is usually called when the uv_read_cb callback is invoked and UV_EOF is detected in the error code.
Also the ->data handle is no longer set by the last argument, you are going to set the pointer yourself after the uv_handle_t is initialized.
client_handle->data = your_thing;
uv_read_cb now takes uv_stream_t* instead of uv_tcp_t*. Simple cast fix.
uv_write now takes the handle, the buffers, the count and a callback. You don’t need to use uv_req_init anymore, you just allocate it and release it when on the callback. None of the requests in libuv are reused, you need to free them when the callback is called.
The http-parser library didn’t suffered any change at all.
Happy libuv hacking!
Hello: Why I’m learning D
I want to learn D programming language because I find it terribly interesting, it has nice features of all programming languages and paradigms I love: Object-Oriented, Functional with Garbage Collector and no Virtual Machine! A very nice System Programming Language.
So what’s the most easy way to get started with D? Download the tools and start practicing with some script-like programs, check this gist:
If I save that gist as “hello.d” and change its permission to executable like this:
We can execute the program from the console just like an interpreted program, in’t it amazing?
Of course, D is a compiled programming language, however, that “she-bang” notation makes it easy edit and run so you can quickly experiment with the syntax. You can also directly invoke the “dmd” compiler when you are writing some serious programs: