Skip to content

Commit aab07d3

Browse files
authored
Merge pull request #61115 from nextcloud/feat/extend-frankenphp-support
feat: Add frankenphp worker support for more endpoints
2 parents f165dc8 + ea512ea commit aab07d3

6 files changed

Lines changed: 292 additions & 223 deletions

File tree

‎Caddyfile‎

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,35 @@
33
#
44
# THIS IS AN EXPERIMENTAL FEATURE
55
# DO NOT USE THIS IN PRODUCTION, YOU HAVE BEEN WARNED.
6+
{
7+
metrics
8+
frankenphp {
9+
num_threads 192
10+
max_threads 256
11+
# max_requests 500
12+
}
13+
}
614

715
localhost {
816
php_server {
9-
worker {
17+
worker {
1018
file index.php
19+
num 32
1120
watch
21+
match /index.php/*
22+
}
23+
worker {
24+
file remote.php
25+
num 32
26+
watch
27+
match /remote.php/*
28+
}
29+
worker {
30+
file ocs/v1.php
31+
num 32
32+
watch
33+
match /ocs/v1.php/*
34+
match /ocs/v2.php/*
1235
}
1336
}
1437

@@ -17,42 +40,42 @@ localhost {
1740
output stderr
1841
}
1942

20-
encode gzip
43+
encode gzip
2144

22-
redir /.well-known/carddav /remote.php/dav 301
23-
redir /.well-known/caldav /remote.php/dav 301
45+
redir /.well-known/carddav /remote.php/dav 301
46+
redir /.well-known/caldav /remote.php/dav 301
2447

25-
# Rule: Maps most RFC 8615 compliant well-known URIs to our main frontend controller (/index.php) by default
26-
@wellKnown {
27-
path "/.well-known/"
28-
not {
29-
path /.well-known/acme-challenge
30-
path /.well-known/pki-validation
31-
}
32-
}
33-
rewrite @wellKnown /index.php
48+
# Rule: Maps most RFC 8615 compliant well-known URIs to our main frontend controller (/index.php) by default
49+
@wellKnown {
50+
path "/.well-known/"
51+
not {
52+
path /.well-known/acme-challenge
53+
path /.well-known/pki-validation
54+
}
55+
}
56+
rewrite @wellKnown /index.php
3457

35-
rewrite /ocm-provider/ /index.php
58+
rewrite /ocm-provider/ /index.php
3659

37-
@forbidden {
38-
path /.htaccess
39-
path /data/*
40-
path /config/*
41-
path /db_structure
42-
path /.xml
43-
path /README
44-
path /3rdparty/*
45-
path /lib/*
46-
path /templates/*
47-
path /occ
48-
path /build
49-
path /tests
50-
path /console.php
51-
path /autotest
52-
path /issue
53-
path /indi
54-
path /db_
55-
path /console
56-
}
57-
respond @forbidden 404
60+
@forbidden {
61+
path /.htaccess
62+
path /data/*
63+
path /config/*
64+
path /db_structure
65+
path /.xml
66+
path /README
67+
path /3rdparty/*
68+
path /lib/*
69+
path /templates/*
70+
path /occ
71+
path /build
72+
path /tests
73+
path /console.php
74+
path /autotest
75+
path /issue
76+
path /indi
77+
path /db_
78+
path /console
79+
}
80+
respond @forbidden 404
5881
}

‎index.php‎

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
* SPDX-License-Identifier: AGPL-3.0-only
99
*/
1010

11-
require_once __DIR__ . '/lib/versioncheck.php';
12-
13-
use OC\Files\Filesystem;
1411
use OC\ServiceUnavailableException;
1512
use OC\User\LoginException;
1613
use OCP\HintException;
@@ -20,27 +17,15 @@
2017
use OCP\Template\ITemplateManager;
2118
use Psr\Log\LoggerInterface;
2219

20+
require_once __DIR__ . '/lib/versioncheck.php';
2321
require_once __DIR__ . '/lib/OC.php';
2422

2523
\OC::boot();
2624

27-
function resetStaticProperties(): void {
28-
// FIXME needed because these use a static var
29-
\OC_Hook::clear();
30-
\OC_Util::$styles = [];
31-
\OC_Util::$headers = [];
32-
\OC_User::setIncognitoMode(false);
33-
\OC_User::$_setupedBackends = [];
34-
\OC_App::reset();
35-
\OC_Helper::reset();
36-
Filesystem::reset();
37-
}
38-
39-
$handler = static function (): void {
25+
\OC::handleRequests(static function (): void {
4026
try {
41-
resetStaticProperties();
42-
OC::init();
43-
OC::handleRequest();
27+
\OC::initForRequest();
28+
\OC::handleRequest();
4429
} catch (ServiceUnavailableException $ex) {
4530
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
4631
'app' => 'index',
@@ -124,20 +109,4 @@ function resetStaticProperties(): void {
124109
}
125110
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
126111
}
127-
};
128-
129-
if (function_exists('frankenphp_handle_request') && isset($_SERVER['FRANKENPHP_WORKER']) && $_SERVER['FRANKENPHP_WORKER'] === '1') {
130-
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
131-
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
132-
$keepRunning = \frankenphp_handle_request($handler);
133-
134-
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
135-
gc_collect_cycles();
136-
137-
if (!$keepRunning) {
138-
break;
139-
}
140-
}
141-
} else {
142-
$handler();
143-
}
112+
});

‎lib/OC.php‎

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* SPDX-License-Identifier: AGPL-3.0-only
88
*/
99

10+
use OC\Files\Filesystem;
1011
use OC\NavigationManager;
1112
use OC\Profiler\BuiltInProfiler;
1213
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
@@ -102,6 +103,11 @@ class OC {
102103
private static float $loaderStart;
103104
private static float $loaderEnd;
104105

106+
/**
107+
* @psalm-suppress ImpureStaticProperty
108+
*/
109+
private static bool $oneTimeChecksDone = false;
110+
105111
/**
106112
* @throws \RuntimeException when the 3rdparty directory is missing or
107113
* the app path list is empty or contains an invalid path
@@ -656,6 +662,9 @@ private static function addSecurityHeaders(): void {
656662
}
657663
}
658664

665+
/*
666+
* Called only once at the beginning to setup things
667+
*/
659668
public static function boot(): void {
660669
// prevent any XML processing from loading external entities
661670
libxml_set_external_entity_loader(static function () {
@@ -714,24 +723,67 @@ public static function boot(): void {
714723
print($e->getMessage());
715724
exit();
716725
}
726+
self::setRequiredIniValues();
727+
728+
// initialize intl fallback if necessary
729+
OC_Util::isSetLocaleWorking();
730+
}
731+
732+
/**
733+
* Run one time checks if not already run. This allows checking after server boot, to have access to translations and pretty error rendering while still checking only once in worker mode.
734+
*/
735+
private static function oneTimeChecks(): void {
736+
if (self::$oneTimeChecksDone) {
737+
return;
738+
}
739+
740+
// Check for PHP SimpleXML extension earlier since we need it before our other checks and want to provide a useful hint for web users
741+
// see https://github.com/nextcloud/server/pull/2619
742+
if (!function_exists('simplexml_load_file')) {
743+
throw new \OCP\HintException('The PHP SimpleXML/PHP-XML extension is not installed. Install the extension or make sure it is enabled.');
744+
}
745+
746+
// Check whether the sample configuration has been copied
747+
if (self::$config->getValue('copied_sample_config', false)) {
748+
$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
749+
Server::get(ITemplateManager::class)->printErrorPage(
750+
$l->t('Sample configuration detected'),
751+
$l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php'),
752+
503
753+
);
754+
return;
755+
}
756+
757+
self::checkConfig();
758+
759+
self::$oneTimeChecksDone = true;
717760
}
718761

719-
public static function init(): void {
762+
/*
763+
* Called before each request served if the same worker serves several request
764+
*/
765+
public static function initForRequest(): void {
766+
self::resetStaticProperties();
767+
720768
// First handle PHP configuration and copy auth headers to the expected
721769
// $_SERVER variable before doing anything Server object related
722-
self::setRequiredIniValues();
723770
self::handleAuthHeaders();
724771

725772
// setup the basic server
726773
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
727774
self::$server->boot();
728775

776+
self::oneTimeChecks();
777+
729778
$loaderStart = microtime(true);
730779

780+
$config = Server::get(IConfig::class);
781+
$request = Server::get(IRequest::class);
782+
731783
try {
732784
$profiler = new BuiltInProfiler(
733-
Server::get(IConfig::class),
734-
Server::get(IRequest::class),
785+
$config,
786+
$request,
735787
);
736788
$profiler->start();
737789
} catch (\Throwable $e) {
@@ -751,10 +803,6 @@ public static function init(): void {
751803
error_reporting(E_ALL);
752804
}
753805

754-
// initialize intl fallback if necessary
755-
OC_Util::isSetLocaleWorking();
756-
757-
$config = Server::get(IConfig::class);
758806
if (!defined('PHPUNIT_RUN')) {
759807
$errorHandler = new OC\Log\ErrorHandler(
760808
Server::get(\Psr\Log\LoggerInterface::class),
@@ -778,12 +826,6 @@ public static function init(): void {
778826

779827
$eventLogger->start('init_session', 'Initialize session');
780828

781-
// Check for PHP SimpleXML extension earlier since we need it before our other checks and want to provide a useful hint for web users
782-
// see https://github.com/nextcloud/server/pull/2619
783-
if (!function_exists('simplexml_load_file')) {
784-
throw new \OCP\HintException('The PHP SimpleXML/PHP-XML extension is not installed.', 'Install the extension or make sure it is enabled.');
785-
}
786-
787829
$systemConfig = Server::get(\OC\SystemConfig::class);
788830
$appManager = Server::get(\OCP\App\IAppManager::class);
789831
if ($systemConfig->getValue('installed', false)) {
@@ -793,7 +835,6 @@ public static function init(): void {
793835
self::initSession();
794836
}
795837
$eventLogger->end('init_session');
796-
self::checkConfig();
797838
self::checkInstalled($systemConfig);
798839

799840
if (!self::$CLI) {
@@ -887,26 +928,14 @@ public static function init(): void {
887928
$lockProvider = Server::get(\OCP\Lock\ILockingProvider::class);
888929
register_shutdown_function([$lockProvider, 'releaseAll']);
889930

890-
// Check whether the sample configuration has been copied
891-
if ($systemConfig->getValue('copied_sample_config', false)) {
892-
$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
893-
Server::get(ITemplateManager::class)->printErrorPage(
894-
$l->t('Sample configuration detected'),
895-
$l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php'),
896-
503
897-
);
898-
return;
899-
}
900-
901-
$request = Server::get(IRequest::class);
902931
$host = $request->getInsecureServerHost();
903932
/**
904933
* if the host passed in headers isn't trusted
905934
* FIXME: Should not be in here at all :see_no_evil:
906935
*/
907936
if (!OC::$CLI
908937
&& !Server::get(\OC\Security\TrustedDomainHelper::class)->isTrustedDomain($host)
909-
&& $config->getSystemValueBool('installed', false)
938+
&& $config->getSystemValueBool('installed')
910939
) {
911940
// Allow access to CSS resources
912941
$isScssRequest = false;
@@ -1332,4 +1361,40 @@ protected static function tryAppAPILogin(OCP\IRequest $request): bool {
13321361
return false;
13331362
}
13341363
}
1364+
1365+
/**
1366+
* @internal
1367+
*/
1368+
private static function resetStaticProperties(): void {
1369+
// FIXME needed because these use a static var
1370+
\OC_Hook::clear();
1371+
\OC_Util::$styles = [];
1372+
\OC_Util::$headers = [];
1373+
\OC_User::setIncognitoMode(false);
1374+
\OC_User::$_setupedBackends = [];
1375+
\OC_App::reset();
1376+
\OC_Helper::reset();
1377+
Filesystem::reset();
1378+
}
1379+
1380+
/**
1381+
* @internal
1382+
*/
1383+
public static function handleRequests(callable $handler): void {
1384+
if (function_exists('frankenphp_handle_request') && isset($_SERVER['FRANKENPHP_WORKER']) && $_SERVER['FRANKENPHP_WORKER'] === '1') {
1385+
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
1386+
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
1387+
$keepRunning = \frankenphp_handle_request($handler);
1388+
1389+
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
1390+
gc_collect_cycles();
1391+
1392+
if (!$keepRunning) {
1393+
break;
1394+
}
1395+
}
1396+
} else {
1397+
$handler();
1398+
}
1399+
}
13351400
}

‎lib/base.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
require_once __DIR__ . '/OC.php';
1010

1111
\OC::boot();
12-
\OC::init();
12+
\OC::initForRequest();

0 commit comments

Comments
 (0)