Problem/Motivation
Persistent app servers (like FrankenPHP in worker mode) can get stale requests. DrupalKernel::terminate() pops the main request only after the kernel.terminate subscribers and the destructable services run. If either throws, the request stays on the stack, and the next request handled by the same container gets the stale request from getMainRequest().
Also, $prepared is never reset. A page cache hit skips preHandle(), but terminate() still runs the subscribers and pops a request it never pushed.
Steps to reproduce
- Register a
kernel.terminatesubscriber that throws. - Handle and terminate request A, catching the exception.
- Handle request B with the same kernel.
\Drupal::requestStack()->getMainRequest()returns A.
Proposed resolution
Wrap the termination callbacks and destructable service cleanup in try/finally. In finally, if $prepared is TRUE, reset it to FALSE and pop the request pushed by preHandle(). Allow the exception to propagate.
This follows the existing try/finally pattern in KernelPreHandle::handle() for popping subrequests.
Issue fork drupal-3626014
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
ptmkenny commentedComment #4
ptmkenny commentedComment #5
amitgoyal commentedThe MR correctly wraps terminate() logic in try/finally, resetting $prepared before popping the request stack. This also correctly handles the page-cache-hit case where $prepared is FALSE.
Test coverage added for both the failed-termination and page-cache-bypass scenarios. Pipeline is green (the only failing jobs are the PHP 8.6 unit test lane, which is a non-blocking allow_failure job for the next PHP major and unrelated to this change).
Marking RTBC.