feat: always ignore user abort (#2189)

Automatically sets `ignore_user_abort` to true in worker mode as
mentioned in #2186, removing the requirement to change it via ini.

Would also be possible to expose something like an explicit
`frankenphp_client_has_closed()` function for in-between critical
sections.

---------

Co-authored-by: Marc <m@pyc.ac>
This commit is contained in:
Alexander Stecher
2026-02-19 11:55:39 +01:00
committed by GitHub
parent 7c563d2567
commit b02d99ae8a
3 changed files with 23 additions and 3 deletions
-3
View File
@@ -72,9 +72,6 @@ The following example shows how to create your own worker script without relying
<?php
// public/index.php
// Prevent worker script termination when a client connection is interrupted
ignore_user_abort(true);
// Boot your app
require __DIR__.'/vendor/autoload.php';
+6
View File
@@ -71,6 +71,8 @@ frankenphp_config frankenphp_get_config() {
}
bool should_filter_var = 0;
bool original_user_abort_setting = 0;
__thread uintptr_t thread_index;
__thread bool is_worker_thread = false;
__thread zval *os_environment = NULL;
@@ -113,6 +115,9 @@ __thread session_user_handlers *worker_session_handlers_snapshot = NULL;
void frankenphp_update_local_thread_context(bool is_worker) {
is_worker_thread = is_worker;
/* workers should keep running if the user aborts the connection */
PG(ignore_user_abort) = is_worker ? 1 : original_user_abort_setting;
}
static void frankenphp_update_request_context() {
@@ -1241,6 +1246,7 @@ static void *php_main(void *arg) {
char *default_filter;
cfg_get_string("filter.default", &default_filter);
should_filter_var = default_filter != NULL;
original_user_abort_setting = PG(ignore_user_abort);
go_frankenphp_main_thread_is_ready();
+17
View File
@@ -1,6 +1,7 @@
package frankenphp_test
import (
"context"
"fmt"
"io"
"log"
@@ -157,3 +158,19 @@ func TestWorkerHasOSEnvironmentVariableInSERVER(t *testing.T) {
assert.Contains(t, string(body), "custom_env_variable_value")
}, &testOptions{workerScript: "worker.php", nbWorkers: 1, nbParallelRequests: 1})
}
func TestKeepRunningOnConnectionAbort(t *testing.T) {
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
req := httptest.NewRequest("GET", "http://example.com/worker-with-counter.php", nil)
ctx, cancel := context.WithCancel(req.Context())
req = req.WithContext(ctx)
cancel()
body1, _ := testRequest(req, handler, t)
assert.Equal(t, "requests:1", body1, "should have handled exactly one request")
body2, _ := testGet("http://example.com/worker-with-counter.php", handler, t)
assert.Equal(t, "requests:2", body2, "should not have stopped execution after the first request was aborted")
}, &testOptions{workerScript: "worker-with-counter.php", nbWorkers: 1, nbParallelRequests: 1})
}