mirror of
https://github.com/php/frankenphp.git
synced 2026-03-23 16:42:13 +01:00
refactor: cleaner cgi string handling
Introduces C-side interned string registry (frankenphp_strings) and a frankenphp_server_vars struct to bulk-register known $_SERVER variables with pre-sized hashtable capacity.
This commit is contained in:
committed by
GitHub
parent
27ff6b49d8
commit
356d2e1745
203
cgi.go
203
cgi.go
@@ -1,13 +1,13 @@
|
||||
package frankenphp
|
||||
|
||||
// #cgo nocallback frankenphp_register_bulk
|
||||
// #cgo nocallback frankenphp_register_variables_from_request_info
|
||||
// #cgo nocallback frankenphp_register_server_vars
|
||||
// #cgo nocallback frankenphp_register_variable_safe
|
||||
// #cgo nocallback frankenphp_register_single
|
||||
// #cgo noescape frankenphp_register_bulk
|
||||
// #cgo noescape frankenphp_register_variables_from_request_info
|
||||
// #cgo nocallback frankenphp_register_known_variable
|
||||
// #cgo nocallback frankenphp_init_persistent_string
|
||||
// #cgo noescape frankenphp_register_server_vars
|
||||
// #cgo noescape frankenphp_register_variable_safe
|
||||
// #cgo noescape frankenphp_register_single
|
||||
// #cgo noescape frankenphp_register_known_variable
|
||||
// #cgo noescape frankenphp_init_persistent_string
|
||||
// #include "frankenphp.h"
|
||||
// #include <php_variables.h>
|
||||
import "C"
|
||||
@@ -26,47 +26,6 @@ import (
|
||||
"golang.org/x/text/search"
|
||||
)
|
||||
|
||||
// Protocol versions, in Apache mod_ssl format: https://httpd.apache.org/docs/current/mod/mod_ssl.html
|
||||
// Note that these are slightly different from SupportedProtocols in caddytls/config.go
|
||||
var tlsProtocolStrings = map[uint16]string{
|
||||
tls.VersionTLS10: "TLSv1",
|
||||
tls.VersionTLS11: "TLSv1.1",
|
||||
tls.VersionTLS12: "TLSv1.2",
|
||||
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 +46,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 {
|
||||
@@ -102,24 +60,21 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
ip = ip[1 : len(ip)-1]
|
||||
}
|
||||
|
||||
var https, sslProtocol, sslCipher, rs string
|
||||
var rs, https, sslProtocol *C.zend_string
|
||||
var sslCipher string
|
||||
|
||||
if request.TLS == nil {
|
||||
rs = "http"
|
||||
https = ""
|
||||
sslProtocol = ""
|
||||
rs = C.frankenphp_strings.httpLowercase
|
||||
https = C.frankenphp_strings.empty
|
||||
sslProtocol = C.frankenphp_strings.empty
|
||||
sslCipher = ""
|
||||
} else {
|
||||
rs = "https"
|
||||
https = "on"
|
||||
rs = C.frankenphp_strings.httpsLowercase
|
||||
https = C.frankenphp_strings.on
|
||||
|
||||
// and pass the protocol details in a manner compatible with Apache's mod_ssl
|
||||
// (which is why these have an SSL_ prefix and not TLS_).
|
||||
if v, ok := tlsProtocolStrings[request.TLS.Version]; ok {
|
||||
sslProtocol = v
|
||||
} else {
|
||||
sslProtocol = ""
|
||||
}
|
||||
sslProtocol = tlsProtocol(request.TLS.Version)
|
||||
|
||||
if request.TLS.CipherSuite != 0 {
|
||||
sslCipher = tls.CipherSuiteName(request.TLS.CipherSuite)
|
||||
@@ -139,9 +94,9 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
// even if the port is the default port for the scheme and could otherwise be omitted from a URI.
|
||||
// https://tools.ietf.org/html/rfc3875#section-4.1.15
|
||||
switch rs {
|
||||
case "https":
|
||||
case C.frankenphp_strings.httpsLowercase:
|
||||
reqPort = "443"
|
||||
case "http":
|
||||
case C.frankenphp_strings.httpLowercase:
|
||||
reqPort = "80"
|
||||
}
|
||||
}
|
||||
@@ -156,59 +111,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),
|
||||
)
|
||||
requestPath := ensureLeadingSlash(request.URL.Path)
|
||||
|
||||
// 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"],
|
||||
)
|
||||
}
|
||||
C.frankenphp_register_server_vars(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(request.Header) + len(fc.env) + lengthOfEnv),
|
||||
|
||||
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))}
|
||||
// CGI vars with variable values
|
||||
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)),
|
||||
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)),
|
||||
|
||||
// CGI vars with known values
|
||||
request_scheme: rs, // "http" or "https"
|
||||
ssl_protocol: sslProtocol, // values from tlsProtocol
|
||||
https: https, // "on" or empty
|
||||
})
|
||||
}
|
||||
|
||||
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_known_variable(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -227,8 +182,8 @@ func addPreparedEnvToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
|
||||
fc.env = nil
|
||||
}
|
||||
|
||||
//export go_register_variables
|
||||
func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
|
||||
//export go_register_server_variables
|
||||
func go_register_server_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
|
||||
thread := phpThreads[threadIndex]
|
||||
fc := thread.frankenPHPContext()
|
||||
|
||||
@@ -410,8 +365,32 @@ func ensureLeadingSlash(path string) string {
|
||||
return "/" + path
|
||||
}
|
||||
|
||||
// toUnsafeChar returns a *C.char pointing at the backing bytes the Go string.
|
||||
// If C does not store the string, it may be passed directly in a Cgo call (most efficient).
|
||||
// If C stores the string, it must be pinned explicitly instead (inefficient).
|
||||
// C may never modify the string.
|
||||
func toUnsafeChar(s string) *C.char {
|
||||
sData := unsafe.StringData(s)
|
||||
|
||||
return (*C.char)(unsafe.Pointer(sData))
|
||||
return (*C.char)(unsafe.Pointer(unsafe.StringData(s)))
|
||||
}
|
||||
|
||||
// initialize a global zend_string that must never be freed and is ignored by GC
|
||||
func newPersistentZendString(str string) *C.zend_string {
|
||||
return C.frankenphp_init_persistent_string(toUnsafeChar(str), C.size_t(len(str)))
|
||||
}
|
||||
|
||||
// Protocol versions, in Apache mod_ssl format: https://httpd.apache.org/docs/current/mod/mod_ssl.html
|
||||
// Note that these are slightly different from SupportedProtocols in caddytls/config.go
|
||||
func tlsProtocol(proto uint16) *C.zend_string {
|
||||
switch proto {
|
||||
case tls.VersionTLS10:
|
||||
return C.frankenphp_strings.tls1
|
||||
case tls.VersionTLS11:
|
||||
return C.frankenphp_strings.tls11
|
||||
case tls.VersionTLS12:
|
||||
return C.frankenphp_strings.tls12
|
||||
case tls.VersionTLS13:
|
||||
return C.frankenphp_strings.tls13
|
||||
default:
|
||||
return C.frankenphp_strings.empty
|
||||
}
|
||||
}
|
||||
|
||||
13
env.go
13
env.go
@@ -1,7 +1,5 @@
|
||||
package frankenphp
|
||||
|
||||
// #cgo nocallback frankenphp_init_persistent_string
|
||||
// #cgo noescape frankenphp_init_persistent_string
|
||||
// #include "frankenphp.h"
|
||||
// #include "types.h"
|
||||
import "C"
|
||||
@@ -10,12 +8,17 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var lengthOfEnv = 0
|
||||
|
||||
//export go_init_os_env
|
||||
func go_init_os_env(mainThreadEnv *C.zend_array) {
|
||||
for _, envVar := range os.Environ() {
|
||||
fullEnv := os.Environ()
|
||||
lengthOfEnv = len(fullEnv)
|
||||
|
||||
for _, envVar := range fullEnv {
|
||||
key, val, _ := strings.Cut(envVar, "=")
|
||||
zkey := C.frankenphp_init_persistent_string(toUnsafeChar(key), C.size_t(len(key)))
|
||||
zStr := C.frankenphp_init_persistent_string(toUnsafeChar(val), C.size_t(len(val)))
|
||||
zkey := newPersistentZendString(key)
|
||||
zStr := newPersistentZendString(val)
|
||||
C.__hash_update_string__(mainThreadEnv, zkey, zStr)
|
||||
}
|
||||
}
|
||||
|
||||
172
frankenphp.c
172
frankenphp.c
@@ -80,6 +80,7 @@ frankenphp_config frankenphp_get_config() {
|
||||
|
||||
bool should_filter_var = 0;
|
||||
bool original_user_abort_setting = 0;
|
||||
frankenphp_interned_strings_t frankenphp_strings = {0};
|
||||
HashTable *main_thread_env = NULL;
|
||||
|
||||
__thread uintptr_t thread_index;
|
||||
@@ -572,6 +573,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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -802,72 +808,53 @@ 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_server_vars(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);
|
||||
zend_hash_extend(ht, vars.total_num_vars, 0);
|
||||
|
||||
// update values with variable strings
|
||||
#define FRANKENPHP_REGISTER_VAR(name) \
|
||||
frankenphp_register_trusted_var(frankenphp_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(ssl_cipher);
|
||||
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 hard-coded zend_strings */
|
||||
zval zv;
|
||||
ZVAL_STR(&zv, frankenphp_strings.cgi11);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.gateway_interface, &zv);
|
||||
ZVAL_STR(&zv, frankenphp_strings.frankenphp);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.server_software, &zv);
|
||||
ZVAL_STR(&zv, vars.request_scheme);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.request_scheme, &zv);
|
||||
ZVAL_STR(&zv, vars.ssl_protocol);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.ssl_protocol, &zv);
|
||||
ZVAL_STR(&zv, vars.https);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.https, &zv);
|
||||
|
||||
/* update values with always empty strings */
|
||||
ZVAL_EMPTY_STRING(&zv);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.auth_type, &zv);
|
||||
zend_hash_update_ind(ht, frankenphp_strings.remote_ident, &zv);
|
||||
}
|
||||
|
||||
/** Create an immutable zend_string that lasts for the whole process **/
|
||||
@@ -882,7 +869,22 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) {
|
||||
return z_string;
|
||||
}
|
||||
|
||||
static void
|
||||
/* initialize all hard-coded zend_strings once per process */
|
||||
static void frankenphp_init_interned_strings(void) {
|
||||
if (frankenphp_strings.remote_addr != NULL) {
|
||||
return; /* already initialized */
|
||||
}
|
||||
|
||||
#define F_INITIALIZE_FIELD(name, str) \
|
||||
frankenphp_strings.name = \
|
||||
frankenphp_init_persistent_string(str, sizeof(str) - 1);
|
||||
|
||||
FRANKENPHP_INTERNED_STRINGS_LIST(F_INITIALIZE_FIELD)
|
||||
#undef F_INITIALIZE_FIELD
|
||||
}
|
||||
|
||||
/* Register variables from SG(request_info) into $_SERVER */
|
||||
static inline void
|
||||
frankenphp_register_variable_from_request_info(zend_string *zKey, char *value,
|
||||
bool must_be_present,
|
||||
zval *track_vars_array) {
|
||||
@@ -895,23 +897,31 @@ frankenphp_register_variable_from_request_info(zend_string *zKey, char *value,
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
static void
|
||||
frankenphp_register_variables_from_request_info(zval *track_vars_array) {
|
||||
frankenphp_register_variable_from_request_info(
|
||||
content_type, (char *)SG(request_info).content_type, true,
|
||||
frankenphp_strings.content_type, (char *)SG(request_info).content_type,
|
||||
true, track_vars_array);
|
||||
frankenphp_register_variable_from_request_info(
|
||||
frankenphp_strings.path_translated,
|
||||
(char *)SG(request_info).path_translated, false, track_vars_array);
|
||||
frankenphp_register_variable_from_request_info(
|
||||
frankenphp_strings.query_string, SG(request_info).query_string, true,
|
||||
track_vars_array);
|
||||
frankenphp_register_variable_from_request_info(
|
||||
path_translated, (char *)SG(request_info).path_translated, false,
|
||||
frankenphp_strings.remote_user, (char *)SG(request_info).auth_user, 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);
|
||||
frankenphp_strings.request_method,
|
||||
(char *)SG(request_info).request_method, false, track_vars_array);
|
||||
}
|
||||
|
||||
/* Only hard-coded keys may be registered this way */
|
||||
void frankenphp_register_known_variable(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
|
||||
@@ -950,7 +960,11 @@ static void frankenphp_register_variables(zval *track_vars_array) {
|
||||
*/
|
||||
zend_hash_copy(Z_ARR_P(track_vars_array), main_thread_env, NULL);
|
||||
|
||||
go_register_variables(thread_index, track_vars_array);
|
||||
/* import CGI variables from the request context in go */
|
||||
go_register_server_variables(thread_index, track_vars_array);
|
||||
|
||||
/* Some variables are already present in SG(request_info) */
|
||||
frankenphp_register_variables_from_request_info(track_vars_array);
|
||||
}
|
||||
|
||||
static void frankenphp_log_message(const char *message, int syslog_type_int) {
|
||||
@@ -1119,6 +1133,8 @@ static void *php_main(void *arg) {
|
||||
frankenphp_sapi_module.ini_entries = php_ini_overrides;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -500,11 +500,11 @@ func go_apache_request_headers(threadIndex C.uintptr_t) (*C.go_string, C.size_t)
|
||||
return sd, C.size_t(len(fc.request.Header))
|
||||
}
|
||||
|
||||
func addHeader(ctx context.Context, fc *frankenPHPContext, cString *C.char, length C.int) {
|
||||
key, val := splitRawHeader(cString, int(length))
|
||||
func addHeader(ctx context.Context, fc *frankenPHPContext, h *C.sapi_header_struct) {
|
||||
key, val := splitRawHeader(h.header, int(h.header_len))
|
||||
if key == "" {
|
||||
if fc.logger.Enabled(ctx, slog.LevelDebug) {
|
||||
fc.logger.LogAttrs(ctx, slog.LevelDebug, "invalid header", slog.String("header", C.GoStringN(cString, length)))
|
||||
fc.logger.LogAttrs(ctx, slog.LevelDebug, "invalid header", slog.String("header", C.GoStringN(h.header, C.int(h.header_len))))
|
||||
}
|
||||
|
||||
return
|
||||
@@ -564,7 +564,7 @@ func go_write_headers(threadIndex C.uintptr_t, status C.int, headers *C.zend_lli
|
||||
for current != nil {
|
||||
h := (*C.sapi_header_struct)(unsafe.Pointer(&(current.data)))
|
||||
|
||||
addHeader(thread.context(), fc, h.header, C.int(h.header_len))
|
||||
addHeader(thread.context(), fc, h)
|
||||
current = current.next
|
||||
}
|
||||
|
||||
|
||||
120
frankenphp.h
120
frankenphp.h
@@ -57,11 +57,96 @@ 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 *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;
|
||||
zend_string *request_scheme;
|
||||
zend_string *ssl_protocol;
|
||||
zend_string *https;
|
||||
} frankenphp_server_vars;
|
||||
|
||||
/**
|
||||
* Cached interned strings for memory and performance benefits
|
||||
* Add more hard-coded strings here if needed
|
||||
*/
|
||||
#define FRANKENPHP_INTERNED_STRINGS_LIST(X) \
|
||||
X(remote_addr, "REMOTE_ADDR") \
|
||||
X(remote_host, "REMOTE_HOST") \
|
||||
X(remote_port, "REMOTE_PORT") \
|
||||
X(document_root, "DOCUMENT_ROOT") \
|
||||
X(path_info, "PATH_INFO") \
|
||||
X(php_self, "PHP_SELF") \
|
||||
X(document_uri, "DOCUMENT_URI") \
|
||||
X(script_filename, "SCRIPT_FILENAME") \
|
||||
X(script_name, "SCRIPT_NAME") \
|
||||
X(https, "HTTPS") \
|
||||
X(httpsLowercase, "https") \
|
||||
X(httpLowercase, "http") \
|
||||
X(ssl_protocol, "SSL_PROTOCOL") \
|
||||
X(request_scheme, "REQUEST_SCHEME") \
|
||||
X(server_name, "SERVER_NAME") \
|
||||
X(server_port, "SERVER_PORT") \
|
||||
X(content_length, "CONTENT_LENGTH") \
|
||||
X(server_protocol, "SERVER_PROTOCOL") \
|
||||
X(http_host, "HTTP_HOST") \
|
||||
X(request_uri, "REQUEST_URI") \
|
||||
X(ssl_cipher, "SSL_CIPHER") \
|
||||
X(server_software, "SERVER_SOFTWARE") \
|
||||
X(frankenphp, "FrankenPHP") \
|
||||
X(gateway_interface, "GATEWAY_INTERFACE") \
|
||||
X(cgi11, "CGI/1.1") \
|
||||
X(auth_type, "AUTH_TYPE") \
|
||||
X(remote_ident, "REMOTE_IDENT") \
|
||||
X(content_type, "CONTENT_TYPE") \
|
||||
X(path_translated, "PATH_TRANSLATED") \
|
||||
X(query_string, "QUERY_STRING") \
|
||||
X(remote_user, "REMOTE_USER") \
|
||||
X(request_method, "REQUEST_METHOD") \
|
||||
X(tls1, "TLSv1") \
|
||||
X(tls11, "TLSv1.1") \
|
||||
X(tls12, "TLSv1.2") \
|
||||
X(tls13, "TLSv1.3") \
|
||||
X(on, "on") \
|
||||
X(empty, "")
|
||||
|
||||
typedef struct frankenphp_interned_strings_t {
|
||||
#define F_DEFINE_STRUCT_FIELD(name, str) zend_string *name;
|
||||
FRANKENPHP_INTERNED_STRINGS_LIST(F_DEFINE_STRUCT_FIELD)
|
||||
#undef F_DEFINE_STRUCT_FIELD
|
||||
} frankenphp_interned_strings_t;
|
||||
|
||||
extern frankenphp_interned_strings_t frankenphp_strings;
|
||||
|
||||
typedef struct frankenphp_version {
|
||||
unsigned char major_version;
|
||||
@@ -90,32 +175,17 @@ 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_known_variable(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_server_vars(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
|
||||
|
||||
@@ -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 "frankenphp.h"
|
||||
// #include <php_variables.h>
|
||||
import "C"
|
||||
@@ -20,18 +18,17 @@ 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
|
||||
state *state.ThreadState
|
||||
done chan struct{}
|
||||
numThreads int
|
||||
maxThreads int
|
||||
phpIni map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
phpThreads []*phpThread
|
||||
mainThread *phpMainThread
|
||||
phpThreads []*phpThread
|
||||
mainThread *phpMainThread
|
||||
commonHeaders map[string]*C.zend_string
|
||||
)
|
||||
|
||||
// initPHPThreads starts the main PHP thread,
|
||||
@@ -111,15 +108,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] = newPersistentZendString(phpKey)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user