diff --git a/src/Handshake/Negotiator.php b/src/Handshake/Negotiator.php index 69a5fe7..4b6373c 100644 --- a/src/Handshake/Negotiator.php +++ b/src/Handshake/Negotiator.php @@ -1,15 +1,9 @@ verifier = new RequestVerifier; @@ -44,7 +38,7 @@ class Negotiator implements NegotiatorInterface { /** * {@inheritdoc} */ - public function isProtocol(RequestInterface $request) { + public function isProtocol(ServerRequestInterface $request) { $version = (int)(string)$request->getHeader('Sec-WebSocket-Version'); return ($this->getVersionNumber() === $version); @@ -60,34 +54,16 @@ class Negotiator implements NegotiatorInterface { /** * {@inheritdoc} */ - public function handshake(RequestInterface $request) { + public function handshake(ServerRequestInterface $request) { if (true !== $this->verifier->verifyAll($request)) { return new Response(400); } - return new Response(101, array( + return new Response(101, [ 'Upgrade' => 'websocket' , 'Connection' => 'Upgrade' , 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')) - )); - } - - /** - * @deprecated - * @param \Ratchet\ConnectionInterface $conn - * @param \Ratchet\MessageInterface $coalescedCallback - * @return \Ratchet\WebSocket\Version\RFC6455\Connection - */ - public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) { - $upgraded = new Connection($conn); - - if (!isset($upgraded->WebSocket)) { - $upgraded->WebSocket = new \StdClass; - } - - $upgraded->WebSocket->coalescedCallback = $coalescedCallback; - - return $upgraded; + ]); } /** @@ -96,102 +72,7 @@ class Negotiator implements NegotiatorInterface { * @param string $data */ public function onMessage(ConnectionInterface $from, $data) { - $overflow = ''; - if (!isset($from->WebSocket->message)) { - $from->WebSocket->message = $this->newMessage(); - } - - // There is a frame fragment attached to the connection, add to it - if (!isset($from->WebSocket->frame)) { - $from->WebSocket->frame = $this->newFrame(); - } - - $from->WebSocket->frame->addBuffer($data); - if ($from->WebSocket->frame->isCoalesced()) { - $frame = $from->WebSocket->frame; - - if ($opcode > 2) { - switch ($opcode) { - case $frame::OP_CLOSE: - $closeCode = 0; - - $bin = $frame->getPayload(); - - if (empty($bin)) { - return $from->close(); - } - - if (strlen($bin) >= 2) { - list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2))); - } - - if (!$this->isValidCloseCode($closeCode)) { - return $from->close($frame::CLOSE_PROTOCOL); - } - - if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) { - return $from->close($frame::CLOSE_BAD_PAYLOAD); - } - - return $from->close($frame); - break; - case $frame::OP_PING: - $from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG)); - break; - case $frame::OP_PONG: - break; - default: - return $from->close($frame::CLOSE_PROTOCOL); - break; - } - - $overflow = $from->WebSocket->frame->extractOverflow(); - - unset($from->WebSocket->frame, $frame, $opcode); - - if (strlen($overflow) > 0) { - $this->onMessage($from, $overflow); - } - - return; - } - - $overflow = $from->WebSocket->frame->extractOverflow(); - - $from->WebSocket->message->addFrame($from->WebSocket->frame); - unset($from->WebSocket->frame); - } - - if ($from->WebSocket->message->isCoalesced()) { - $parsed = $from->WebSocket->message->getPayload(); - unset($from->WebSocket->message); - - $from->WebSocket->coalescedCallback->onMessage($from, $parsed); - } - - if (strlen($overflow) > 0) { - $this->onMessage($from, $overflow); - } - } - - /** - * @deprecated - * @return RFC6455\Message - */ - public function newMessage() { - return new Message; - } - - /** - * @deprecated - * @param string|null $payload - * @param bool|null $final - * @param int|null $opcode - * @return RFC6455\Frame - */ - public function newFrame($payload = null, $final = null, $opcode = null) { - return new Frame($payload, $final, $opcode); } /** @@ -203,41 +84,4 @@ class Negotiator implements NegotiatorInterface { public function sign($key) { return base64_encode(sha1($key . static::GUID, true)); } - - /** - * @deprecated - * Determine if a close code is valid - * @param int|string - * @return bool - */ - public function isValidCloseCode($val) { - if (array_key_exists($val, $this->closeCodes)) { - return true; - } - - if ($val >= 3000 && $val <= 4999) { - return true; - } - - return false; - } - - /** - * @deprecated - * Creates a private lookup of valid, private close codes - */ - protected function setCloseCodes() { - $this->closeCodes[Frame::CLOSE_NORMAL] = true; - $this->closeCodes[Frame::CLOSE_GOING_AWAY] = true; - $this->closeCodes[Frame::CLOSE_PROTOCOL] = true; - $this->closeCodes[Frame::CLOSE_BAD_DATA] = true; - //$this->closeCodes[Frame::CLOSE_NO_STATUS] = true; - //$this->closeCodes[Frame::CLOSE_ABNORMAL] = true; - $this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true; - $this->closeCodes[Frame::CLOSE_POLICY] = true; - $this->closeCodes[Frame::CLOSE_TOO_BIG] = true; - $this->closeCodes[Frame::CLOSE_MAND_EXT] = true; - $this->closeCodes[Frame::CLOSE_SRV_ERR] = true; - //$this->closeCodes[Frame::CLOSE_TLS] = true; - } } diff --git a/src/Handshake/NegotiatorInterface.php b/src/Handshake/NegotiatorInterface.php index 718d760..cb7cbaf 100644 --- a/src/Handshake/NegotiatorInterface.php +++ b/src/Handshake/NegotiatorInterface.php @@ -1,8 +1,8 @@ currentMessage)) { + $this->currentMessage = $this->newMessage(); + } + + // There is a frame fragment attached to the connection, add to it + if (!isset($this->currentFrame)) { + $this->currentFrame = $this->newFrame(); + } + + $frame = $this->currentFrame; + + $frame->addBuffer($data); + if ($frame->isCoalesced()) { + $opcode = $frame->getOpcode(); + if ($opcode > 2) { + switch ($opcode) { + case $frame::OP_CLOSE: + $closeCode = 0; + + $bin = $frame->getPayload(); + + if (empty($bin)) { + $this->emit('close', [null]); + return; + } + + if (strlen($bin) >= 2) { + list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2))); + } + + if (!$this->isValidCloseCode($closeCode)) { + $this->emit('close', [$frame::CLOSE_PROTOCOL]); + return; + } + + // todo: + //if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) { + // $this->emit('close', [$frame::CLOSE_BAD_PAYLOAD]); + // return; + //} + + $this->emit('close', [$closeCode]); + return; + break; + case $frame::OP_PING: + // this should probably be automatic + //$from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG)); + $this->emit('ping', [$frame]); + break; + case $frame::OP_PONG: + $this->emit('pong', [$frame]); + break; + default: + $this->emit('close', [$frame::CLOSE_PROTOCOL]); + return; + break; + } + + $overflow = $frame->extractOverflow(); + + unset($this->currentFrame, $frame, $opcode); + + if (strlen($overflow) > 0) { + $this->onData($overflow); + } + + return; + } + + $overflow = $frame->extractOverflow(); + + $this->currentMessage->addFrame($this->currentFrame); + unset($this->currentFrame); + } + + if ($this->currentMessage->isCoalesced()) { + $this->emit('message', [$this->currentMessage]); + //$parsed = $from->WebSocket->message->getPayload(); + unset($this->currentMessage); + } + + if (strlen($overflow) > 0) { + $this->onData($overflow); + } + } + + /** + * @return Message + */ + public function newMessage() { + return new Message; + } + + /** + * @param string|null $payload + * @param bool|null $final + * @param int|null $opcode + * @return Frame + */ + public function newFrame($payload = null, $final = null, $opcode = null) { + return new Frame($payload, $final, $opcode); + } + + /** + * Determine if a close code is valid + * @param int|string + * @return bool + */ + public function isValidCloseCode($val) { + if (array_key_exists($val, $this->closeCodes)) { + return true; + } + + if ($val >= 3000 && $val <= 4999) { + return true; + } + + return false; + } + + /** + * Creates a private lookup of valid, private close codes + */ + protected function setCloseCodes() { + $this->closeCodes[Frame::CLOSE_NORMAL] = true; + $this->closeCodes[Frame::CLOSE_GOING_AWAY] = true; + $this->closeCodes[Frame::CLOSE_PROTOCOL] = true; + $this->closeCodes[Frame::CLOSE_BAD_DATA] = true; + //$this->closeCodes[Frame::CLOSE_NO_STATUS] = true; + //$this->closeCodes[Frame::CLOSE_ABNORMAL] = true; + $this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true; + $this->closeCodes[Frame::CLOSE_POLICY] = true; + $this->closeCodes[Frame::CLOSE_TOO_BIG] = true; + $this->closeCodes[Frame::CLOSE_MAND_EXT] = true; + $this->closeCodes[Frame::CLOSE_SRV_ERR] = true; + //$this->closeCodes[Frame::CLOSE_TLS] = true; + } +} \ No newline at end of file