isDev()) { ini_set('display_errors', 'On'); } else { ini_set('display_errors', 'Off'); } } private static function loadAppConfigFile(): void { // Load env configuration $dotenv = \Dotenv\Dotenv::createImmutable(__DIR__); $dotenv->load(); // default Config storage driver is swoole_table, but this has a limitation // of 2048 string length. Our Database config hits this threshold // Due to this limitation we use SplArray storage. // Must also obtain the original config configuration item and load it into the new configuration item. $config = Config::getInstance()->getConf(); Config::getInstance()->storageHandler(new SplArrayConfig())->load($config); // Load all configuration files $files = File::scanDirectory(EASYSWOOLE_ROOT . '/App/Config'); if (is_array($files) && isset($files['files'])) { foreach ($files['files'] as $file) { $fileNameArr = explode('.', $file); $fileSuffix = end($fileNameArr); if ($fileSuffix == 'php') { Config::getInstance()->loadFile($file); } } } } // Register connection pool for Redis, MySQL, Memcache private static function registerPool(): void { $poolManager = Manager::getInstance(); // Register mysql database connection pool // UF #2073: Pass in a connection pool configuration object $poolConf = new \EasySwoole\Pool\Config( Config::getInstance()->getConf('pool') ); $databaseList = Config::getInstance()->getConf('database'); foreach( $databaseList as $dbGroup => $dbConfig ) { $mysqlConfig = new \EasySwoole\Mysqli\Config( $dbConfig ); $poolManager->register(new MysqlPool($poolConf, $mysqlConfig), $dbGroup.'_db'); } // Register redis connection pool $redisConfig = new \EasySwoole\Redis\Config\RedisConfig( Config::getInstance()->getConf('redis') ); $redisPoolConfig = RedisPool::getInstance()->register($redisConfig); $redisPoolConfig->setMaxObjectNum( Config::getInstance()->getConf('redis.pool.maxObjectNum') ); $redisPoolConfig->setMinObjectNum( Config::getInstance()->getConf('redis.pool.minObjectNum') ); $redisPoolConfig->setGetObjectTimeout( Config::getInstance()->getConf('redis.pool.getObjectTimeout') ); $redisPoolConfig->setIntervalCheckTime( Config::getInstance()->getConf('redis.pool.intervalCheckTime') ); $redisPoolConfig->setMaxIdleTime( Config::getInstance()->getConf('redis.pool.maxIdleTime') ); // Register memcache connection pool $poolConf = new \EasySwoole\Pool\Config(); $memcacheConfig = new \EasySwoole\Memcache\Config( Config::getInstance()->getConf('memcache.server') ); $poolManager->register(new MemcachePool($poolConf, $memcacheConfig), 'memcache'); } // Pre-create a connection for Redis, MySQL Pool private static function preLoadPool() { // Keep the minimum connection //$databaseList = Config::getInstance()->getConf('database'); //foreach( $databaseList as $dbGroup => $dbConfig ) //{ // $minObjectNum = Config::getInstance()->getConf( 'database.' . $dbGroup . '.pool.minObjectNum' ); // if( $minObjectNum > 0 ) $poolManager->get($dbGroup.'_db')->keepMin( $minObjectNum ); //} } // Register custom process to periodically remove blocked ip protected static function registerIpLimitProcess() { if( Config::getInstance()->getConf('app.iplimit_open') ) { // Enable IP current limit IpLimit::getInstance(); $class = new class('IpAccessCount') extends \EasySwoole\Component\Process\AbstractProcess{ protected function run($arg){ $this->addTick(5*1000, function (){ IpLimit::getInstance()->clear(); }); } }; ServerManager::getInstance()->getSwooleServer()->addProcess(($class)->getProcess()); } } // Enable IP current limit interception function protected static function registerIpLimit(Request $request) { $ipLimitMaxNumber = Config::getInstance()->getConf('app.iplimit_https_max_number'); if( Config::getInstance()->getConf('app.iplimit_open') && $ipLimitMaxNumber ) { $fd = $request->getSwooleRequest()->fd; $ip = Utils::getClientIp($request); // Intercept if the access frequency of the current period has exceeded the set value if (IpLimit::getInstance()->access($ip, 'https') > $ipLimitMaxNumber) { // Ignore these limits for internal network // Our guestapp queries the API frequently if( FALSE !== strpos( $ip, '10.0.') ) { return true; } // Directly forcibly close the connection Utils::requestLogError($request, "Forcibly closed the connection due to IP limit"); ServerManager::getInstance()->getSwooleServer()->close($fd); return false; } } return true; } public static function initialize() { // Set error display level self::setErrorReporting(); // Load the configuration file in the Config folder self::loadAppConfigFile(); // Set time zone date_default_timezone_set( getenv('APP_TIMEZONE') ); // Register connection pool self::registerPool(); } public static function mainServerCreate(EventRegister $register) { $server = ServerManager::getInstance()->getSwooleServer(); //$server->addProcess((new HotReload('HotReload', ['disableInotify' => false]))->getProcess()); /** * **************** Websocket controller ********************** */ // Register service event $register->add(EventRegister::onClose, [WebSocketEvents::class, 'onClose']); // Custom handshake event $websocketEvent = new WebSocketEvents(); $register->set(EventRegister::onHandShake, function (\swoole_http_request $request, \swoole_http_response $response) use ($websocketEvent) { $websocketEvent->onHandShake($request, $response); }); // Processed when a user message is received // Create a Dispatcher configuration $conf = new \EasySwoole\Socket\Config(); // Set Dispatcher to WebSocket mode $conf->setType($conf::WEB_SOCKET); // Set the parser object $conf->setParser(new WebSocketParser()); // Create a Dispatcher object and inject the config object $dispatch = new Dispatcher($conf); // Register the relevant event for the server In the WebSocket mode, the on message event must be registered and passed to the Dispatcher object for processing. $register->set(EventRegister::onMessage, function (\swoole_websocket_server $server, \swoole_websocket_frame $frame) use ($dispatch) { $dispatch->dispatch($server, $frame->data, $frame); }); // Cache service Cache::getInstance()->setTempDir(EASYSWOOLE_TEMP_DIR)->attachToServer( $server ); $register->add(EventRegister::onWorkerStart, function (\swoole_server $server, int $workerId) { // Pre-create the connection pool object to avoid the sudden large number of requests at startup, causing the connection to fail to create and fail if ($server->taskworker == false) { self::preLoadPool(); } }); MemcacheLibrary::getInstance(); // Intercept IP high access flow self::registerIpLimitProcess(); } public static function onRequest(Request $request, Response $response): bool { $request->withAttribute('remote_ip', Utils::getClientIp($request)); // Set access allow origin if the request origin is in allowed list $possibleOrigins = json_decode(getenv('POSSIBLE_ORIGINS'), TRUE); if(Core::getInstance()->isDev()) { $possibleOrigins[] = 'localhost'; } // Get the request origin $headerOrigin = $request->getHeader('origin'); $requestOrigin = !empty($headerOrigin) ? $headerOrigin[0] : ''; $requestOriginHost = parse_url($requestOrigin, PHP_URL_HOST); // Set allow origin if allowed if( in_array($requestOriginHost, $possibleOrigins)) { $response->withHeader('Access-Control-Allow-Origin', $requestOrigin); } else if( !empty($possibleOrigins) ) { // Default allow origin is the first domain $response->withHeader('Access-Control-Allow-Origin', "https://{$possibleOrigins[0]}"); } $response->withHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE, PATCH'); $response->withHeader('Access-Control-Allow-Credentials', 'true'); $response->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); if ($request->getMethod() === 'OPTIONS') { $response->withStatus(Status::CODE_OK); return false; } if(Config::getInstance()->getConf('ACCESS_CONTROL_ALLOW_ORIGIN')) { $response->withAddedHeader('Access-Control-Allow-Origin', '*'); } $request->withAttribute('request_method', $request->getSwooleRequest()->server['request_method'] ?? ''); $request->withAttribute('request_uri', $request->getSwooleRequest()->server['request_uri'] ?? ''); $request->withAttribute('query_string', $request->getSwooleRequest()->server['query_string'] ?? ''); $request->withAttribute('xrequest_uri', $request->getHeaderLine('x-request-uri')); $waSessionCookie = $request->getCookieParams(Config::getInstance()->getConf('app.wa_session_cookie_name')); $waSessionCookie = empty($waSessionCookie) ? "empty-session" : $waSessionCookie; $userAgent = $request->getHeaderLine('user-agent'); $referer = $request->getHeaderLine('referer'); Utils::requestLogInfo($request, "Request received :: {$waSessionCookie} :: {$referer} :: $userAgent"); //IP interceptor return self::registerIpLimit($request); } public static function afterRequest(Request $request, Response $response): void { // TODO: Implement afterAction() method. $requestEndTime = microtime(true); $requestStartTime = $request->getSwooleRequest()->server['request_time_float']; $responseTime = round($requestEndTime - $requestStartTime, 3); $responseStatus = $response->getStatusCode(); $responseBody = ''; if ($responseStatus != 200 || Config::getInstance()->getConf('LOG_OPTIONS.response_body')) { $responseBody = "\nResponseBody :: " . $response->getBody()->__toString(); } Utils::requestLogInfo($request, "Response status[$responseStatus] time[{$responseTime}s]{$responseBody}\n"); } }