diff --git a/cgi.go b/cgi.go index 4668feb03..7d6fb87ef 100644 --- a/cgi.go +++ b/cgi.go @@ -1,13 +1,11 @@ package frankenphp // #cgo nocallback frankenphp_register_bulk -// #cgo nocallback frankenphp_register_variables_from_request_info // #cgo nocallback frankenphp_register_variable_safe -// #cgo nocallback frankenphp_register_single +// #cgo nocallback frankenphp_register_variable_unsafe // #cgo noescape frankenphp_register_bulk -// #cgo noescape frankenphp_register_variables_from_request_info // #cgo noescape frankenphp_register_variable_safe -// #cgo noescape frankenphp_register_single +// #cgo noescape frankenphp_register_variable_unsafe // #include // #include "frankenphp.h" import "C" @@ -35,38 +33,6 @@ var tlsProtocolStrings = map[uint16]string{ tls.VersionTLS13: "TLSv1.3", } -// Known $_SERVER keys -var knownServerKeys = []string{ - "CONTENT_LENGTH", - "DOCUMENT_ROOT", - "DOCUMENT_URI", - "GATEWAY_INTERFACE", - "HTTP_HOST", - "HTTPS", - "PATH_INFO", - "PHP_SELF", - "REMOTE_ADDR", - "REMOTE_HOST", - "REMOTE_PORT", - "REQUEST_SCHEME", - "SCRIPT_FILENAME", - "SCRIPT_NAME", - "SERVER_NAME", - "SERVER_PORT", - "SERVER_PROTOCOL", - "SERVER_SOFTWARE", - "SSL_PROTOCOL", - "SSL_CIPHER", - "AUTH_TYPE", - "REMOTE_IDENT", - "CONTENT_TYPE", - "PATH_TRANSLATED", - "QUERY_STRING", - "REMOTE_USER", - "REQUEST_METHOD", - "REQUEST_URI", -} - // cStringHTTPMethods caches C string versions of common HTTP methods // to avoid allocations in pinCString on every request. var cStringHTTPMethods = map[string]*C.char{ @@ -87,7 +53,6 @@ var cStringHTTPMethods = map[string]*C.char{ // Inspired by https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { request := fc.request - keys := mainThread.knownServerKeys // Separate remote IP and port; more lenient than net.SplitHostPort var ip, port string if idx := strings.LastIndex(request.RemoteAddr, ":"); idx > -1 { @@ -156,59 +121,59 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { requestURI = fc.requestURI } - C.frankenphp_register_bulk( - trackVarsArray, - packCgiVariable(keys["REMOTE_ADDR"], ip), - packCgiVariable(keys["REMOTE_HOST"], ip), - packCgiVariable(keys["REMOTE_PORT"], port), - packCgiVariable(keys["DOCUMENT_ROOT"], fc.documentRoot), - packCgiVariable(keys["PATH_INFO"], fc.pathInfo), - packCgiVariable(keys["PHP_SELF"], ensureLeadingSlash(request.URL.Path)), - packCgiVariable(keys["DOCUMENT_URI"], fc.docURI), - packCgiVariable(keys["SCRIPT_FILENAME"], fc.scriptFilename), - packCgiVariable(keys["SCRIPT_NAME"], fc.scriptName), - packCgiVariable(keys["HTTPS"], https), - packCgiVariable(keys["SSL_PROTOCOL"], sslProtocol), - packCgiVariable(keys["REQUEST_SCHEME"], rs), - packCgiVariable(keys["SERVER_NAME"], reqHost), - packCgiVariable(keys["SERVER_PORT"], serverPort), - // Variables defined in CGI 1.1 spec - // Some variables are unused but cleared explicitly to prevent - // the parent environment from interfering. - // These values can not be overridden - packCgiVariable(keys["CONTENT_LENGTH"], contentLength), - packCgiVariable(keys["GATEWAY_INTERFACE"], "CGI/1.1"), - packCgiVariable(keys["SERVER_PROTOCOL"], request.Proto), - packCgiVariable(keys["SERVER_SOFTWARE"], "FrankenPHP"), - packCgiVariable(keys["HTTP_HOST"], request.Host), - // These values are always empty but must be defined: - packCgiVariable(keys["AUTH_TYPE"], ""), - packCgiVariable(keys["REMOTE_IDENT"], ""), - // Request uri of the original request - packCgiVariable(keys["REQUEST_URI"], requestURI), - packCgiVariable(keys["SSL_CIPHER"], sslCipher), - ) - - // These values are already present in the SG(request_info), so we'll register them from there - C.frankenphp_register_variables_from_request_info( - trackVarsArray, - keys["CONTENT_TYPE"], - keys["PATH_TRANSLATED"], - keys["QUERY_STRING"], - keys["REMOTE_USER"], - keys["REQUEST_METHOD"], - ) -} - -func packCgiVariable(key *C.zend_string, value string) C.ht_key_value_pair { - return C.ht_key_value_pair{key, toUnsafeChar(value), C.size_t(len(value))} + requestPath := ensureLeadingSlash(request.URL.Path) + + C.frankenphp_register_bulk(trackVarsArray, C.frankenphp_server_vars{ + // approximate total length to avoid array re-hashing: + // 28 CGI vars + headers + environment + total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + len(mainThread.sandboxedEnv)), + + remote_addr: toUnsafeChar(ip), + remote_addr_len: C.size_t(len(ip)), + remote_host: toUnsafeChar(ip), + remote_host_len: C.size_t(len(ip)), + remote_port: toUnsafeChar(port), + remote_port_len: C.size_t(len(port)), + document_root: toUnsafeChar(fc.documentRoot), + document_root_len: C.size_t(len(fc.documentRoot)), + path_info: toUnsafeChar(fc.pathInfo), + path_info_len: C.size_t(len(fc.pathInfo)), + php_self: toUnsafeChar(requestPath), + php_self_len: C.size_t(len(requestPath)), + document_uri: toUnsafeChar(fc.docURI), + document_uri_len: C.size_t(len(fc.docURI)), + script_filename: toUnsafeChar(fc.scriptFilename), + script_filename_len: C.size_t(len(fc.scriptFilename)), + script_name: toUnsafeChar(fc.scriptName), + script_name_len: C.size_t(len(fc.scriptName)), + https: toUnsafeChar(https), + https_len: C.size_t(len(https)), + ssl_protocol: toUnsafeChar(sslProtocol), + ssl_protocol_len: C.size_t(len(sslProtocol)), + request_scheme: toUnsafeChar(rs), + request_scheme_len: C.size_t(len(rs)), + server_name: toUnsafeChar(reqHost), + server_name_len: C.size_t(len(reqHost)), + server_port: toUnsafeChar(serverPort), + server_port_len: C.size_t(len(serverPort)), + content_length: toUnsafeChar(contentLength), + content_length_len: C.size_t(len(contentLength)), + server_protocol: toUnsafeChar(request.Proto), + server_protocol_len: C.size_t(len(request.Proto)), + http_host: toUnsafeChar(request.Host), + http_host_len: C.size_t(len(request.Host)), + request_uri: toUnsafeChar(requestURI), + request_uri_len: C.size_t(len(requestURI)), + ssl_cipher: toUnsafeChar(sslCipher), + ssl_cipher_len: C.size_t(len(sslCipher)), + }) } func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArray *C.zval) { for field, val := range request.Header { - if k := mainThread.commonHeaders[field]; k != nil { + if k := commonHeaders[field]; k != nil { v := strings.Join(val, ", ") - C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) + C.frankenphp_register_variable_unsafe(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) continue } diff --git a/cgi_test.go b/cgi_test.go index d7c0e854d..c4c7a7701 100644 --- a/cgi_test.go +++ b/cgi_test.go @@ -25,7 +25,7 @@ func TestEnsureLeadingSlash(t *testing.T) { } for _, tt := range tests { - t.Run(tt.input + "-" + tt.expected, func(t *testing.T) { + t.Run(tt.input+"-"+tt.expected, func(t *testing.T) { t.Parallel() assert.Equal(t, tt.expected, ensureLeadingSlash(tt.input), "ensureLeadingSlash(%q)", tt.input) diff --git a/frankenphp.c b/frankenphp.c index 20b36130b..4068869c8 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -29,6 +29,7 @@ #include "_cgo_export.h" #include "frankenphp_arginfo.h" +#include "internal/strings/strings.h" #if defined(PHP_WIN32) && defined(ZTS) ZEND_TSRMLS_CACHE_DEFINE() @@ -71,6 +72,7 @@ frankenphp_config frankenphp_get_config() { } bool should_filter_var = 0; +frankenphp_interned_strings_t interned_strings; __thread uintptr_t thread_index; __thread bool is_worker_thread = false; __thread zval *os_environment = NULL; @@ -688,6 +690,11 @@ PHP_FUNCTION(frankenphp_handle_request) { if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { callback_ret = &retval; + + /* pass NULL instead of the NULL zval as return value */ + if (Z_TYPE(retval) == IS_NULL) { + callback_ret = NULL; + } } /* @@ -926,116 +933,83 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, } } -void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, - zval *track_vars_array) { - HashTable *ht = Z_ARRVAL_P(track_vars_array); - frankenphp_register_trusted_var(z_key, value, val_len, ht); -} - /* Register known $_SERVER variables in bulk to avoid cgo overhead */ -void frankenphp_register_bulk( - zval *track_vars_array, ht_key_value_pair remote_addr, - ht_key_value_pair remote_host, ht_key_value_pair remote_port, - ht_key_value_pair document_root, ht_key_value_pair path_info, - ht_key_value_pair php_self, ht_key_value_pair document_uri, - ht_key_value_pair script_filename, ht_key_value_pair script_name, - ht_key_value_pair https, ht_key_value_pair ssl_protocol, - ht_key_value_pair request_scheme, ht_key_value_pair server_name, - ht_key_value_pair server_port, ht_key_value_pair content_length, - ht_key_value_pair gateway_interface, ht_key_value_pair server_protocol, - ht_key_value_pair server_software, ht_key_value_pair http_host, - ht_key_value_pair auth_type, ht_key_value_pair remote_ident, - ht_key_value_pair request_uri, ht_key_value_pair ssl_cipher) { +void frankenphp_register_bulk(zval *track_vars_array, + frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); - frankenphp_register_trusted_var(remote_addr.key, remote_addr.val, - remote_addr.val_len, ht); - frankenphp_register_trusted_var(remote_host.key, remote_host.val, - remote_host.val_len, ht); - frankenphp_register_trusted_var(remote_port.key, remote_port.val, - remote_port.val_len, ht); - frankenphp_register_trusted_var(document_root.key, document_root.val, - document_root.val_len, ht); - frankenphp_register_trusted_var(path_info.key, path_info.val, - path_info.val_len, ht); - frankenphp_register_trusted_var(php_self.key, php_self.val, php_self.val_len, - ht); - frankenphp_register_trusted_var(document_uri.key, document_uri.val, - document_uri.val_len, ht); - frankenphp_register_trusted_var(script_filename.key, script_filename.val, - script_filename.val_len, ht); - frankenphp_register_trusted_var(script_name.key, script_name.val, - script_name.val_len, ht); - frankenphp_register_trusted_var(https.key, https.val, https.val_len, ht); - frankenphp_register_trusted_var(ssl_protocol.key, ssl_protocol.val, - ssl_protocol.val_len, ht); - frankenphp_register_trusted_var(ssl_cipher.key, ssl_cipher.val, - ssl_cipher.val_len, ht); - frankenphp_register_trusted_var(request_scheme.key, request_scheme.val, - request_scheme.val_len, ht); - frankenphp_register_trusted_var(server_name.key, server_name.val, - server_name.val_len, ht); - frankenphp_register_trusted_var(server_port.key, server_port.val, - server_port.val_len, ht); - frankenphp_register_trusted_var(content_length.key, content_length.val, - content_length.val_len, ht); - frankenphp_register_trusted_var(gateway_interface.key, gateway_interface.val, - gateway_interface.val_len, ht); - frankenphp_register_trusted_var(server_protocol.key, server_protocol.val, - server_protocol.val_len, ht); - frankenphp_register_trusted_var(server_software.key, server_software.val, - server_software.val_len, ht); - frankenphp_register_trusted_var(http_host.key, http_host.val, - http_host.val_len, ht); - frankenphp_register_trusted_var(auth_type.key, auth_type.val, - auth_type.val_len, ht); - frankenphp_register_trusted_var(remote_ident.key, remote_ident.val, - remote_ident.val_len, ht); - frankenphp_register_trusted_var(request_uri.key, request_uri.val, - request_uri.val_len, ht); -} - -/** Create an immutable zend_string that lasts for the whole process **/ -zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { - /* persistent strings will be ignored by the GC at the end of a request */ - zend_string *z_string = zend_string_init(string, len, 1); - zend_string_hash_val(z_string); - - /* interned strings will not be ref counted by the GC */ - GC_ADD_FLAGS(z_string, IS_STR_INTERNED); - - return z_string; + zend_hash_extend(ht, vars.total_num_vars, 0); + + // update values with variable strings +#define FRANKENPHP_REGISTER_VAR(name) \ + frankenphp_register_trusted_var(interned_strings.name, vars.name, \ + vars.name##_len, ht) + + FRANKENPHP_REGISTER_VAR(remote_addr); + FRANKENPHP_REGISTER_VAR(remote_host); + FRANKENPHP_REGISTER_VAR(remote_port); + FRANKENPHP_REGISTER_VAR(document_root); + FRANKENPHP_REGISTER_VAR(path_info); + FRANKENPHP_REGISTER_VAR(php_self); + FRANKENPHP_REGISTER_VAR(document_uri); + FRANKENPHP_REGISTER_VAR(script_filename); + FRANKENPHP_REGISTER_VAR(script_name); + FRANKENPHP_REGISTER_VAR(https); + FRANKENPHP_REGISTER_VAR(ssl_protocol); + FRANKENPHP_REGISTER_VAR(ssl_cipher); + FRANKENPHP_REGISTER_VAR(request_scheme); + FRANKENPHP_REGISTER_VAR(server_name); + FRANKENPHP_REGISTER_VAR(server_port); + FRANKENPHP_REGISTER_VAR(content_length); + FRANKENPHP_REGISTER_VAR(server_protocol); + FRANKENPHP_REGISTER_VAR(http_host); + FRANKENPHP_REGISTER_VAR(request_uri); + +#undef FRANKENPHP_REGISTER_VAR + + // update values with unchanging strings + zval zv; + ZVAL_STR(&zv, interned_strings.gateway_interface_str); + zend_hash_update(ht, interned_strings.gateway_interface, &zv); + ZVAL_STR(&zv, interned_strings.server_software_str); + zend_hash_update(ht, interned_strings.server_software, &zv); + + // update values with empty strings + ZVAL_EMPTY_STRING(&zv); + zend_hash_update(ht, interned_strings.auth_type, &zv); + zend_hash_update(ht, interned_strings.remote_ident, &zv); } +/* Register variables from SG(request_info) into $_SERVER */ static void -frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, - bool must_be_present, - zval *track_vars_array) { - if (value != NULL) { - frankenphp_register_trusted_var(zKey, value, strlen(value), - Z_ARRVAL_P(track_vars_array)); - } else if (must_be_present) { - frankenphp_register_trusted_var(zKey, NULL, 0, - Z_ARRVAL_P(track_vars_array)); - } +frankenphp_register_variables_from_request_info(zval *track_vars_array) { + HashTable *ht = Z_ARRVAL_P(track_vars_array); + +#define FRANKENPHP_REGISTER_FROM_INFO(key, field, required) \ + do { \ + char *value = (char *)SG(request_info).field; \ + if (value != NULL) { \ + frankenphp_register_trusted_var(interned_strings.key, value, \ + strlen(value), ht); \ + } else if (required) { \ + frankenphp_register_trusted_var(interned_strings.key, NULL, 0, ht); \ + } \ + } while (0) + + FRANKENPHP_REGISTER_FROM_INFO(content_type, content_type, true); + FRANKENPHP_REGISTER_FROM_INFO(path_translated, path_translated, false); + FRANKENPHP_REGISTER_FROM_INFO(query_string, query_string, true); + FRANKENPHP_REGISTER_FROM_INFO(remote_user, auth_user, false); + FRANKENPHP_REGISTER_FROM_INFO(request_method, request_method, false); + +#undef FRANKENPHP_REGISTER_FROM_INFO } -void frankenphp_register_variables_from_request_info( - zval *track_vars_array, zend_string *content_type, - zend_string *path_translated, zend_string *query_string, - zend_string *auth_user, zend_string *request_method) { - frankenphp_register_variable_from_request_info( - content_type, (char *)SG(request_info).content_type, true, - track_vars_array); - frankenphp_register_variable_from_request_info( - path_translated, (char *)SG(request_info).path_translated, false, - track_vars_array); - frankenphp_register_variable_from_request_info( - query_string, SG(request_info).query_string, true, track_vars_array); - frankenphp_register_variable_from_request_info( - auth_user, (char *)SG(request_info).auth_user, false, track_vars_array); - frankenphp_register_variable_from_request_info( - request_method, (char *)SG(request_info).request_method, false, - track_vars_array); +/* Only hard-coded keys may be registered this way */ +void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, + size_t val_len, + zval *track_vars_array) { + frankenphp_register_trusted_var(z_key, value, val_len, + Z_ARRVAL_P(track_vars_array)); } /* variables with user-defined keys must be registered safely @@ -1077,6 +1051,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { get_full_env(track_vars_array); // php_import_environment_variables(track_vars_array); go_register_variables(thread_index, track_vars_array); + frankenphp_register_variables_from_request_info(track_vars_array); return; } @@ -1096,6 +1071,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { (copy_ctor_func_t)zval_add_ref); go_register_variables(thread_index, track_vars_array); + frankenphp_register_variables_from_request_info(track_vars_array); } static void frankenphp_log_message(const char *message, int syslog_type_int) { @@ -1234,6 +1210,8 @@ static void *php_main(void *arg) { frankenphp_sapi_module.ini_entries = php_ini_overrides; } + interned_strings = frankenphp_init_interned_strings(); + frankenphp_sapi_module.startup(&frankenphp_sapi_module); /* check if a default filter is set in php.ini and only filter if diff --git a/frankenphp.go b/frankenphp.go index c651de3cf..8ce554a23 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - //_ "github.com/ianlancetaylor/cgosymbolizer" + // _ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} diff --git a/frankenphp.h b/frankenphp.h index c833c44f9..f79bfed88 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -17,11 +17,47 @@ typedef struct go_string { char *data; } go_string; -typedef struct ht_key_value_pair { - zend_string *key; - char *val; - size_t val_len; -} ht_key_value_pair; +typedef struct frankenphp_server_vars { + size_t total_num_vars; + char *remote_addr; + size_t remote_addr_len; + char *remote_host; + size_t remote_host_len; + char *remote_port; + size_t remote_port_len; + char *document_root; + size_t document_root_len; + char *path_info; + size_t path_info_len; + char *php_self; + size_t php_self_len; + char *document_uri; + size_t document_uri_len; + char *script_filename; + size_t script_filename_len; + char *script_name; + size_t script_name_len; + char *https; + size_t https_len; + char *ssl_protocol; + size_t ssl_protocol_len; + char *request_scheme; + size_t request_scheme_len; + char *server_name; + size_t server_name_len; + char *server_port; + size_t server_port_len; + char *content_length; + size_t content_length_len; + char *server_protocol; + size_t server_protocol_len; + char *http_host; + size_t http_host_len; + char *request_uri; + size_t request_uri_len; + char *ssl_cipher; + size_t ssl_cipher_len; +} frankenphp_server_vars; typedef struct frankenphp_version { unsigned char major_version; @@ -50,32 +86,18 @@ void frankenphp_update_local_thread_context(bool is_worker); int frankenphp_execute_script_cli(char *script, int argc, char **argv, bool eval); -void frankenphp_register_variables_from_request_info( - zval *track_vars_array, zend_string *content_type, - zend_string *path_translated, zend_string *query_string, - zend_string *auth_user, zend_string *request_method); +void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, + size_t val_len, + zval *track_vars_array); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, zval *track_vars_array); +void frankenphp_register_bulk(zval *track_vars_array, + frankenphp_server_vars vars); + zend_string *frankenphp_init_persistent_string(const char *string, size_t len); int frankenphp_reset_opcache(void); int frankenphp_get_current_memory_limit(); -void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, - zval *track_vars_array); -void frankenphp_register_bulk( - zval *track_vars_array, ht_key_value_pair remote_addr, - ht_key_value_pair remote_host, ht_key_value_pair remote_port, - ht_key_value_pair document_root, ht_key_value_pair path_info, - ht_key_value_pair php_self, ht_key_value_pair document_uri, - ht_key_value_pair script_filename, ht_key_value_pair script_name, - ht_key_value_pair https, ht_key_value_pair ssl_protocol, - ht_key_value_pair request_scheme, ht_key_value_pair server_name, - ht_key_value_pair server_port, ht_key_value_pair content_length, - ht_key_value_pair gateway_interface, ht_key_value_pair server_protocol, - ht_key_value_pair server_software, ht_key_value_pair http_host, - ht_key_value_pair auth_type, ht_key_value_pair remote_ident, - ht_key_value_pair request_uri, ht_key_value_pair ssl_cipher); - void register_extensions(zend_module_entry **m, int len); #endif diff --git a/internal/strings/strings.h b/internal/strings/strings.h new file mode 100644 index 000000000..ce0d9edc5 --- /dev/null +++ b/internal/strings/strings.h @@ -0,0 +1,92 @@ +#ifndef _FRANKENPHP_STRINGS_H +#define _FRANKENPHP_STRINGS_H + +/** + * Cached interned strings for memory and performance benefits + * Add more hard-coded strings here if needed + */ +typedef struct frankenphp_interned_strings_t { + zend_string *remote_addr; + zend_string *remote_host; + zend_string *remote_port; + zend_string *document_root; + zend_string *path_info; + zend_string *php_self; + zend_string *document_uri; + zend_string *script_filename; + zend_string *script_name; + zend_string *https; + zend_string *ssl_protocol; + zend_string *request_scheme; + zend_string *server_name; + zend_string *server_port; + zend_string *content_length; + zend_string *server_protocol; + zend_string *http_host; + zend_string *request_uri; + zend_string *ssl_cipher; + zend_string *server_software; + zend_string *server_software_str; + zend_string *gateway_interface; + zend_string *gateway_interface_str; + zend_string *auth_type; + zend_string *remote_ident; + zend_string *content_type; + zend_string *path_translated; + zend_string *query_string; + zend_string *remote_user; + zend_string *request_method; +} frankenphp_interned_strings_t; + +zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { + /* persistent strings will be ignored by the GC at the end of a request */ + zend_string *z_string = zend_string_init(string, len, 1); + zend_string_hash_val(z_string); + + /* interned strings will not be ref counted by the GC */ + GC_ADD_FLAGS(z_string, IS_STR_INTERNED); + + return z_string; +} + +#define FRANKENPHP_INTERNED_STR(str) \ + frankenphp_init_persistent_string(str, sizeof(str) - 1) + +static frankenphp_interned_strings_t frankenphp_init_interned_strings() { + return (frankenphp_interned_strings_t){ + .remote_addr = FRANKENPHP_INTERNED_STR("REMOTE_ADDR"), + .remote_host = FRANKENPHP_INTERNED_STR("REMOTE_HOST"), + .remote_port = FRANKENPHP_INTERNED_STR("REMOTE_PORT"), + .document_root = FRANKENPHP_INTERNED_STR("DOCUMENT_ROOT"), + .path_info = FRANKENPHP_INTERNED_STR("PATH_INFO"), + .php_self = FRANKENPHP_INTERNED_STR("PHP_SELF"), + .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), + .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), + .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), + .https = FRANKENPHP_INTERNED_STR("HTTPS"), + .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), + .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), + .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), + .server_port = FRANKENPHP_INTERNED_STR("SERVER_PORT"), + .content_length = FRANKENPHP_INTERNED_STR("CONTENT_LENGTH"), + .server_protocol = FRANKENPHP_INTERNED_STR("SERVER_PROTOCOL"), + .http_host = FRANKENPHP_INTERNED_STR("HTTP_HOST"), + .request_uri = FRANKENPHP_INTERNED_STR("REQUEST_URI"), + .ssl_cipher = FRANKENPHP_INTERNED_STR("SSL_CIPHER"), + .server_software = FRANKENPHP_INTERNED_STR("SERVER_SOFTWARE"), + .server_software_str = FRANKENPHP_INTERNED_STR("FrankenPHP"), + .gateway_interface = FRANKENPHP_INTERNED_STR("GATEWAY_INTERFACE"), + .gateway_interface_str = FRANKENPHP_INTERNED_STR("CGI/1.1"), + .auth_type = FRANKENPHP_INTERNED_STR("AUTH_TYPE"), + .remote_ident = FRANKENPHP_INTERNED_STR("REMOTE_IDENT"), + .content_type = FRANKENPHP_INTERNED_STR("CONTENT_TYPE"), + .path_translated = FRANKENPHP_INTERNED_STR("PATH_TRANSLATED"), + .query_string = FRANKENPHP_INTERNED_STR("QUERY_STRING"), + .remote_user = FRANKENPHP_INTERNED_STR("REMOTE_USER"), + .request_method = FRANKENPHP_INTERNED_STR("REQUEST_METHOD"), + }; +} + +#undef FRANKENPHP_INTERNED_STR + +#endif diff --git a/phpmainthread.go b/phpmainthread.go index 1ba7dc3d4..f2fcc813a 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -1,9 +1,7 @@ package frankenphp // #cgo nocallback frankenphp_new_main_thread -// #cgo nocallback frankenphp_init_persistent_string // #cgo noescape frankenphp_new_main_thread -// #cgo noescape frankenphp_init_persistent_string // #include // #include "frankenphp.h" import "C" @@ -20,19 +18,18 @@ import ( // represents the main PHP thread // the thread needs to keep running as long as all other threads are running type phpMainThread struct { - state *state.ThreadState - done chan struct{} - numThreads int - maxThreads int - phpIni map[string]string - commonHeaders map[string]*C.zend_string - knownServerKeys map[string]*C.zend_string - sandboxedEnv map[string]*C.zend_string + state *state.ThreadState + done chan struct{} + numThreads int + maxThreads int + phpIni map[string]string + sandboxedEnv map[string]*C.zend_string } var ( - phpThreads []*phpThread - mainThread *phpMainThread + phpThreads []*phpThread + mainThread *phpMainThread + commonHeaders map[string]*C.zend_string ) // initPHPThreads starts the main PHP thread, @@ -113,15 +110,11 @@ func (mainThread *phpMainThread) start() error { mainThread.state.WaitFor(state.Ready) // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) - mainThread.commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) - for key, phpKey := range phpheaders.CommonRequestHeaders { - mainThread.commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) - } - - // cache $_SERVER keys as zend_strings (SERVER_PROTOCOL, SERVER_SOFTWARE, etc.) - mainThread.knownServerKeys = make(map[string]*C.zend_string, len(knownServerKeys)) - for _, phpKey := range knownServerKeys { - mainThread.knownServerKeys[phpKey] = C.frankenphp_init_persistent_string(toUnsafeChar(phpKey), C.size_t(len(phpKey))) + if commonHeaders == nil { + commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) + for key, phpKey := range phpheaders.CommonRequestHeaders { + commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) + } } return nil