Handshake request listener
Closes the connection on handshake from an origin that isn't allowed

@param Connection $connection
@param string     $path
@param string     $origin
@param string     $key
@param array      $extensions
490395
public function onHandshakeRequest(
        Connection $connection,
        string $path,
        string $origin,
        string $key,
        array $extensions
    ): void {
        if (!$this->isAllowed($origin)) {
            $connection->close(Protocol::CLOSE_NORMAL, 'Not allowed origin during handshake request');
        }
    }
724163
public function onMessage($connection)
    {
        // REQUEST_URI.
        $workerman_url_info = parse_url('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
        if (!$workerman_url_info) {
            Http::header('HTTP/1.1 400 Bad Request');
            $connection->close('<h1>400 Bad Request</h1>');
            return;
        }

        $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/';

        $workerman_path_info      = pathinfo($workerman_path);
        $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : '';
        if ($workerman_file_extension === '') {
            $workerman_path           = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php';
            $workerman_file_extension = 'php';
        }

        $workerman_siteConfig = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot);
		$workerman_root_dir = $workerman_siteConfig['root'];
        $workerman_file = "$workerman_root_dir/$workerman_path";
		if(isset($workerman_siteConfig['additionHeader'])){
			Http::header($workerman_siteConfig['additionHeader']);
		}
        if ($workerman_file_extension === 'php' && !is_file($workerman_file)) {
            $workerman_file = "$workerman_root_dir/index.php";
            if (!is_file($workerman_file)) {
                $workerman_file           = "$workerman_root_dir/index.html";
                $workerman_file_extension = 'html';
            }
        }

        // File exsits.
        if (is_file($workerman_file)) {
            // Security check.
            if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath,
                    $workerman_root_dir_realpath)
            ) {
                Http::header('HTTP/1.1 400 Bad Request');
                $connection->close('<h1>400 Bad Request</h1>');
                return;
            }

            $workerman_file = realpath($workerman_file);

            // Request php file.
            if ($workerman_file_extension === 'php') {
                $workerman_cwd = getcwd();
                chdir($workerman_root_dir);
                ini_set('display_errors', 'off');
                ob_start();
                // Try to include php file.
                try {
                    // $_SERVER.
                    $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
                    $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
                    include $workerman_file;
                } catch (\Exception $e) {
                    // Jump_exit?
                    if ($e->getMessage() != 'jump_exit') {
                        Worker::safeEcho($e);
                    }
                }
                $content = ob_get_clean();
                ini_set('display_errors', 'on');
                if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") {
                    $connection->send($content);
                } else {
                    $connection->close($content);
                }
                chdir($workerman_cwd);
                return;
            }

            // Send file to client.
            return self::sendFile($connection, $workerman_file);
        } else {
            // 404
            Http::header("HTTP/1.1 404 Not Found");
			if(isset($workerman_siteConfig['custom404']) && file_exists($workerman_siteConfig['custom404'])){
				$html404 = file_get_contents($workerman_siteConfig['custom404']);
			}else{
				$html404 = '<html><head><title>404 File not found</title></head><body><center><h3>404 Not Found</h3></center></body></html>';
			}
            $connection->close($html404);
            return;
        }
    }
1267524
public function onClose($server, $serverSocket, $id)
    {
        if (isset($this->connections[$id])) {
            $this->fireCloseEvent(
                $this->connections[$id]['handler'],
                $this->connections[$id]['connection']
            );

            unset($this->connections[$id]);
        }
    }
499497
public function close(Server $server, int $fd)
    {
        try {
            if (!$path = WebSocketContext::getMeta('path', $fd)) {
                throw new ContextLostException(
                    "The connection info has lost of the fd#$fd, on connection closed"
                );
            }

            $className = $this->getHandler($path)[0];

            /** @var HandlerInterface $handler */
            $handler = \bean($className);

            if (\method_exists($handler, 'onClose')) {
                $handler->onClose($server, $fd);
            }
        } catch (\Throwable $e) {
            App::error($e->getMessage(), ['fd' => $fd]);
        }
    }
943608
public void evictConnection(Connection connection)
   {
      ProxyConnection proxyConnection = (ProxyConnection) connection;
      proxyConnection.cancelLeakTask();

      try {
         softEvictConnection(proxyConnection.getPoolEntry(), "(connection evicted by user)", !connection.isClosed() /* owner */);
      }
      catch (SQLException e) {
         // unreachable in HikariCP, but we're still forced to catch it
      }
   }
365407
protected function createRedirectUrisTable(Connection $connection, DatabaseSchemaInterface $schema): void
    {
        $manager = $connection->getSchemaManager();

        $table = new Table($schema->getRedirectUrisTable());
        $table->addColumn($schema->getRedirectUrisIdentityColumn(), Type::INTEGER)
            ->setNotnull(true)->setAutoincrement(true)->setUnsigned(true);
        $table->addColumn($schema->getRedirectUrisClientIdentityColumn(), Type::STRING)->setNotnull(true);
        $table->addColumn($schema->getRedirectUrisValueColumn(), Type::STRING)->setNotnull(true);
        $table->addColumn($schema->getRedirectUrisCreatedAtColumn(), Type::DATETIME)->setNotnull(true);
        $table->addColumn($schema->getRedirectUrisUpdatedAtColumn(), Type::DATETIME)->setNotnull(false);
        $table->setPrimaryKey([$schema->getRedirectUrisIdentityColumn()]);

        $table->addForeignKeyConstraint(
            $schema->getClientsTable(),
            [$schema->getRedirectUrisClientIdentityColumn()],
            [$schema->getClientsIdentityColumn()],
            $this->getOnDeleteCascadeConstraint()
        );

        $manager->dropAndCreateTable($table);
    }
442446
protected function createClientsTable(Connection $connection, DatabaseSchemaInterface $schema): void
    {
        $manager = $connection->getSchemaManager();

        $table = new Table($schema->getClientsTable());
        $table->addColumn($schema->getClientsIdentityColumn(), Type::STRING)->setNotnull(true);
        $table->addColumn($schema->getClientsNameColumn(), Type::STRING)->setNotnull(true);
        $table->addColumn($schema->getClientsDescriptionColumn(), Type::STRING)->setNotnull(false);
        $table->addColumn($schema->getClientsCredentialsColumn(), Type::STRING)->setNotnull(false);
        $table->addColumn($schema->getClientsIsConfidentialColumn(), Type::BOOLEAN)->setDefault(true);
        $table->addColumn($schema->getClientsIsScopeExcessAllowedColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsIsUseDefaultScopeColumn(), Type::BOOLEAN)->setDefault(true);
        $table->addColumn($schema->getClientsIsCodeGrantEnabledColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsIsImplicitGrantEnabledColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsIsPasswordGrantEnabledColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsIsClientGrantEnabledColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsIsRefreshGrantEnabledColumn(), Type::BOOLEAN)->setDefault(false);
        $table->addColumn($schema->getClientsCreatedAtColumn(), Type::DATETIME)->setNotnull(true);
        $table->addColumn($schema->getClientsUpdatedAtColumn(), Type::DATETIME)->setNotnull(false);
        $table->setPrimaryKey([$schema->getClientsIdentityColumn()]);
        $manager->dropAndCreateTable($table);
    }
1341350
private function createPassportView(Connection $connection, DatabaseSchemaInterface $schema): void
    {
        $tokensView   = $schema->getTokensView();
        $view         = $schema->getPassportView();
        $users        = $schema->getUsersTable();
        $tokensUserFk = $schema->getTokensUserIdentityColumn();

        $sql = <<< EOT
CREATE OR REPLACE VIEW {$view} AS
    SELECT *
    FROM $tokensView
      LEFT JOIN $users USING ($tokensUserFk);
EOT;
        $connection->exec($sql);
    }
40916
protected function checkSlave(Connection $connection)
    {
        $paused = 0;

        while (($lag = $this->getLag($connection)) > $this->options['max_lag']) {
            $this->paused = true;
            $for = $this->options['pause_lag'];

            $this->logger->notice('Chunk detected lag of {lag} on {database}, pausing for {for}', [
                'lag'      => $lag,
                'database' => sprintf('%s/%s', $connection->getHost(), $connection->getDatabase()),
                'for'      => $for
            ]);

            usleep($for);
            $paused += $for;

            if ($paused > $this->options['max_pause_lag']) {
                if ($this->options['continue_lag']) {
                    break;
                } else {
                    throw new RuntimeException('Slave lag did not recover after processing chunk. Aborting.');
                }
            }
        }
    }
415837
private synchronized void connectionOpened(String protocol, 
                                               String extensionsHeader) {
        // ### TODO: Currently, the Gateway is not sending the negotiated
        //           protocol.
        setNegotiatedProtocol(protocol);
        
        // Parse the negotiated extensions and parameters. This can result in 
        // _exception to be setup indicating that there is something wrong
        // while parsing the negotiated extensions and parameters.
        setNegotiatedExtensions(extensionsHeader);
        
        if ((getException() == null) && (_readyState == ReadyState.CONNECTING)) {
            _readyState = ReadyState.OPEN;
        }
        else {
            // The exception can be caused either while parsing the negotiated
            // extensions and parameters or the expiry of the connection timeout.
            // The parsing of negotiated extension can cause an exception if -- 
            // 1) a negotiated extension is not an enabled extension or
            // 2) the type of a negotiated parameter is not String.
            _readyState = ReadyState.CLOSED;
            
            // Inform the Gateway to close the WebSocket.
            _handler.processClose(_channel, 0, null);
        }

        // Unblock the connect() call so that it can proceed.
        notifyAll();
    }
1294344
public function onWorkerConnect($connection)
    {
        $connection->maxSendBufferSize = $this->sendToWorkerBufferSize;
        $connection->authorized = $this->secretKey ? false : true;
    }
808288
protected function createTokensScopesTable(Connection $connection, DatabaseSchemaInterface $schema): void
    {
        $manager = $connection->getSchemaManager();

        $table = new Table($schema->getTokensScopesTable());
        $table->addColumn($schema->getTokensScopesIdentityColumn(), Type::INTEGER)
            ->setNotnull(true)->setAutoincrement(true)->setUnsigned(true);
        $table->addColumn($schema->getTokensScopesTokenIdentityColumn(), Type::INTEGER)->setNotnull(true)
            ->setUnsigned(true);
        $table->addColumn($schema->getTokensScopesScopeIdentityColumn(), Type::STRING)->setNotnull(true);
        $table->setPrimaryKey([$schema->getTokensScopesIdentityColumn()]);
        $table->addUniqueIndex([
            $schema->getTokensScopesTokenIdentityColumn(),
            $schema->getTokensScopesScopeIdentityColumn()
        ]);

        $table->addForeignKeyConstraint(
            $schema->getTokensTable(),
            [$schema->getTokensScopesTokenIdentityColumn()],
            [$schema->getTokensIdentityColumn()],
            $this->getOnDeleteCascadeConstraint()
        );

        $table->addForeignKeyConstraint(
            $schema->getScopesTable(),
            [$schema->getTokensScopesScopeIdentityColumn()],
            [$schema->getScopesIdentityColumn()],
            $this->getOnDeleteCascadeConstraint()
        );

        $manager->dropAndCreateTable($table);
    }
1323701
protected function delete_many_connection(Memcached $connection, array $keys) {
        $count = 0;
        if ($this->candeletemulti) {
            // We can use deleteMulti, this is a bit faster yay!
            $result = $connection->deleteMulti($keys);
            foreach ($result as $key => $outcome) {
                if ($outcome === true) {
                    $count++;
                }
            }
            return $count;
        }

        // They are running an older version of the php memcached extension.
        foreach ($keys as $key) {
            if ($connection->delete($key)) {
                $count++;
            }
        }
        return $count;
    }
615157
public function connect($host, $port, Connection $connection, $timeout = 5)
    {
        $deferred = new Deferred();
        $this->disconnect()
            ->then(function () use ($deferred, $host, $port, $connection, $timeout) {
                $this->establishConnection($host, $port, $connection, $timeout)
                    ->then(function () use ($deferred) {
                        $deferred->resolve($this);
                    })
                    ->otherwise(function (\Exception $error) use ($deferred) {
                        $deferred->reject($error);
                    });
            })
            ->otherwise(function () use ($deferred) {
                $deferred->reject($this);
            });

        return $deferred->promise();
    }
1094213
public static function sendHandshake($connection)
    {
        if (!empty($connection->handshakeStep)) {
            return;
        }
        // Get Host.
        $port = $connection->getRemotePort();
        $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
        // Handshake header.
        $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n".
        "Host: $host\r\n".
        "Connection: Upgrade\r\n".
        "Upgrade: websocket\r\n".
        "Origin: ". (isset($connection->websocketOrigin) ? $connection->websocketOrigin : '*') ."\r\n".
        "Sec-WebSocket-Version: 13\r\n".
        "Sec-WebSocket-Key: ".base64_encode(sha1(uniqid(mt_rand(), true), true))."\r\n\r\n";
        $connection->send($header, true);
        $connection->handshakeStep               = 1;
        $connection->websocketCurrentFrameLength = 0;
        $connection->websocketDataBuffer         = '';
        $connection->tmpWebsocketData            = '';
    }
411703
public static function connect(ConnectionConfig $connectionConfig, CancellationToken $token = null): Promise
    {
        // @codeCoverageIgnoreStart
        if (Loop::get()->getHandle() instanceof \EvLoop) {
            throw new \Error('ext-pgsql is not compatible with pecl-ev; use pecl-pq or a different loop extension');
        } // @codeCoverageIgnoreEnd

        $connectionString = \str_replace(";", " ", $connectionConfig->getConnectionString());

        if (!$connection = @\pg_connect($connectionString, \PGSQL_CONNECT_ASYNC | \PGSQL_CONNECT_FORCE_NEW)) {
            return new Failure(new ConnectionException("Failed to create connection resource"));
        }

        if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
            return new Failure(new ConnectionException(\pg_last_error($connection)));
        }

        if (!$socket = \pg_socket($connection)) {
            return new Failure(new ConnectionException("Failed to access connection socket"));
        }

        $deferred = new Deferred;

        $callback = function ($watcher, $resource) use ($connection, $deferred) {
            switch (\pg_connect_poll($connection)) {
                case \PGSQL_POLLING_READING:
                    return; // Connection not ready, poll again.

                case \PGSQL_POLLING_WRITING:
                    return; // Still writing...

                case \PGSQL_POLLING_FAILED:
                    $deferred->fail(new ConnectionException(\pg_last_error($connection)));
                    return;

                case \PGSQL_POLLING_OK:
                    $deferred->resolve(new self($connection, $resource));
                    return;
            }
        };

        $poll = Loop::onReadable($socket, $callback);
        $await = Loop::onWritable($socket, $callback);

        $promise = $deferred->promise();

        $token = $token ?? new NullCancellationToken();
        $id = $token->subscribe([$deferred, "fail"]);

        $promise->onResolve(function ($exception) use ($connection, $poll, $await, $id, $token) {
            if ($exception) {
                \pg_close($connection);
            }

            $token->unsubscribe($id);
            Loop::cancel($poll);
            Loop::cancel($await);
        });

        return $promise;
    }
811814
public static function syncConfig(Connection $connection, array $configs, \Closure $callback)
    {
        foreach ($configs as $row) {
            $config = $connection->fetchAssoc('SELECT id, name FROM fusio_config WHERE name = :name', [
                'name' => $row['name']
            ]);

            if (empty($config)) {
                self::insertRow('fusio_config', $row, $callback);
            }
        }
    }