Thursday, June 3, 2010

WebSocket Handshake 76 Simplified

update
there was a superfluous CR+LN with char 0x00 that was causing buffer troubles, now fixed



I am working during my free time (... recently extremely hard to have ...) over a little project that I'd like to show at the Front Trends event this October and WebSocket is the key of this project.

While 2 days ago I eventually found a way to communicate in few lines of php with a WebSocket, yesterday Chromium blog announced they "simply changed it", causing basically problems to all those projects based over the good old handshake75.

After I have found in Axod's Hack that somebody else had basically my same thoughts, I still could not find any valid example able to do the new handshake ... so here I am with the first draft-ietf-hybi-thewebsocketprotocol-00 php implementation I know, inspired somehow from the go version.


<?php

class WebSocketHandshake {

/*! Easy way to handshake a WebSocket via draft-ietf-hybi-thewebsocketprotocol-00
* @link http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt
* @author Andrea Giammarchi
* @blog webreflection.blogspot.com
* @date 4th June 2010
* @example
* // via function call ...
* $handshake = WebSocketHandshake($buffer);
* // ... or via class
* $handshake = (string)new WebSocketHandshake($buffer);
*
* socket_write($socket, $handshake, strlen($handshake));
*/

private $__value__;

public function __construct($buffer) {
$resource = $host = $origin = $key1 = $key2 = $protocol = $code = $handshake = null;
preg_match('#GET (.*?) HTTP#', $buffer, $match) && $resource = $match[1];
preg_match("#Host: (.*?)\r\n#", $buffer, $match) && $host = $match[1];
preg_match("#Sec-WebSocket-Key1: (.*?)\r\n#", $buffer, $match) && $key1 = $match[1];
preg_match("#Sec-WebSocket-Key2: (.*?)\r\n#", $buffer, $match) && $key2 = $match[1];
preg_match("#Sec-WebSocket-Protocol: (.*?)\r\n#", $buffer, $match) && $protocol = $match[1];
preg_match("#Origin: (.*?)\r\n#", $buffer, $match) && $origin = $match[1];
preg_match("#\r\n(.*?)\$#", $buffer, $match) && $code = $match[1];
$this->__value__ =
"HTTP/1.1 101 WebSocket Protocol Handshake\r\n".
"Upgrade: WebSocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Origin: {$origin}\r\n".
"Sec-WebSocket-Location: ws://{$host}{$resource}\r\n".
($protocol ? "Sec-WebSocket-Protocol: {$protocol}\r\n" : "").
"\r\n".
$this->_createHandshakeThingy($key1, $key2, $code)
;
}

public function __toString() {
return $this->__value__;
}

private function _doStuffToObtainAnInt32($key) {
return preg_match_all('#[0-9]#', $key, $number) && preg_match_all('# #', $key, $space) ?
implode('', $number[0]) / count($space[0]) :
''
;
}

private function _createHandshakeThingy($key1, $key2, $code) {
return md5(
pack('N', $this->_doStuffToObtainAnInt32($key1)).
pack('N', $this->_doStuffToObtainAnInt32($key2)).
$code,
true
);
}
}

// handshake headers strings factory
function WebSocketHandshake($buffer) {
return (string)new WebSocketHandshake($buffer);
}

?>


I am pretty sure above code does not need any other comment and methods are "as much semantic as possible", since I completely agree about the Axod point and the fact it's both over engineered and absolutely badly documented via those "specs" ... weird from a company famous for its simplicity concept that maybe this time forgot some KISS approach ...

No comments:

Post a Comment