Making websocket work over HAProxy

184 views
Skip to first unread message

cjb_livefyre

unread,
Oct 12, 2010, 6:52:06 AM10/12/10
to Tornado Web Server, b...@livefyre.com, bwmc...@evilmonkeylabs.com
Greetings, wondering if this might be of use to someone else...

Using this node.js commit as my reference:
http://github.com/Worlize/Socket.IO-node/commit/9982232032771574ceb68e2bccee4e43fd5af887#diff-0

I set out to make the latest tornado WebSocketHandler able to
establish connections over a proxy in http mode. I was able to do it
in much the same way (ahem, and with less code) as the node solution
above. The key is to delay reading and responding to the
cryptographic nonce until AFTER the 101 response has been sent. In
general I'd like to hear your thoughts...here's what it looks like

class WebSocketHandler(tornado.web.RequestHandler):

...

def _execute(self, transforms, *args, **kwargs):
self.open_args = args
self.open_kwargs = kwargs
try:
self.ws_request = WebSocketRequest(self.request)
except ValueError:
logging.debug("Malformed WebSocket request received")
self._abort()
return

self.stream.write(
"HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Server: TornadoServer/%(version)s\r\n"
"Sec-WebSocket-Origin: %(origin)s\r\n"
"Sec-WebSocket-Location: ws://%(host)s%(path)s\r\n\r\n" %
(dict(
version=tornado.version,
origin=self.request.headers["Origin"],
host=self.request.host,

path=self.request.path)),self._do_handle_challenge)

def _do_handle_challenge(self):
#This is called after we write the first part of the header,
but
#before we write the challenge response.
#Allows an intermediary like HAProxy to use standard http by
#writing the 101 response before acquiring the nonce for
challenge.
self.stream.read_bytes(8, self._handle_challenge)

def _handle_challenge(self, challenge):
try:
challenge_response =
self.ws_request.challenge_response(challenge)
except ValueError:
logging.debug("Malformed key data in WebSocket request")
self._abort()
return
self._write_response(challenge_response)

def _write_response(self, challenge):
self.stream.write(
"%(challenge)s" % (dict(
challenge=challenge)))
self.async_callback(self.open)(*self.open_args,
**self.open_kwargs)
self._receive_message()

Ben Darnell

unread,
Oct 12, 2010, 7:08:46 PM10/12/10
to python-...@googlegroups.com, b...@livefyre.com, bwmc...@evilmonkeylabs.com
This looks like a good change. Can you put it in a diff or send a
pull request to make sure I can apply it cleanly?

Thanks,
-Ben

Reply all
Reply to author
Forward
0 new messages