diff --git a/src/Client.php b/src/Client.php index 9f09541..be694a7 100644 --- a/src/Client.php +++ b/src/Client.php @@ -78,7 +78,7 @@ public function initialize($keepAlive = false) /** * Reads a message from the socket * - * @return MessageInterface Message read from the socket + * @return string Message read from the socket */ public function read() { @@ -89,6 +89,9 @@ public function read() /** * Emits a message through the engine * + * @param string $event + * @param array $args + * * @return $this */ public function emit($event, array $args) diff --git a/src/Engine/AbstractSocketIO.php b/src/Engine/AbstractSocketIO.php index 0ad6483..7120e7b 100644 --- a/src/Engine/AbstractSocketIO.php +++ b/src/Engine/AbstractSocketIO.php @@ -12,6 +12,7 @@ namespace ElephantIO\Engine; use DomainException; +use ElephantIO\Engine\SocketIO\Session; use RuntimeException; use Psr\Log\LoggerInterface; @@ -37,7 +38,7 @@ abstract class AbstractSocketIO implements EngineInterface /** @var array cookies received during handshake */ protected $cookies = []; - /** @var string[] Session information */ + /** @var Session Session information */ protected $session; /** @var mixed[] Array of options for the engine */ @@ -190,6 +191,8 @@ public function getName() /** * Parse an url into parts we may expect * + * @param string $url + * * @return string[] information on the given URL */ protected function parseUrl($url) diff --git a/src/Engine/SocketIO/Session.php b/src/Engine/SocketIO/Session.php index 27ac94e..0c5b668 100644 --- a/src/Engine/SocketIO/Session.php +++ b/src/Engine/SocketIO/Session.php @@ -42,7 +42,12 @@ public function __construct($id, $interval, $timeout, array $upgrades) 'interval' => $interval]; } - /** The property should not be modified, hence the private accessibility on them */ + /** + * The property should not be modified, hence the private accessibility on them + * + * @param string $prop + * @return mixed + */ public function __get($prop) { static $list = ['id', 'upgrades']; diff --git a/src/Engine/SocketIO/Version0X.php b/src/Engine/SocketIO/Version0X.php index 8d75f80..8b388c3 100644 --- a/src/Engine/SocketIO/Version0X.php +++ b/src/Engine/SocketIO/Version0X.php @@ -68,7 +68,7 @@ public function connect() $this->stream = stream_socket_client($host, $errors[0], $errors[1], $this->options['timeout'], STREAM_CLIENT_CONNECT, stream_context_create($this->context)); if (!is_resource($this->stream)) { - throw new SocketException($error[0], $error[1]); + throw new SocketException($errors[0], $errors[1]); } stream_set_timeout($this->stream, $this->options['timeout']); diff --git a/src/Engine/SocketIO/Version1X.php b/src/Engine/SocketIO/Version1X.php index 7783650..680fe46 100644 --- a/src/Engine/SocketIO/Version1X.php +++ b/src/Engine/SocketIO/Version1X.php @@ -167,7 +167,9 @@ protected function handshake() throw new ServerConnectionFailureException; } - $decoded = json_decode(substr($result, strpos($result, '{')), true); + $open_curly_at = strpos($result, '{'); + $todecode = substr($result, $open_curly_at, strrpos($result, '}')-$open_curly_at+1); + $decoded = json_decode($todecode, true); if (!in_array('websocket', $decoded['upgrades'])) { throw new UnsupportedTransportException('websocket'); @@ -184,16 +186,27 @@ protected function handshake() $this->session = new Session($decoded['sid'], $decoded['pingInterval'], $decoded['pingTimeout'], $decoded['upgrades']); } - /** Upgrades the transport to WebSocket */ - private function upgradeTransport() + /** + * Upgrades the transport to WebSocket + * + * FYI: + * Version "2" is used for the EIO param by socket.io v1 + * Version "3" is used by socket.io v2 + */ + protected function upgradeTransport() { $query = ['sid' => $this->session->id, 'EIO' => $this->options['version'], - 'use_b64' => $this->options['use_b64'], 'transport' => static::TRANSPORT_WEBSOCKET]; + if ($this->options['version'] === 2) + $query['use_b64'] = $this->options['use_b64']; + $url = sprintf('/%s/?%s', trim($this->url['path'], '/'), http_build_query($query)); - $key = base64_encode(sha1(uniqid(mt_rand(), true), true)); + $hash = sha1(uniqid(mt_rand(), true), true); + if ($this->options['version'] !== 2) + $hash = substr($hash, 0, 16); + $key = base64_encode($hash); $origin = '*'; $headers = isset($this->context['headers']) ? (array) $this->context['headers'] : [] ; @@ -208,7 +221,7 @@ private function upgradeTransport() } $request = "GET {$url} HTTP/1.1\r\n" - . "Host: {$this->url['host']}\r\n" + . "Host: {$this->url['host']}:{$this->url['port']}\r\n" . "Upgrade: WebSocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Key: {$key}\r\n" @@ -234,7 +247,8 @@ private function upgradeTransport() $this->write(EngineInterface::UPGRADE); //remove message '40' from buffer, emmiting by socket.io after receiving EngineInterface::UPGRADE - $this->read(); + if ($this->options['version'] === 2) + $this->read(); } } diff --git a/src/Engine/SocketIO/Version2X.php b/src/Engine/SocketIO/Version2X.php new file mode 100644 index 0000000..a3372b3 --- /dev/null +++ b/src/Engine/SocketIO/Version2X.php @@ -0,0 +1,55 @@ + + * @link https://tools.ietf.org/html/rfc6455#section-5.2 Websocket's RFC + */ +class Version2X extends Version1X +{ + + /** {@inheritDoc} */ + public function getName() + { + return 'SocketIO Version 2.X'; + } + + /** {@inheritDoc} */ + protected function getDefaultOptions() + { + $defaults = parent::getDefaultOptions(); + + $defaults['version'] = 3; + + return $defaults; + } +} + diff --git a/src/Payload/Encoder.php b/src/Payload/Encoder.php index 2eb7725..4a51d56 100644 --- a/src/Payload/Encoder.php +++ b/src/Payload/Encoder.php @@ -25,11 +25,12 @@ class Encoder extends AbstractPayload { private $data; + /** @var string */ private $payload; /** * @param string $data data to encode - * @param integer $opcode OpCode to use (one of AbstractPayload's constant) + * @param integer $opCode OpCode to use (one of AbstractPayload's constant) * @param bool $mask Should we use a mask ? */ public function __construct($data, $opCode, $mask)