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) {
 | 
					    public function onRecv(SocketInterface $from, $msg) {
 | 
				
			||||||
        $client = $this->_clients[$from];
 | 
					        $client = $this->_clients[$from];
 | 
				
			||||||
        if (true !== $client->isHandshakeComplete()) {
 | 
					        if (true !== $client->isHandshakeComplete()) {
 | 
				
			||||||
 | 
					            $response = $client->setVersion($this->getVersion($msg))->doHandshake($msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $headers  = $this->getHeaders($msg);
 | 
					            if (is_array($response)) {
 | 
				
			||||||
            $response = $client->setVersion($this->getVersion($headers))->doHandshake($headers);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $header = '';
 | 
					                $header = '';
 | 
				
			||||||
                foreach ($response as $key => $val) {
 | 
					                foreach ($response as $key => $val) {
 | 
				
			||||||
                    if (!empty($key)) {
 | 
					                    if (!empty($key)) {
 | 
				
			||||||
@ -74,6 +73,9 @@ class WebSocket implements ProtocolInterface {
 | 
				
			|||||||
                    $header .= "{$val}\r\n";
 | 
					                    $header .= "{$val}\r\n";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                $header .= "\r\n";
 | 
					                $header .= "\r\n";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $header = $response;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $to  = new \Ratchet\SocketCollection;
 | 
					            $to  = new \Ratchet\SocketCollection;
 | 
				
			||||||
            $to->enqueue($from);
 | 
					            $to->enqueue($from);
 | 
				
			||||||
@ -133,30 +135,66 @@ class WebSocket implements ProtocolInterface {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param string
 | 
					     * @param array of HTTP headers
 | 
				
			||||||
     * @return array
 | 
					     * @return Version\VersionInterface
 | 
				
			||||||
     * @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
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function getHeaders($http_message) {
 | 
					    protected function getVersion($message) {
 | 
				
			||||||
        return http_parse_headers($http_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
 | 
					     * @return Version\VersionInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function getVersion(array $headers) {
 | 
					    protected function versionFactory($version) {
 | 
				
			||||||
        if (isset($headers['Sec-Websocket-Version'])) { // HyBi
 | 
					        if (null === $this->_versions[$version]) {
 | 
				
			||||||
            if ($headers['Sec-Websocket-Version'] == '8') {
 | 
					            $ns = __CLASS__ . "\\Version\\{$version}";
 | 
				
			||||||
                if (null === $this->_versions['HyBi10']) {
 | 
					            $this->_version[$version] = new $ns;
 | 
				
			||||||
                    $this->_versions['HyBi10'] = new Version\HyBi10;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return $this->_versions['HyBi10'];
 | 
					        return $this->_version[$version];
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        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
 | 
					     * @param string
 | 
				
			||||||
     * @return array
 | 
					     * @return array|string
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function doHandshake(array $headers) {
 | 
					    public function doHandshake($headers) {
 | 
				
			||||||
        $this->_hands_shook = true;
 | 
					        $this->_hands_shook = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->_version->handshake($headers);
 | 
					        return $this->_version->handshake($headers);
 | 
				
			||||||
 | 
				
			|||||||
@ -3,26 +3,56 @@ namespace Ratchet\Protocol\WebSocket\Version;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The Hixie76 is currently implemented by Safari
 | 
					 * 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 {
 | 
					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) {
 | 
					    public function unframe($message) {
 | 
				
			||||||
 | 
					        return substr($message, 1, strlen($message) - 2);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function frame($message) {
 | 
					    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]) :
 | 
				
			||||||
 | 
					            ''
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    protected function _createHandshakeThingy($key1, $key2, $code) {
 | 
				
			||||||
     * What was I doing here?
 | 
					        return md5(
 | 
				
			||||||
     * @param Headers
 | 
					            pack('N', $this->_doStuffToObtainAnInt32($key1))
 | 
				
			||||||
     * @return string
 | 
					          . pack('N', $this->_doStuffToObtainAnInt32($key2))
 | 
				
			||||||
     */
 | 
					          . $code
 | 
				
			||||||
    public function concatinateKeyString($headers) {
 | 
					        , true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -8,7 +8,12 @@ namespace Ratchet\Protocol\WebSocket\Version;
 | 
				
			|||||||
class HyBi10 implements VersionInterface {
 | 
					class HyBi10 implements VersionInterface {
 | 
				
			||||||
    const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
 | 
					    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']);
 | 
					        $key     = $this->sign($headers['Sec-Websocket-Key']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return array(
 | 
					        return array(
 | 
				
			||||||
@ -178,6 +183,12 @@ class HyBi10 implements VersionInterface {
 | 
				
			|||||||
		return $frame;
 | 
							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) {
 | 
					    public function sign($key) {
 | 
				
			||||||
        return base64_encode(sha1($key . static::GUID, 1));
 | 
					        return base64_encode(sha1($key . static::GUID, 1));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -8,10 +8,10 @@ namespace Ratchet\Protocol\WebSocket\Version;
 | 
				
			|||||||
interface VersionInterface {
 | 
					interface VersionInterface {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Perform the handshake and return the response headers
 | 
					     * Perform the handshake and return the response headers
 | 
				
			||||||
     * @param array
 | 
					     * @param string
 | 
				
			||||||
     * @return array
 | 
					     * @return array|string
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    function handshake(array $headers);
 | 
					    function handshake($message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get a framed message as per the protocol and return the decoded message
 | 
					     * Get a framed message as per the protocol and return the decoded message
 | 
				
			||||||
@ -26,12 +26,4 @@ interface VersionInterface {
 | 
				
			|||||||
     * @return string
 | 
					     * @return string
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    function frame($message);
 | 
					    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