public function handshake($data)
    {
        try {
            list($path, $origin, $key, $extensions, $protocol, $headers, $params)
                = $this->protocol->validateRequestHandshake($data);

            $this->headers = $headers;
            $this->queryParams = $params;

            $this->application = $this->manager->getApplicationForPath($path);
            if (!$this->application) {
                throw new BadRequestException('Invalid application');
            }

            $this->manager->getServer()->notify(
                Server::EVENT_HANDSHAKE_REQUEST,
                [$this, $path, $origin, $key, $extensions]
            );

            $response = $this->protocol->getResponseHandshake($key);

            if (!$this->socket->isConnected()) {
                throw new HandshakeException('Socket is not connected');
            }

            if ($this->socket->send($response) === null) {
                throw new HandshakeException('Could not send handshake response');
            }

            $this->handshaked = true;

            $this->logger->info(sprintf(
                'Handshake successful: %s:%d (%s) connected to %s',
                $this->getIp(),
                $this->getPort(),
                $this->getId(),
                $path
            ));

            $this->manager->getServer()->notify(
                Server::EVENT_HANDSHAKE_SUCCESSFUL,
                [$this]
            );

            if (method_exists($this->application, 'onConnect')) {
                $this->application->onConnect($this);
            }
        } catch (WrenchException $e) {
            $this->logger->error('Handshake failed: {exception}', [
                'exception' => $e,
            ]);
            $this->close(Protocol::CLOSE_PROTOCOL_ERROR, (string)$e);
            throw $e;
        }
    }