Hixie-76 protocol
Implemented WebSocket Hixie-76 protocol
This commit is contained in:
parent
7c5c5ed6ce
commit
2d7774fd65
@ -61,10 +61,9 @@ class WebSocket implements ProtocolInterface {
|
||||
public function onRecv(SocketInterface $from, $msg) {
|
||||
$client = $this->_clients[$from];
|
||||
if (true !== $client->isHandshakeComplete()) {
|
||||
$response = $client->setVersion($this->getVersion($msg))->doHandshake($msg);
|
||||
|
||||
$headers = $this->getHeaders($msg);
|
||||
$response = $client->setVersion($this->getVersion($headers))->doHandshake($headers);
|
||||
|
||||
if (is_array($response)) {
|
||||
$header = '';
|
||||
foreach ($response as $key => $val) {
|
||||
if (!empty($key)) {
|
||||
@ -74,6 +73,9 @@ class WebSocket implements ProtocolInterface {
|
||||
$header .= "{$val}\r\n";
|
||||
}
|
||||
$header .= "\r\n";
|
||||
} else {
|
||||
$header = $response;
|
||||
}
|
||||
|
||||
$to = new \Ratchet\SocketCollection;
|
||||
$to->enqueue($from);
|
||||
@ -133,30 +135,66 @@ class WebSocket implements ProtocolInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return array
|
||||
* @todo Consider strtolower all the header keys...right now PHP Changes Sec-WebSocket-X to Sec-Websocket-X...this could change
|
||||
* @todo Put in fallback code if http_parse_headers is not a function
|
||||
* @param array of HTTP headers
|
||||
* @return Version\VersionInterface
|
||||
*/
|
||||
protected function getHeaders($http_message) {
|
||||
return http_parse_headers($http_message);
|
||||
protected function getVersion($message) {
|
||||
$headers = $this->getHeaders($message);
|
||||
|
||||
if (isset($headers['Sec-Websocket-Version'])) { // HyBi
|
||||
if ($headers['Sec-Websocket-Version'] == '8') {
|
||||
return $this->versionFactory('HyBi10');
|
||||
}
|
||||
} elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie
|
||||
return $this->versionFactory('Hixie76');
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException('Could not identify WebSocket protocol');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Version\VersionInterface
|
||||
*/
|
||||
protected function getVersion(array $headers) {
|
||||
if (isset($headers['Sec-Websocket-Version'])) { // HyBi
|
||||
if ($headers['Sec-Websocket-Version'] == '8') {
|
||||
if (null === $this->_versions['HyBi10']) {
|
||||
$this->_versions['HyBi10'] = new Version\HyBi10;
|
||||
protected function versionFactory($version) {
|
||||
if (null === $this->_versions[$version]) {
|
||||
$ns = __CLASS__ . "\\Version\\{$version}";
|
||||
$this->_version[$version] = new $ns;
|
||||
}
|
||||
|
||||
return $this->_versions['HyBi10'];
|
||||
}
|
||||
} elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie
|
||||
return $this->_version[$version];
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException('Could not identify WebSocket protocol');
|
||||
/**
|
||||
* @param string
|
||||
* @return array
|
||||
* @todo Consider strtolower all the header keys...right now PHP Changes Sec-WebSocket-X to Sec-Websocket-X...this could change
|
||||
*/
|
||||
protected function getHeaders($http_message) {
|
||||
return function_exists('http_parse_headers') ? http_parse_headers($http_message) : $this->http_parse_headers($http_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a fallback method for http_parse_headers as not all php installs have the HTTP module present
|
||||
* @internal
|
||||
*/
|
||||
protected function http_parse_headers($http_message) {
|
||||
$retVal = array();
|
||||
$fields = explode("br", preg_replace("%(<|/\>|>)%", "", nl2br($header)));
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (preg_match('%^(GET|POST|PUT|DELETE|PATCH)(\s)(.*)%', $field, $matchReq)) {
|
||||
$retVal["Request Method"] = $matchReq[1];
|
||||
$retVal["Request Url"] = $matchReq[3];
|
||||
} elseif (preg_match('/([^:]+): (.+)/m', $field, $match) ) {
|
||||
$match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
|
||||
if (isset($retVal[$match[1]])) {
|
||||
$retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
|
||||
} else {
|
||||
$retVal[$match[1]] = trim($match[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
}
|
@ -33,10 +33,10 @@ class Client {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array
|
||||
* @return array
|
||||
* @param string
|
||||
* @return array|string
|
||||
*/
|
||||
public function doHandshake(array $headers) {
|
||||
public function doHandshake($headers) {
|
||||
$this->_hands_shook = true;
|
||||
|
||||
return $this->_version->handshake($headers);
|
||||
|
@ -3,26 +3,56 @@ namespace Ratchet\Protocol\WebSocket\Version;
|
||||
|
||||
/**
|
||||
* The Hixie76 is currently implemented by Safari
|
||||
* Not yet complete
|
||||
* Handshake from Andrea Giammarchi (http://webreflection.blogspot.com/2010/06/websocket-handshake-76-simplified.html)
|
||||
* @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
||||
*/
|
||||
class Hixie76 implements VersionInterface {
|
||||
public function handshake(array $headers) {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function handshake($message) {
|
||||
$buffer = $message;
|
||||
$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];
|
||||
|
||||
return "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 unframe($message) {
|
||||
return substr($message, 1, strlen($message) - 2);
|
||||
}
|
||||
|
||||
public function frame($message) {
|
||||
return chr(0) . $message . chr(255);
|
||||
}
|
||||
|
||||
public function sign($key) {
|
||||
protected function _doStuffToObtainAnInt32($key) {
|
||||
return preg_match_all('#[0-9]#', $key, $number) && preg_match_all('# #', $key, $space) ?
|
||||
implode('', $number[0]) / count($space[0]) :
|
||||
''
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* What was I doing here?
|
||||
* @param Headers
|
||||
* @return string
|
||||
*/
|
||||
public function concatinateKeyString($headers) {
|
||||
protected function _createHandshakeThingy($key1, $key2, $code) {
|
||||
return md5(
|
||||
pack('N', $this->_doStuffToObtainAnInt32($key1))
|
||||
. pack('N', $this->_doStuffToObtainAnInt32($key2))
|
||||
. $code
|
||||
, true);
|
||||
}
|
||||
}
|
@ -8,7 +8,12 @@ namespace Ratchet\Protocol\WebSocket\Version;
|
||||
class HyBi10 implements VersionInterface {
|
||||
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||
|
||||
public function handshake(array $headers) {
|
||||
/**
|
||||
* @return array
|
||||
* @todo Use the WebSocket::http_parse_headers wrapper instead of native function (how to get to method is question)
|
||||
*/
|
||||
public function handshake($message) {
|
||||
$headers = http_parse_headers($message);
|
||||
$key = $this->sign($headers['Sec-Websocket-Key']);
|
||||
|
||||
return array(
|
||||
@ -178,6 +183,12 @@ class HyBi10 implements VersionInterface {
|
||||
return $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when doing the handshake to encode the key, verifying client/server are speaking the same language
|
||||
* @param string
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function sign($key) {
|
||||
return base64_encode(sha1($key . static::GUID, 1));
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ namespace Ratchet\Protocol\WebSocket\Version;
|
||||
interface VersionInterface {
|
||||
/**
|
||||
* Perform the handshake and return the response headers
|
||||
* @param array
|
||||
* @return array
|
||||
* @param string
|
||||
* @return array|string
|
||||
*/
|
||||
function handshake(array $headers);
|
||||
function handshake($message);
|
||||
|
||||
/**
|
||||
* Get a framed message as per the protocol and return the decoded message
|
||||
@ -26,12 +26,4 @@ interface VersionInterface {
|
||||
* @return string
|
||||
*/
|
||||
function frame($message);
|
||||
|
||||
/**
|
||||
* Used when doing the handshake to encode the key, verifying client/server are speaking the same language
|
||||
* @param string
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
function sign($key);
|
||||
}
|
Loading…
Reference in New Issue
Block a user