mirror of
https://github.com/nextcloud/server.git
synced 2026-06-11 01:30:50 -04:00
feat: enabled frankenphp worker mode
Signed-off-by: Carl Schwan <carlschwan@kde.org>
This commit is contained in:
parent
689196b1d3
commit
6fc03681bd
9 changed files with 148 additions and 105 deletions
|
|
@ -1,9 +1,10 @@
|
|||
localhost {
|
||||
php_server {
|
||||
|
||||
worker index.php
|
||||
}
|
||||
|
||||
log {
|
||||
level ERROR
|
||||
output stderr
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ function exceptionHandler($exception) {
|
|||
}
|
||||
try {
|
||||
require_once __DIR__ . '/lib/base.php';
|
||||
OC::init();
|
||||
|
||||
// set to run indefinitely if needed
|
||||
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
|
||||
|
|
|
|||
2
cron.php
2
cron.php
|
|
@ -17,6 +17,8 @@ require_once __DIR__ . '/lib/versioncheck.php';
|
|||
try {
|
||||
require_once __DIR__ . '/lib/base.php';
|
||||
|
||||
OC::init();
|
||||
|
||||
if (isset($argv[1]) && ($argv[1] === '-h' || $argv[1] === '--help')) {
|
||||
echo 'Description:
|
||||
Run the background job routine
|
||||
|
|
|
|||
169
index.php
169
index.php
|
|
@ -19,90 +19,113 @@ use OCP\Server;
|
|||
use OCP\Template\ITemplateManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
try {
|
||||
require_once __DIR__ . '/lib/base.php';
|
||||
require_once __DIR__ . '/lib/base.php';
|
||||
|
||||
OC::handleRequest();
|
||||
} catch (ServiceUnavailableException $ex) {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
|
||||
//show the user a detailed error page
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 503);
|
||||
} catch (HintException $ex) {
|
||||
$handler = static function () {
|
||||
try {
|
||||
Server::get(ITemplateManager::class)->printErrorPage($ex->getMessage(), $ex->getHint(), 503);
|
||||
} catch (Exception $ex2) {
|
||||
// In worker mode, script name is empty in FrankenPHP
|
||||
if ($_SERVER['SCRIPT_NAME'] === '') {
|
||||
$_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
|
||||
}
|
||||
OC::init();
|
||||
OC::handleRequest();
|
||||
} catch (ServiceUnavailableException $ex) {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
|
||||
//show the user a detailed error page
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 503);
|
||||
} catch (HintException $ex) {
|
||||
try {
|
||||
Server::get(ITemplateManager::class)->printErrorPage($ex->getMessage(), $ex->getHint(), 503);
|
||||
} catch (Exception $ex2) {
|
||||
try {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
Server::get(LoggerInterface::class)->error($ex2->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex2,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
// no way to log it properly - but to avoid a white page of death we try harder and ignore this one here
|
||||
}
|
||||
|
||||
//show the user a detailed error page
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
|
||||
}
|
||||
} catch (LoginException $ex) {
|
||||
$request = Server::get(IRequest::class);
|
||||
/**
|
||||
* Routes with the @CORS annotation and other API endpoints should
|
||||
* not return a webpage, so we only print the error page when html is accepted,
|
||||
* otherwise we reply with a JSON array like the SecurityMiddleware would do.
|
||||
*/
|
||||
if (stripos($request->getHeader('Accept'), 'html') === false) {
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['message' => $ex->getMessage()]);
|
||||
exit();
|
||||
}
|
||||
Server::get(ITemplateManager::class)->printErrorPage($ex->getMessage(), $ex->getMessage(), 401);
|
||||
} catch (MaxDelayReached $ex) {
|
||||
$request = Server::get(IRequest::class);
|
||||
/**
|
||||
* Routes with the @CORS annotation and other API endpoints should
|
||||
* not return a webpage, so we only print the error page when html is accepted,
|
||||
* otherwise we reply with a JSON array like the BruteForceMiddleware would do.
|
||||
*/
|
||||
if (stripos($request->getHeader('Accept'), 'html') === false) {
|
||||
http_response_code(429);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['message' => $ex->getMessage()]);
|
||||
exit();
|
||||
}
|
||||
http_response_code(429);
|
||||
Server::get(ITemplateManager::class)->printGuestPage('core', '429');
|
||||
} catch (Exception $ex) {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
|
||||
//show the user a detailed error page
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
|
||||
} catch (Error $ex) {
|
||||
try {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
Server::get(LoggerInterface::class)->error($ex2->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex2,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
// no way to log it properly - but to avoid a white page of death we try harder and ignore this one here
|
||||
}
|
||||
} catch (Error $e) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
print("Internal Server Error\n\n");
|
||||
print("The server encountered an internal error and was unable to complete your request.\n");
|
||||
print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
|
||||
print("More details can be found in the webserver log.\n");
|
||||
|
||||
//show the user a detailed error page
|
||||
throw $ex;
|
||||
}
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
|
||||
}
|
||||
} catch (LoginException $ex) {
|
||||
$request = Server::get(IRequest::class);
|
||||
/**
|
||||
* Routes with the @CORS annotation and other API endpoints should
|
||||
* not return a webpage, so we only print the error page when html is accepted,
|
||||
* otherwise we reply with a JSON array like the SecurityMiddleware would do.
|
||||
*/
|
||||
if (stripos($request->getHeader('Accept'), 'html') === false) {
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['message' => $ex->getMessage()]);
|
||||
exit();
|
||||
}
|
||||
Server::get(ITemplateManager::class)->printErrorPage($ex->getMessage(), $ex->getMessage(), 401);
|
||||
} catch (MaxDelayReached $ex) {
|
||||
$request = Server::get(IRequest::class);
|
||||
/**
|
||||
* Routes with the @CORS annotation and other API endpoints should
|
||||
* not return a webpage, so we only print the error page when html is accepted,
|
||||
* otherwise we reply with a JSON array like the BruteForceMiddleware would do.
|
||||
*/
|
||||
if (stripos($request->getHeader('Accept'), 'html') === false) {
|
||||
http_response_code(429);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['message' => $ex->getMessage()]);
|
||||
exit();
|
||||
}
|
||||
http_response_code(429);
|
||||
Server::get(ITemplateManager::class)->printGuestPage('core', '429');
|
||||
} catch (Exception $ex) {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
};
|
||||
|
||||
//show the user a detailed error page
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
|
||||
} catch (Error $ex) {
|
||||
try {
|
||||
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
|
||||
'app' => 'index',
|
||||
'exception' => $ex,
|
||||
]);
|
||||
} catch (Error $e) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
print("Internal Server Error\n\n");
|
||||
print("The server encountered an internal error and was unable to complete your request.\n");
|
||||
print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
|
||||
print("More details can be found in the webserver log.\n");
|
||||
if (function_exists('frankenphp_handle_request')) {
|
||||
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
|
||||
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
|
||||
$keepRunning = \frankenphp_handle_request($handler);
|
||||
|
||||
throw $ex;
|
||||
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
|
||||
gc_collect_cycles();
|
||||
|
||||
if (!$keepRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
|
||||
} else {
|
||||
$handler();
|
||||
}
|
||||
|
|
|
|||
73
lib/base.php
73
lib/base.php
|
|
@ -104,17 +104,6 @@ class OC {
|
|||
* the app path list is empty or contains an invalid path
|
||||
*/
|
||||
public static function initPaths(): void {
|
||||
if (defined('PHPUNIT_CONFIG_DIR')) {
|
||||
self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
|
||||
} elseif (defined('PHPUNIT_RUN') && PHPUNIT_RUN && is_dir(OC::$SERVERROOT . '/tests/config/')) {
|
||||
self::$configDir = OC::$SERVERROOT . '/tests/config/';
|
||||
} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
|
||||
self::$configDir = rtrim($dir, '/') . '/';
|
||||
} else {
|
||||
self::$configDir = OC::$SERVERROOT . '/config/';
|
||||
}
|
||||
self::$config = new \OC\Config(self::$configDir);
|
||||
|
||||
OC::$SUBURI = str_replace('\\', '/', substr(realpath($_SERVER['SCRIPT_FILENAME'] ?? ''), strlen(OC::$SERVERROOT)));
|
||||
/**
|
||||
* FIXME: The following lines are required because we can't yet instantiate
|
||||
|
|
@ -166,7 +155,7 @@ class OC {
|
|||
// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
|
||||
// slash which is required by URL generation.
|
||||
if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT
|
||||
&& substr($_SERVER['REQUEST_URI'], -1) !== '/') {
|
||||
&& substr($_SERVER['REQUEST_URI'], -1) !== '/') {
|
||||
header('Location: ' . \OC::$WEBROOT . '/');
|
||||
exit();
|
||||
}
|
||||
|
|
@ -186,6 +175,7 @@ class OC {
|
|||
OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
|
||||
}
|
||||
|
||||
|
||||
if (empty(OC::$APPSROOTS)) {
|
||||
throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
|
||||
. '. You can also configure the location in the config.php file.');
|
||||
|
|
@ -664,12 +654,7 @@ class OC {
|
|||
}
|
||||
}
|
||||
|
||||
public static function init(): void {
|
||||
// First handle PHP configuration and copy auth headers to the expected
|
||||
// $_SERVER variable before doing anything Server object related
|
||||
self::setRequiredIniValues();
|
||||
self::handleAuthHeaders();
|
||||
|
||||
public static function boot(): void {
|
||||
// prevent any XML processing from loading external entities
|
||||
libxml_set_external_entity_loader(static function () {
|
||||
return null;
|
||||
|
|
@ -692,14 +677,32 @@ class OC {
|
|||
self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
|
||||
self::$composerAutoloader->setApcuPrefix(null);
|
||||
|
||||
// setup 3rdparty autoloader
|
||||
$vendorAutoLoad = OC::$SERVERROOT . '/3rdparty/autoload.php';
|
||||
if (!file_exists($vendorAutoLoad)) {
|
||||
throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
|
||||
}
|
||||
require_once $vendorAutoLoad;
|
||||
|
||||
$loaderEnd = microtime(true);
|
||||
|
||||
// load configs
|
||||
if (defined('PHPUNIT_CONFIG_DIR')) {
|
||||
self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
|
||||
} elseif (defined('PHPUNIT_RUN') && PHPUNIT_RUN && is_dir(OC::$SERVERROOT . '/tests/config/')) {
|
||||
self::$configDir = OC::$SERVERROOT . '/tests/config/';
|
||||
} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
|
||||
self::$configDir = rtrim($dir, '/') . '/';
|
||||
} else {
|
||||
self::$configDir = OC::$SERVERROOT . '/config/';
|
||||
}
|
||||
self::$config = new \OC\Config(self::$configDir);
|
||||
|
||||
// Enable lazy loading if activated
|
||||
\OC\AppFramework\Utility\SimpleContainer::$useLazyObjects = (bool)self::$config->getValue('enable_lazy_objects', true);
|
||||
|
||||
try {
|
||||
self::initPaths();
|
||||
// setup 3rdparty autoloader
|
||||
$vendorAutoLoad = OC::$SERVERROOT . '/3rdparty/autoload.php';
|
||||
if (!file_exists($vendorAutoLoad)) {
|
||||
throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
|
||||
}
|
||||
require_once $vendorAutoLoad;
|
||||
} catch (\RuntimeException $e) {
|
||||
if (!self::$CLI) {
|
||||
http_response_code(503);
|
||||
|
|
@ -709,15 +712,24 @@ class OC {
|
|||
print($e->getMessage());
|
||||
exit();
|
||||
}
|
||||
$loaderEnd = microtime(true);
|
||||
|
||||
// Enable lazy loading if activated
|
||||
\OC\AppFramework\Utility\SimpleContainer::$useLazyObjects = (bool)self::$config->getValue('enable_lazy_objects', true);
|
||||
//$eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
|
||||
//$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
|
||||
//$eventLogger->start('init', 'Initialize');
|
||||
}
|
||||
|
||||
// setup the basic server
|
||||
public static function init(): void {
|
||||
// First handle PHP configuration and copy auth headers to the expected
|
||||
// $_SERVER variable before doing anything Server object related
|
||||
self::setRequiredIniValues();
|
||||
self::handleAuthHeaders();
|
||||
|
||||
// set up the basic server
|
||||
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
|
||||
self::$server->boot();
|
||||
|
||||
$loaderStart = microtime(true);
|
||||
|
||||
try {
|
||||
$profiler = new BuiltInProfiler(
|
||||
Server::get(IConfig::class),
|
||||
|
|
@ -733,8 +745,7 @@ class OC {
|
|||
}
|
||||
|
||||
$eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
|
||||
$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
|
||||
$eventLogger->start('boot', 'Initialize');
|
||||
$eventLogger->start('init', 'Initialize');
|
||||
|
||||
// Override php.ini and log everything if we're troubleshooting
|
||||
if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
|
||||
|
|
@ -1307,4 +1318,4 @@ class OC {
|
|||
}
|
||||
}
|
||||
|
||||
OC::init();
|
||||
OC::boot();
|
||||
|
|
|
|||
|
|
@ -280,6 +280,7 @@ class Router implements IRouter {
|
|||
$this->loadRoutes();
|
||||
}
|
||||
|
||||
|
||||
$this->eventLogger->start('route:url:match', 'Symfony url matcher call');
|
||||
$matcher = new UrlMatcher($this->root, $this->context);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use OCP\IRequest;
|
|||
use OCP\Server;
|
||||
|
||||
require_once __DIR__ . '/../lib/base.php';
|
||||
OC::init();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use Psr\Log\LoggerInterface;
|
|||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
|
||||
OC::init();
|
||||
$request = Server::get(IRequest::class);
|
||||
|
||||
if ((Util::needUpgrade() || Server::get(IConfig::class)->getSystemValueBool('maintenance')) && $request->getPathInfo() !== '/core/update') {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ function resolveService($service) {
|
|||
try {
|
||||
require_once __DIR__ . '/lib/base.php';
|
||||
|
||||
OC::init();
|
||||
|
||||
// All resources served via the DAV endpoint should have the strictest possible
|
||||
// policy. Exempted from this is the SabreDAV browser plugin which overwrites
|
||||
// this policy with a softer one if debug mode is enabled.
|
||||
|
|
|
|||
Loading…
Reference in a new issue