Websockets are the next big thing for full-duplex communication on the web. At least that is what they are saying for a while now. Actual implementations are hard to find and documentation is a bit sparse. However, the idea of full-duplex communication is intriguing, the most used example is a webchat client. No more polling, long-polling, keep-a-lives and other tricks. Since there are already an over abundance of webchat clients it is not really a strong use-case. We figured that an interesting use-case would to have a multi-user GIS where you can actually see where the other guy is, what he is seeing and together edit the map; think google-docs for map editing.
Websockets are really easy to start with, especially if you use node.js, on the client it is:
var url = ws://host/
var ws = new WebSocket(url, 'connect');
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
ws.onerror = onError;
On the server I used this; basically you start a websocket server and define what it has to do when people connect, send messages and disconnect again. This worked like a charm. In no time we were sending view-extents, features and the like around on the local network.
The problem however is that websockets are really really new. There is not that much information about them and hardly any tools to work with them. Most of the hardware of the internet does not know how to deal with it. We already had a problem when the node.js server was moved onto a different network within the company. The gateway didn’t understand the websocket packages and dropped them. There is a nice article explaining what is happening and what you should do. Basically the way forward is not using websockets (ws://) but secure websockets (wss://). So while websockets are new and not too well documented, secure websockets are a complete disaster in that respect.
Setting up a secure websocket with node.js is also very easy; just use wss:// on the client side and on the serverside use:
var http = require('https');
var options = {
key: fs.readFileSync( 'ssl/socket-key.pem'),
cert: fs.readFileSync('ssl/socket-cert.pem')
};
var server = http.createServer(options, function(request, response) {
response.writeHead(200);
response.end();
});
....etc
Since I am using self-signed certificates I needed to get the user sign the certificate, otherwise the wss:// connection fails silently. So I added an empty HTTP 200 response so the browser would show an unsecure certificate warning. This worked nicely through the company gateways. However, it was still impossible to access the server from the outside, because non of the proxy server we use understand websocket or secure websocket. So while I made a little progress within the different networks of the company I still couldn’t access the socket server from outside the network.
It took me a while, but with the help of this article I figured out the trick. He uses stunnel to unencrypt the secure websocket, but I prefer a one stop shop so: You need to setup a proxy facing the internet using the latest haproxy (with SSL support). Configure it as such:
global
log 127.0.0.1 local0 info
maxconn 4096
user haproxy
group haproxy
daemon
#debug
#quiet
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
option http-server-close
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
backend ws
balance roundrobin
option forwardfor # This sets X-Forwarded-For
timeout connect 86400000s
timeout server 86400000s
server ws1 localhost:8080 weight 1 maxconn 1024 check
backend ww
balance roundrobin
option forwardfor # This sets X-Forwarded-For
timeout connect 10s
timeout server 30s
server ww1 localhost
frontend https_proxy
bind *:443 ssl crt /etc/ssl/cert.key_pem
mode http
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws
use_backend ws if is_websocket
timeout client 86400000s
default_backend ww
What this does is providing an https ‘server’ on the internet and filtering the wss requests out, unencrypt them and send them to the node.js instance. http://afitnerd.com really did a great job explaining their setup, only two things weren’t that clear. The first is that the client is using the wss:// protocol, so your config looks like this:
var url = wss://host/
var ws = new WebSocket(url, 'connect');
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
ws.onerror = onError;
The second is that the server still provides a non secure websocket service. So you need to use var http = require('http'); and not var https = require('https');. Now we have secure websockets working through all proxies, gateways and routers between you and the ws server, so without further ado:
Please accept the self-signed certificate, use a websocket enabled browser and click here.