diff --git a/src/Engine/.Transport.php.swp b/src/Engine/.Transport.php.swp new file mode 100644 index 0000000..a7e8943 Binary files /dev/null and b/src/Engine/.Transport.php.swp differ diff --git a/src/Engine/Engine.php b/src/Engine/Engine.php index 86bfb43..6054ca6 100644 --- a/src/Engine/Engine.php +++ b/src/Engine/Engine.php @@ -80,17 +80,11 @@ public function dealRequest($err, $success, $req, $res) protected function sendErrorMessage($req, $res, $code) { - $headers = array('Content-Type'=> 'application/json'); + $headers = array( + 'Content-Type'=> 'application/json', + 'Access-Control-Allow-Origin' => '*' + ); - if(isset($req->headers['origin'])) - { - $headers['Access-Control-Allow-Credentials'] = 'true'; - $headers['Access-Control-Allow-Origin'] = $req->headers['origin']; - } - else - { - $headers['Access-Control-Allow-Origin'] = '*'; - } $res->writeHead(400, '', $headers); $res->end(json_encode(array( 'code' => $code, @@ -222,10 +216,27 @@ public function dealWebSocketConnect($err, $success, $req, $res) return; } + if(isset($req->_query['sid'])) { + if(!isset($this->clients[$req->_query['sid']])) + { + self::sendErrorMessage($req, $res, 'upgrade attempt for closed client'); + return; + } + $client = $this->clients[$req->_query['sid']]; + if($client->upgrading) + { + self::sendErrorMessage($req, $res, 'transport has already been trying to upgrade'); + return; + } + if($client->upgraded) + { + self::sendErrorMessage($req, $res, 'transport had already been upgraded'); + return; + } $transport = new WebSocket($req); - $this->clients[$req->_query['sid']]->maybeUpgrade($transport); + $client->maybeUpgrade($transport); } else { diff --git a/src/Engine/Socket.php b/src/Engine/Socket.php index 267ab64..4672038 100644 --- a/src/Engine/Socket.php +++ b/src/Engine/Socket.php @@ -6,6 +6,7 @@ class Socket extends Emitter { public $id = 0; public $server = null; + public $upgrading = false; public $upgraded = false; public $readyState = 'opening'; public $writeBuffer = array(); @@ -29,6 +30,7 @@ public function __construct($id, $server, $transport, $req) public function maybeUpgrade($transport) { + $this->upgrading = true; $this->upgradeTimeoutTimer = Timer::add( $this->server->upgradeTimeout, array($this, 'upgradeTimeoutCallback'), @@ -53,11 +55,11 @@ public function onUpgradePacket($packet) $this->upgradeTransport->send(array(array('type'=> 'pong', 'data'=> 'probe'))); //$this->transport->shouldClose = function(){}; Timer::del($this->checkIntervalTimer); - $this->checkIntervalTimer = Timer::add(0.1, array($this, 'check')); + $this->checkIntervalTimer = Timer::add(0.5, array($this, 'check')); } else if('upgrade' === $packet['type'] && $this->readyState !== 'closed') { - $this->cleanup(); + $this->upgradeCleanup(); $this->upgraded = true; $this->clearTransport(); $this->setTransport($this->upgradeTransport); @@ -74,7 +76,7 @@ public function onUpgradePacket($packet) { if(!empty($this->upgradeTransport)) { - $this->cleanup(); + $this->upgradeCleanup(); $this->upgradeTransport->close(); $this->upgradeTransport = null; } @@ -83,13 +85,17 @@ public function onUpgradePacket($packet) } - public function cleanup() + public function upgradeCleanup() { + $this->upgrading = false; Timer::del($this->checkIntervalTimer); Timer::del($this->upgradeTimeoutTimer); - $this->upgradeTransport->removeListener('packet', array($this, 'onUpgradePacket')); - $this->upgradeTransport->removeListener('close', array($this, 'onUpgradeTransportClose')); - $this->upgradeTransport->removeListener('error', array($this, 'onUpgradeTransportError')); + if(!empty($this->upgradeTransport)) + { + $this->upgradeTransport->removeListener('packet', array($this, 'onUpgradePacket')); + $this->upgradeTransport->removeListener('close', array($this, 'onUpgradeTransportClose')); + $this->upgradeTransport->removeListener('error', array($this, 'onUpgradeTransportError')); + } $this->removeListener('close', array($this, 'onUpgradeTransportClose')); } @@ -101,15 +107,18 @@ public function onUpgradeTransportClose() public function onUpgradeTransportError($err) { echo $err; - $this->cleanup(); - $this->upgradeTransport->close(); - $this->upgradeTransport = null; + $this->upgradeCleanup(); + if($this->upgradeTransport) + { + $this->upgradeTransport->close(); + $this->upgradeTransport = null; + } } public function upgradeTimeoutCallback($transport) { - echo("client did not complete upgrade - closing transport\n"); - $this->cleanup(); + //echo("client did not complete upgrade - closing transport\n"); + $this->upgradeCleanup(); if('open' === $transport->readyState) { $transport->close(); @@ -212,7 +221,8 @@ public function clearTransport() public function onClose($reason = '', $description = null) { - if ('closed' !== $this->readyState) { + if ('closed' !== $this->readyState) + { Timer::del($this->pingTimeoutTimer); Timer::del($this->checkIntervalTimer); $this->checkIntervalTimer = null; @@ -229,9 +239,11 @@ public function onClose($reason = '', $description = null) $this->request = null; $this->upgradeTransport = null; $this->removeAllListeners(); - $this->transport->removeAllListeners(); - $this->transport = null; - + if(empty($this->transport)) + { + $this->transport->removeAllListeners(); + $this->transport = null; + } } } diff --git a/src/Engine/Transports/Polling.php b/src/Engine/Transports/Polling.php index 09407a6..c0d4468 100644 --- a/src/Engine/Transports/Polling.php +++ b/src/Engine/Transports/Polling.php @@ -58,15 +58,18 @@ public function onPollRequest($req, $res) public function pollRequestOnClose() { - $this->pollRequestClean(); $this->onError('poll connection closed prematurely'); + $this->pollRequestClean(); } public function pollRequestClean() { - $this->req->res = null; - $this->req->onClose = $this->req->cleanup = null; - $this->req = $this->res = null; + if(isset($this->req)) + { + $this->req->res = null; + $this->req->onClose = $this->req->cleanup = null; + $this->req = $this->res = null; + } } public function onDataRequest($req, $res) @@ -79,8 +82,6 @@ public function onDataRequest($req, $res) return; } - $isBinary = 'application/octet-stream' == $req->headers['content-type']; - $this->dataReq = $req; $this->dataRes = $res; $req->onClose = array($this, 'dataRequestOnClose'); @@ -118,20 +119,11 @@ public function dataRequestOnEnd () $this->onData($this->chunks); $headers = array( - // text/html is required instead of text/plain to avoid an - // unwanted download dialog on certain user-agents (GH-43) 'Content-Type'=> 'text/html', - 'Content-Length'=> 2 + 'Content-Length'=> 2, + 'X-XSS-Protection' => '0', ); - // prevent XSS warnings on IE - // https://github.com/LearnBoost/socket.io/pull/1333 - $ua = $this->dataReq->headers['user-agent']; - if ($ua && (strpos($ua, ';MSIE') || strpos($ua, 'Trident/'))) - { - $headers['X-XSS-Protection'] = '0'; - } - $this->dataRes->writeHead(200, '', $this->headers($this->dataReq, $headers)); $this->dataRes->end('ok'); $this->dataRequestCleanup(); @@ -170,7 +162,8 @@ public function onClose() } public function send($packets) - { + { + $this->writable = false; if($this->shouldClose) { echo('appending close packet to payload'); @@ -186,26 +179,25 @@ public function write($data) { $this->doWrite($data); call_user_func($this->req->cleanup); - $this->writable = false; } public function doClose($fn) { if(!empty($this->dataReq)) { - echo('aborting ongoing data request'); + //echo('aborting ongoing data request'); $this->dataReq->destroy(); } if($this->writable) { - echo('transport writable - closing right away'); + //echo('transport writable - closing right away'); $this->send(array(array('type'=> 'close'))); call_user_func($fn); } else { - echo('transport not writable - buffering orderly close'); + //echo("transport not writable - buffering orderly close\n"); $this->shouldClose = $fn; } } diff --git a/src/Engine/Transports/PollingJsonp.php b/src/Engine/Transports/PollingJsonp.php index a57c8e6..ec6c272 100644 --- a/src/Engine/Transports/PollingJsonp.php +++ b/src/Engine/Transports/PollingJsonp.php @@ -39,7 +39,7 @@ public function doWrite($data) 'Content-Length'=> strlen($data), 'X-XSS-Protection'=>'0' ); - + if(empty($this->res)){echo new \Exception('empty $this->res');return;} $this->res->writeHead(200, '',$this->headers($this->req, $headers)); $this->res->end($data); } diff --git a/src/Engine/Transports/PollingXHR.php b/src/Engine/Transports/PollingXHR.php index 80df403..9fcb9a3 100644 --- a/src/Engine/Transports/PollingXHR.php +++ b/src/Engine/Transports/PollingXHR.php @@ -28,16 +28,10 @@ public function doWrite($data) $content_length = strlen($data); $headers = array( 'Content-Type'=> $content_type, - 'Content-Length'=> $content_length + 'Content-Length'=> $content_length, + 'X-XSS-Protection' => '0', ); - - // prevent XSS warnings on IE - // https://github.com/LearnBoost/socket.io/pull/1333 - $ua = $this->req->headers['user-agent']; - if ($ua && (strpos($ua, ';MSIE') || strpos($ua, 'Trident/'))) - { - $headers['X-XSS-Protection'] = '0'; - } + if(empty($this->res)){echo new \Exception('empty this->res');return;} $this->res->writeHead(200, '', $this->headers($this->req, $headers)); $this->res->end($data); }