Skip to content

Commit d5c7e5e

Browse files
committed
feat: Add frankenphp worker support for more endpoints
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
1 parent d1fbf3d commit d5c7e5e

5 files changed

Lines changed: 191 additions & 143 deletions

File tree

Caddyfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +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 {
917
worker {
1018
file index.php
19+
num 32
20+
watch
21+
}
22+
worker {
23+
file remote.php
24+
num 32
25+
watch
26+
}
27+
worker {
28+
file ocs/v1.php
29+
num 32
30+
watch
31+
}
32+
worker {
33+
file ocs/v2.php
34+
num 32
1135
watch
1236
}
1337
}

index.php

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
require_once __DIR__ . '/lib/versioncheck.php';
1212

13-
use OC\Files\Filesystem;
1413
use OC\ServiceUnavailableException;
1514
use OC\User\LoginException;
1615
use OCP\HintException;
@@ -24,23 +23,11 @@
2423

2524
\OC::boot();
2625

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 () {
26+
\OC::handleRequests(static function () {
4027
try {
41-
resetStaticProperties();
42-
OC::init();
43-
OC::handleRequest();
28+
\OC::resetStaticProperties();
29+
\OC::init();
30+
\OC::handleRequest();
4431
} catch (ServiceUnavailableException $ex) {
4532
Server::get(LoggerInterface::class)->error($ex->getMessage(), [
4633
'app' => 'index',
@@ -124,20 +111,4 @@ function resetStaticProperties(): void {
124111
}
125112
Server::get(ITemplateManager::class)->printExceptionErrorPage($ex, 500);
126113
}
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-
}
114+
});

lib/OC.php

Lines changed: 37 additions & 0 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\Profiler\BuiltInProfiler;
1112
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
1213
use OC\Share20\GroupDeletedListener;
@@ -1324,4 +1325,40 @@ protected static function tryAppAPILogin(OCP\IRequest $request): bool {
13241325
return false;
13251326
}
13261327
}
1328+
1329+
/**
1330+
* @internal
1331+
*/
1332+
public static function resetStaticProperties(): void {
1333+
// FIXME needed because these use a static var
1334+
\OC_Hook::clear();
1335+
\OC_Util::$styles = [];
1336+
\OC_Util::$headers = [];
1337+
\OC_User::setIncognitoMode(false);
1338+
\OC_User::$_setupedBackends = [];
1339+
\OC_App::reset();
1340+
\OC_Helper::reset();
1341+
Filesystem::reset();
1342+
}
1343+
1344+
/**
1345+
* @internal
1346+
*/
1347+
public static function handleRequests(callable $handler): void {
1348+
if (function_exists('frankenphp_handle_request') && isset($_SERVER['FRANKENPHP_WORKER']) && $_SERVER['FRANKENPHP_WORKER'] === '1') {
1349+
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
1350+
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
1351+
$keepRunning = \frankenphp_handle_request($handler);
1352+
1353+
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
1354+
gc_collect_cycles();
1355+
1356+
if (!$keepRunning) {
1357+
break;
1358+
}
1359+
}
1360+
} else {
1361+
$handler();
1362+
}
1363+
}
13271364
}

ocs/v1.php

Lines changed: 57 additions & 51 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-
require_once __DIR__ . '/../lib/base.php';
13-
1411
use OC\OCS\ApiHelper;
1512
use OC\Route\Router;
1613
use OC\SystemConfig;
@@ -28,60 +25,69 @@
2825
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
2926
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
3027

31-
$request = Server::get(IRequest::class);
28+
require_once __DIR__ . '/../lib/versioncheck.php';
29+
require_once __DIR__ . '/../lib/OC.php';
3230

33-
if ((Util::needUpgrade() || Server::get(IConfig::class)->getSystemValueBool('maintenance')) && $request->getPathInfo() !== '/core/update') {
34-
// since the behavior of apps or remotes are unpredictable during
35-
// an upgrade, return a 503 directly
36-
ApiHelper::respond(503, 'Service unavailable', ['X-Nextcloud-Maintenance-Mode' => '1'], 503);
37-
exit;
38-
}
31+
\OC::boot();
3932

40-
/*
41-
* Try the appframework routes
42-
*/
43-
try {
44-
$appManager = Server::get(IAppManager::class);
45-
$appManager->loadApps(['session']);
46-
$appManager->loadApps(['authentication']);
47-
$appManager->loadApps(['extended_authentication']);
33+
\OC::handleRequests(static function () {
34+
\OC::resetStaticProperties();
35+
\OC::init();
36+
$request = Server::get(IRequest::class);
37+
38+
if ((Util::needUpgrade() || Server::get(IConfig::class)->getSystemValueBool('maintenance')) && $request->getPathInfo() !== '/core/update') {
39+
// since the behavior of apps or remotes are unpredictable during
40+
// an upgrade, return a 503 directly
41+
ApiHelper::respond(503, 'Service unavailable', ['X-Nextcloud-Maintenance-Mode' => '1'], 503);
42+
exit;
43+
}
44+
45+
/*
46+
* Try the appframework routes
47+
*/
48+
try {
49+
$appManager = Server::get(IAppManager::class);
50+
$appManager->loadApps(['session']);
51+
$appManager->loadApps(['authentication']);
52+
$appManager->loadApps(['extended_authentication']);
4853

49-
$request->throwDecodingExceptionIfAny();
54+
$request->throwDecodingExceptionIfAny();
5055

51-
if ($request->getPathInfo() !== '/core/update') {
52-
// load all apps to get all api routes properly setup
53-
// FIXME: this should ideally appear after handleLogin but will cause
54-
// side effects in existing apps
55-
$appManager->loadApps();
56-
if (!Server::get(IUserSession::class)->isLoggedIn()) {
57-
OC::handleLogin($request);
56+
if ($request->getPathInfo() !== '/core/update') {
57+
// load all apps to get all api routes properly setup
58+
// FIXME: this should ideally appear after handleLogin but will cause
59+
// side effects in existing apps
60+
$appManager->loadApps();
61+
if (!Server::get(IUserSession::class)->isLoggedIn()) {
62+
OC::handleLogin($request);
63+
}
64+
} else {
65+
$appManager->loadApps(['core']);
5866
}
59-
} else {
60-
$appManager->loadApps(['core']);
61-
}
6267

63-
Server::get(Router::class)->match('/ocsapp' . $request->getRawPathInfo());
64-
} catch (MaxDelayReached $ex) {
65-
ApiHelper::respond(Http::STATUS_TOO_MANY_REQUESTS, $ex->getMessage());
66-
} catch (ResourceNotFoundException $e) {
67-
$txt = 'Invalid query, please check the syntax. API specifications are here:'
68-
. ' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services.' . "\n";
69-
ApiHelper::respond(OCSController::RESPOND_NOT_FOUND, $txt);
70-
} catch (MethodNotAllowedException $e) {
71-
ApiHelper::setContentType();
72-
http_response_code(405);
73-
} catch (LoginException $e) {
74-
ApiHelper::respond(OCSController::RESPOND_UNAUTHORISED, 'Unauthorised');
75-
} catch (\Exception $e) {
76-
Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
68+
Server::get(Router::class)->match('/ocsapp' . $request->getRawPathInfo());
69+
} catch (MaxDelayReached $ex) {
70+
ApiHelper::respond(Http::STATUS_TOO_MANY_REQUESTS, $ex->getMessage());
71+
} catch (ResourceNotFoundException $e) {
72+
$txt = 'Invalid query, please check the syntax. API specifications are here:'
73+
. ' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services.' . "\n";
74+
ApiHelper::respond(OCSController::RESPOND_NOT_FOUND, $txt);
75+
} catch (MethodNotAllowedException $e) {
76+
ApiHelper::setContentType();
77+
http_response_code(405);
78+
} catch (LoginException $e) {
79+
ApiHelper::respond(OCSController::RESPOND_UNAUTHORISED, 'Unauthorised');
80+
} catch (\Exception $e) {
81+
Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
7782

78-
$txt = 'Internal Server Error' . "\n";
79-
try {
80-
if (Server::get(SystemConfig::class)->getValue('debug', false)) {
81-
$txt .= $e->getMessage();
83+
$txt = 'Internal Server Error' . "\n";
84+
try {
85+
if (Server::get(SystemConfig::class)->getValue('debug', false)) {
86+
$txt .= $e->getMessage();
87+
}
88+
} catch (\Throwable $e) {
89+
// Just to be save
8290
}
83-
} catch (\Throwable $e) {
84-
// Just to be save
91+
ApiHelper::respond(OCSController::RESPOND_SERVER_ERROR, $txt);
8592
}
86-
ApiHelper::respond(OCSController::RESPOND_SERVER_ERROR, $txt);
87-
}
93+
});

0 commit comments

Comments
 (0)