Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 52 additions & 35 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@
}
}

localhost {
franken.local {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be pushed I guess

php_server {
# Keeps /index.php out of Nextcloud's generated URLs, matching the pretty-URL rewrite below.
env front_controller_active true

worker {
file index.php
num 32
watch
#watch
match /index.php/*
}
worker {
file remote.php
num 32
watch
#watch
match /remote.php/*
}
worker {
file ocs/v1.php
num 32
watch
#watch
match /ocs/v1.php/*
match /ocs/v2.php/*
}
Expand All @@ -42,40 +45,54 @@ localhost {

encode gzip

redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301
# Wrapped in route{} so these all run in the order written, regardless of Caddy's
# default directive ordering: the specific rewrites and the forbidden-path block
# must be evaluated before the catch-all pretty-URL rewrite below.
route {
redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301

# Rule: Maps most RFC 8615 compliant well-known URIs to our main frontend controller (/index.php) by default
@wellKnown {
path "/.well-known/"
not {
path /.well-known/acme-challenge
path /.well-known/pki-validation
# Rule: Maps most RFC 8615 compliant well-known URIs to our main frontend controller (/index.php) by default
@wellKnown {
path "/.well-known/"
not {
path /.well-known/acme-challenge
path /.well-known/pki-validation
}
}
}
rewrite @wellKnown /index.php
rewrite @wellKnown /index.php

rewrite /ocm-provider/ /index.php
rewrite /ocm-provider/ /index.php

@forbidden {
path /.htaccess
path /data/*
path /config/*
path /db_structure
path /.xml
path /README
path /3rdparty/*
path /lib/*
path /templates/*
path /occ
path /build
path /tests
path /console.php
path /autotest
path /issue
path /indi
path /db_
path /console
@forbidden {
path /.htaccess
path /data/*
path /config/*
path /db_structure
path /.xml
path /README
path /3rdparty/*
path /lib/*
path /templates/*
path /occ
path /build
path /tests
path /console.php
path /autotest
path /issue
path /indi
path /db_
path /console
}
respond @forbidden 404

# Pretty URLs: rewrite anything that isn't already destined for a worker's own
# prefix and doesn't correspond to an existing static file (assets, etc.) to the
# front controller, so franken.local/ works instead of only franken.local/index.php/.
@prettyUrl {
not path /index.php/* /remote.php/* /ocs/v1.php/* /ocs/v2.php/*
not file
}
rewrite @prettyUrl /index.php{path}
}
respond @forbidden 404
}
11 changes: 7 additions & 4 deletions apps/files/tests/Controller/ViewControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
*/
#[\PHPUnit\Framework\Attributes\Group('RoutingWeirdness')]
class ViewControllerTest extends TestCase {
private ContainerInterface&MockObject $container;
private IAppManager&MockObject $appManager;
private IAppConfig&MockObject $appConfig;
private ICacheFactory&MockObject $cacheFactory;
Expand Down Expand Up @@ -114,13 +113,12 @@ protected function setUp(): void {
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->eventLogger = $this->createMock(IEventLogger::class);
$this->container = $this->createMock(ContainerInterface::class);
$this->router = new Router(
$this->logger,
$this->request,
$this->config,
$this->eventLogger,
$this->container,
$this->createMock(ContainerInterface::class),
$this->appManager,
);

Expand Down Expand Up @@ -257,9 +255,14 @@ public function testShortRedirect(?string $openfile, ?string $opendetails, strin
}

public function testShowFileRouteWithTrashedFile(): void {
$this->appManager->expects($this->exactly(2))
// Only ViewController's own trashbin check goes through isEnabledForUser now; Router's
// route-loading gate uses isEnabledForAnyone() instead (see PersistAcrossRequests).
$this->appManager->expects($this->once())
->method('isEnabledForUser')
->willReturn(true);
$this->appManager->expects($this->any())
->method('isEnabledForAnyone')
->willReturn(true);

$parentNode = $this->createMock(Folder::class);
$parentNode->expects($this->once())
Expand Down
5 changes: 5 additions & 0 deletions core/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OC\Core\Listener\BeforeTemplateRenderedListener;
use OC\Core\Listener\LoadAdditionalEntriesListener;
use OC\Core\Listener\PasswordUpdatedListener;
use OC\Core\Listener\PersistentServiceInvalidationListener;
use OC\Core\Listener\RestrictInteractionListener;
use OC\Core\Notification\CoreNotifier;
use OC\Core\Sharing\Permission\EditSharePermissionPreset;
Expand All @@ -42,6 +43,8 @@
use OC\DirectEditing\Listeners\UserDisabledTokenCleanupListener as UserDisabledDirectEditingTokenCleanupListener;
use OC\OCM\OCMDiscoveryHandler;
use OC\TagManager;
use OCP\App\Events\AppDisableEvent;
use OCP\App\Events\AppEnableEvent;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand Down Expand Up @@ -92,6 +95,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
$context->registerEventListener(BeforeLoginTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
$context->registerEventListener(LoadAdditionalEntriesEvent::class, LoadAdditionalEntriesListener::class);
$context->registerEventListener(AppEnableEvent::class, PersistentServiceInvalidationListener::class);
$context->registerEventListener(AppDisableEvent::class, PersistentServiceInvalidationListener::class);
$context->registerEventListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
$context->registerEventListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
$context->registerEventListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
Expand Down
34 changes: 34 additions & 0 deletions core/Listener/PersistentServiceInvalidationListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OC\Core\Listener;

use OCP\App\Events\AppDisableEvent;
use OCP\App\Events\AppEnableEvent;
use OCP\AppFramework\Utility\IPersistentServiceInvalidator;
use OCP\AppFramework\Utility\PersistentServiceGroup;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

/**
* @template-implements IEventListener<AppEnableEvent|AppDisableEvent>
*/
class PersistentServiceInvalidationListener implements IEventListener {
public function __construct(
private IPersistentServiceInvalidator $invalidator,
) {
}

#[\Override]
public function handle(Event $event): void {
if ($event instanceof AppEnableEvent || $event instanceof AppDisableEvent) {
$this->invalidator->invalidate(PersistentServiceGroup::Apps);
}
}
}
23 changes: 22 additions & 1 deletion lib/OC.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

use OC\AppFramework\Utility\SimpleContainer;
use OC\Files\Filesystem;
use OC\NavigationManager;
use OC\Profiler\BuiltInProfiler;
Expand Down Expand Up @@ -770,7 +771,16 @@
self::handleAuthHeaders();

// setup the basic server
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
if (isset(self::$server)) {

Check failure on line 774 in lib/OC.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

RedundantPropertyInitializationCheck

lib/OC.php:774:7: RedundantPropertyInitializationCheck: Static property OC::$server with type OC\Server has unexpected isset check — should it be nullable? (see https://psalm.dev/261)
// Same worker (e.g. FrankenPHP) serving another request: keep every service
// *definition* alive and only forget the instances a fresh request shouldn't
// inherit, rather than discarding and rebuilding the whole container. Anything
// kept alive on purpose (see \OCP\AppFramework\Attribute\PersistAcrossRequests)
// is left untouched.
self::$server->resetForNextRequest();
} else {
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
}
self::$server->boot();

self::oneTimeChecks();
Expand All @@ -780,6 +790,15 @@
$config = Server::get(IConfig::class);
$request = Server::get(IRequest::class);

// The router may be reused from a previous request on a long-running worker: it's
// per-request data, not a dependency, so nothing rebuilds it automatically.
$router = Server::get(\OC\Route\Router::class);
$router->refreshContext($request);
$router->refreshRequestScopedCollaborators(
Server::get(\OCP\App\IAppManager::class),
Server::get(\OCP\Diagnostics\IEventLogger::class),
);

try {
$profiler = new BuiltInProfiler(
$config,
Expand Down Expand Up @@ -1382,6 +1401,8 @@
*/
public static function handleRequests(callable $handler): void {
if (function_exists('frankenphp_handle_request') && isset($_SERVER['FRANKENPHP_WORKER']) && $_SERVER['FRANKENPHP_WORKER'] === '1') {
SimpleContainer::$keepPersistentServices = true;

$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
Expand Down
5 changes: 5 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
'OCP\\AppFramework\\Attribute\\ExceptionalImplementable' => $baseDir . '/lib/public/AppFramework/Attribute/ExceptionalImplementable.php',
'OCP\\AppFramework\\Attribute\\Implementable' => $baseDir . '/lib/public/AppFramework/Attribute/Implementable.php',
'OCP\\AppFramework\\Attribute\\Listenable' => $baseDir . '/lib/public/AppFramework/Attribute/Listenable.php',
'OCP\\AppFramework\\Attribute\\PersistAcrossRequests' => $baseDir . '/lib/public/AppFramework/Attribute/PersistAcrossRequests.php',
'OCP\\AppFramework\\Attribute\\Throwable' => $baseDir . '/lib/public/AppFramework/Attribute/Throwable.php',
'OCP\\AppFramework\\AuthPublicShareController' => $baseDir . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Bootstrap\\IBootContext' => $baseDir . '/lib/public/AppFramework/Bootstrap/IBootContext.php',
Expand Down Expand Up @@ -197,7 +198,9 @@
'OCP\\AppFramework\\Services\\IInitialState' => $baseDir . '/lib/public/AppFramework/Services/IInitialState.php',
'OCP\\AppFramework\\Services\\InitialStateProvider' => $baseDir . '/lib/public/AppFramework/Services/InitialStateProvider.php',
'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => $baseDir . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php',
'OCP\\AppFramework\\Utility\\IPersistentServiceInvalidator' => $baseDir . '/lib/public/AppFramework/Utility/IPersistentServiceInvalidator.php',
'OCP\\AppFramework\\Utility\\ITimeFactory' => $baseDir . '/lib/public/AppFramework/Utility/ITimeFactory.php',
'OCP\\AppFramework\\Utility\\PersistentServiceGroup' => $baseDir . '/lib/public/AppFramework/Utility/PersistentServiceGroup.php',
'OCP\\App\\AppInfoDefinition' => $baseDir . '/lib/public/App/AppInfoDefinition.php',
'OCP\\App\\AppPathNotFoundException' => $baseDir . '/lib/public/App/AppPathNotFoundException.php',
'OCP\\App\\Events\\AppDisableEvent' => $baseDir . '/lib/public/App/Events/AppDisableEvent.php',
Expand Down Expand Up @@ -1250,6 +1253,7 @@
'OC\\AppFramework\\Services\\AppConfig' => $baseDir . '/lib/private/AppFramework/Services/AppConfig.php',
'OC\\AppFramework\\Services\\InitialState' => $baseDir . '/lib/private/AppFramework/Services/InitialState.php',
'OC\\AppFramework\\Utility\\ControllerMethodReflector' => $baseDir . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php',
'OC\\AppFramework\\Utility\\PersistentServiceInvalidator' => $baseDir . '/lib/private/AppFramework/Utility/PersistentServiceInvalidator.php',
'OC\\AppFramework\\Utility\\QueryNotFoundException' => $baseDir . '/lib/private/AppFramework/Utility/QueryNotFoundException.php',
'OC\\AppFramework\\Utility\\SimpleContainer' => $baseDir . '/lib/private/AppFramework/Utility/SimpleContainer.php',
'OC\\AppFramework\\Utility\\TimeFactory' => $baseDir . '/lib/private/AppFramework/Utility/TimeFactory.php',
Expand Down Expand Up @@ -1637,6 +1641,7 @@
'OC\\Core\\Listener\\FeedBackHandler' => $baseDir . '/core/Listener/FeedBackHandler.php',
'OC\\Core\\Listener\\LoadAdditionalEntriesListener' => $baseDir . '/core/Listener/LoadAdditionalEntriesListener.php',
'OC\\Core\\Listener\\PasswordUpdatedListener' => $baseDir . '/core/Listener/PasswordUpdatedListener.php',
'OC\\Core\\Listener\\PersistentServiceInvalidationListener' => $baseDir . '/core/Listener/PersistentServiceInvalidationListener.php',
'OC\\Core\\Listener\\RestrictInteractionListener' => $baseDir . '/core/Listener/RestrictInteractionListener.php',
'OC\\Core\\Middleware\\TwoFactorMiddleware' => $baseDir . '/core/Middleware/TwoFactorMiddleware.php',
'OC\\Core\\Migrations\\Version13000Date20170705121758' => $baseDir . '/core/Migrations/Version13000Date20170705121758.php',
Expand Down
5 changes: 5 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\AppFramework\\Attribute\\ExceptionalImplementable' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Attribute/ExceptionalImplementable.php',
'OCP\\AppFramework\\Attribute\\Implementable' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Attribute/Implementable.php',
'OCP\\AppFramework\\Attribute\\Listenable' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Attribute/Listenable.php',
'OCP\\AppFramework\\Attribute\\PersistAcrossRequests' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Attribute/PersistAcrossRequests.php',
'OCP\\AppFramework\\Attribute\\Throwable' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Attribute/Throwable.php',
'OCP\\AppFramework\\AuthPublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Bootstrap\\IBootContext' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Bootstrap/IBootContext.php',
Expand Down Expand Up @@ -238,7 +239,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\AppFramework\\Services\\IInitialState' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Services/IInitialState.php',
'OCP\\AppFramework\\Services\\InitialStateProvider' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Services/InitialStateProvider.php',
'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php',
'OCP\\AppFramework\\Utility\\IPersistentServiceInvalidator' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/IPersistentServiceInvalidator.php',
'OCP\\AppFramework\\Utility\\ITimeFactory' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/ITimeFactory.php',
'OCP\\AppFramework\\Utility\\PersistentServiceGroup' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/PersistentServiceGroup.php',
'OCP\\App\\AppInfoDefinition' => __DIR__ . '/../../..' . '/lib/public/App/AppInfoDefinition.php',
'OCP\\App\\AppPathNotFoundException' => __DIR__ . '/../../..' . '/lib/public/App/AppPathNotFoundException.php',
'OCP\\App\\Events\\AppDisableEvent' => __DIR__ . '/../../..' . '/lib/public/App/Events/AppDisableEvent.php',
Expand Down Expand Up @@ -1291,6 +1294,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\AppFramework\\Services\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/AppConfig.php',
'OC\\AppFramework\\Services\\InitialState' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/InitialState.php',
'OC\\AppFramework\\Utility\\ControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php',
'OC\\AppFramework\\Utility\\PersistentServiceInvalidator' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/PersistentServiceInvalidator.php',
'OC\\AppFramework\\Utility\\QueryNotFoundException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/QueryNotFoundException.php',
'OC\\AppFramework\\Utility\\SimpleContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/SimpleContainer.php',
'OC\\AppFramework\\Utility\\TimeFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/TimeFactory.php',
Expand Down Expand Up @@ -1678,6 +1682,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Listener\\FeedBackHandler' => __DIR__ . '/../../..' . '/core/Listener/FeedBackHandler.php',
'OC\\Core\\Listener\\LoadAdditionalEntriesListener' => __DIR__ . '/../../..' . '/core/Listener/LoadAdditionalEntriesListener.php',
'OC\\Core\\Listener\\PasswordUpdatedListener' => __DIR__ . '/../../..' . '/core/Listener/PasswordUpdatedListener.php',
'OC\\Core\\Listener\\PersistentServiceInvalidationListener' => __DIR__ . '/../../..' . '/core/Listener/PersistentServiceInvalidationListener.php',
'OC\\Core\\Listener\\RestrictInteractionListener' => __DIR__ . '/../../..' . '/core/Listener/RestrictInteractionListener.php',
'OC\\Core\\Middleware\\TwoFactorMiddleware' => __DIR__ . '/../../..' . '/core/Middleware/TwoFactorMiddleware.php',
'OC\\Core\\Migrations\\Version13000Date20170705121758' => __DIR__ . '/../../..' . '/core/Migrations/Version13000Date20170705121758.php',
Expand Down
Loading
Loading