Skip to content

Commit 952754d

Browse files
fix: don't flush env between requests (#1814)
1 parent 9b851bf commit 952754d

3 files changed

Lines changed: 51 additions & 48 deletions

File tree

frankenphp.c

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,44 @@ static void frankenphp_free_request_context() {
101101
SG(request_info).request_uri = NULL;
102102
}
103103

104-
static void frankenphp_destroy_super_globals() {
104+
/* reset all 'auto globals' in worker mode except of $_ENV
105+
* see: php_hash_environment() */
106+
static void frankenphp_reset_super_globals() {
105107
zend_try {
106-
for (int i = 0; i < NUM_TRACK_VARS; i++) {
107-
zval_ptr_dtor_nogc(&PG(http_globals)[i]);
108-
}
108+
/* only $_FILES needs to be flushed explicitly
109+
* $_GET, $_POST, $_COOKIE and $_SERVER are flushed on reimport
110+
* $_ENV is not flushed
111+
* for more info see: php_startup_auto_globals()
112+
*/
113+
zval *files = &PG(http_globals)[TRACK_VARS_FILES];
114+
zval_ptr_dtor_nogc(files);
115+
memset(files, 0, sizeof(*files));
109116
}
110117
zend_end_try();
118+
119+
zend_auto_global *auto_global;
120+
zend_string *_env = ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV);
121+
zend_string *_server = ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER);
122+
ZEND_HASH_MAP_FOREACH_PTR(CG(auto_globals), auto_global) {
123+
if (auto_global->name == _env) {
124+
/* skip $_ENV */
125+
} else if (auto_global->name == _server) {
126+
/* always reimport $_SERVER */
127+
auto_global->armed = auto_global->auto_global_callback(auto_global->name);
128+
} else if (auto_global->jit) {
129+
/* globals with jit are: $_SERVER, $_ENV, $_REQUEST, $GLOBALS,
130+
* jit will only trigger on script parsing and therefore behaves
131+
* differently in worker mode. We will skip all jit globals
132+
*/
133+
} else if (auto_global->auto_global_callback) {
134+
/* $_GET, $_POST, $_COOKIE, $_FILES are reimported here */
135+
auto_global->armed = auto_global->auto_global_callback(auto_global->name);
136+
} else {
137+
/* $_SESSION will land here (not an http_global) */
138+
auto_global->armed = 0;
139+
}
140+
}
141+
ZEND_HASH_FOREACH_END();
111142
}
112143

113144
/*
@@ -187,7 +218,6 @@ static int frankenphp_worker_request_startup() {
187218
frankenphp_update_request_context();
188219

189220
zend_try {
190-
frankenphp_destroy_super_globals();
191221
frankenphp_release_temporary_streams();
192222
php_output_activate();
193223

@@ -225,21 +255,7 @@ static int frankenphp_worker_request_startup() {
225255
php_output_set_implicit_flush(1);
226256
}
227257

228-
php_hash_environment();
229-
230-
/* zend_is_auto_global will force a re-import of the $_SERVER global */
231-
zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER));
232-
233-
/* disarm the $_ENV auto_global to prevent it from being reloaded in worker
234-
* mode */
235-
if (zend_hash_str_exists(&EG(symbol_table), "_ENV", 4)) {
236-
zend_auto_global *env_global;
237-
if ((env_global = zend_hash_find_ptr(
238-
CG(auto_globals), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV))) !=
239-
NULL) {
240-
env_global->armed = 0;
241-
}
242-
}
258+
frankenphp_reset_super_globals();
243259

244260
const char **module_name;
245261
zend_module_entry *module;
@@ -514,11 +530,6 @@ static zend_module_entry frankenphp_module = {
514530
STANDARD_MODULE_PROPERTIES};
515531

516532
static void frankenphp_request_shutdown() {
517-
if (is_worker_thread) {
518-
/* ensure $_ENV is not in an invalid state before shutdown */
519-
zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_ENV]);
520-
array_init(&PG(http_globals)[TRACK_VARS_ENV]);
521-
}
522533
frankenphp_free_request_context();
523534
php_request_shutdown((void *)0);
524535
}

internal/phpheaders/phpheaders_test.go

Lines changed: 0 additions & 23 deletions
This file was deleted.

phpmainthread_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"testing"
1313
"time"
1414

15+
"github.com/dunglas/frankenphp/internal/phpheaders"
1516
"github.com/stretchr/testify/assert"
1617
)
1718

@@ -235,6 +236,20 @@ func allPossibleTransitions(worker1Path string, worker2Path string) []func(*phpT
235236
}
236237
}
237238

239+
func TestAllCommonHeadersAreCorrect(t *testing.T) {
240+
fakeRequest := httptest.NewRequest("GET", "http://localhost", nil)
241+
242+
for header, phpHeader := range phpheaders.CommonRequestHeaders {
243+
// verify that common and uncommon headers return the same result
244+
expectedPHPHeader := phpheaders.GetUnCommonHeader(header)
245+
assert.Equal(t, phpHeader+"\x00", expectedPHPHeader, "header is not well formed: "+phpHeader)
246+
247+
// net/http will capitalize lowercase headers, verify that headers are capitalized
248+
fakeRequest.Header.Add(header, "foo")
249+
assert.Contains(t, fakeRequest.Header, header, "header is not correctly capitalized: "+header)
250+
}
251+
}
252+
238253
func TestCorrectThreadCalculation(t *testing.T) {
239254
maxProcs := runtime.GOMAXPROCS(0) * 2
240255
oneWorkerThread := []workerOpt{{num: 1}}

0 commit comments

Comments
 (0)