feat: add go_apache_request_headers()

This commit is contained in:
Kévin Dunglas
2024-01-22 13:23:37 +01:00
parent a92d774742
commit 49baf02035
7 changed files with 96 additions and 2 deletions

View File

@@ -242,6 +242,29 @@ PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */
RETURN_TRUE;
} /* }}} */
/* {{{ Fetch all HTTP request headers */
PHP_FUNCTION(apache_request_headers) {
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
frankenphp_server_context *ctx = SG(server_context);
struct go_apache_request_headers_return headers =
go_apache_request_headers(ctx->current_request);
array_init_size(return_value, headers.r1);
for (size_t i = 0; i < headers.r1; i++) {
go_string key = headers.r0[i * 2];
go_string val = headers.r0[i * 2 + 1];
add_assoc_stringl_ex(return_value, key.data, key.len, val.data, val.len);
}
free(headers.r0);
}
/* }}} */
PHP_FUNCTION(frankenphp_handle_request) {
zend_fcall_info fci;
zend_fcall_info_cache fcc;

View File

@@ -581,6 +581,27 @@ func go_register_variables(rh C.uintptr_t, trackVarsArray *C.zval) {
fc.env = nil
}
//export go_apache_request_headers
func go_apache_request_headers(rh C.uintptr_t) (*C.go_string, C.size_t) {
r := cgo.Handle(rh).Value().(*http.Request)
rl := len(r.Header)
scs := unsafe.Sizeof(C.go_string{})
headers := (*C.go_string)(unsafe.Pointer(C.malloc(C.size_t(rl*2) * (C.size_t)(scs))))
header := headers
for field, val := range r.Header {
*header = C.go_string{C.size_t(len(field)), (*C.char)(unsafe.Pointer(unsafe.StringData(field)))}
header = (*C.go_string)(unsafe.Add(unsafe.Pointer(header), scs))
cv := strings.Join(val, ", ")
*header = C.go_string{C.size_t(len(cv)), (*C.char)(unsafe.Pointer(unsafe.StringData(cv)))}
header = (*C.go_string)(unsafe.Add(unsafe.Pointer(header), scs))
}
return headers, C.size_t(rl)
}
func addHeader(fc *FrankenPHPContext, cString *C.char, length C.int) {
parts := strings.SplitN(C.GoStringN(cString, length), ": ", 2)
if len(parts) != 2 {

View File

@@ -11,6 +11,11 @@
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
typedef struct go_string {
size_t len;
const char *data;
} go_string;
typedef struct frankenphp_version {
unsigned char major_version;
unsigned char minor_version;

View File

@@ -12,3 +12,10 @@ function frankenphp_finish_request(): bool {}
* @alias frankenphp_finish_request
*/
function fastcgi_finish_request(): bool {}
function apache_request_headers(): array {}
/**
* @alias apache_request_headers
*/
function getallheaders(): array {}

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: de4dc4063fafd8c933e3068c8349889a7ece5f03 */
* Stub hash: f925a1c280fb955eb32d0823cbd4f360b0cbabed */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_handle_request, 0, 1,
_IS_BOOL, 0)
@@ -16,13 +16,23 @@ ZEND_END_ARG_INFO()
#define arginfo_fastcgi_finish_request arginfo_frankenphp_finish_request
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_apache_request_headers, 0, 0,
IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_getallheaders arginfo_apache_request_headers
ZEND_FUNCTION(frankenphp_handle_request);
ZEND_FUNCTION(headers_send);
ZEND_FUNCTION(frankenphp_finish_request);
ZEND_FUNCTION(apache_request_headers);
static const zend_function_entry ext_functions[] = {
ZEND_FE(frankenphp_handle_request, arginfo_frankenphp_handle_request)
ZEND_FE(headers_send, arginfo_headers_send) ZEND_FE(
frankenphp_finish_request, arginfo_frankenphp_finish_request)
ZEND_FALIAS(fastcgi_finish_request, frankenphp_finish_request,
arginfo_fastcgi_finish_request) ZEND_FE_END};
arginfo_fastcgi_finish_request)
ZEND_FE(apache_request_headers, arginfo_apache_request_headers)
ZEND_FALIAS(getallheaders, apache_request_headers,
arginfo_getallheaders) ZEND_FE_END};

View File

@@ -553,6 +553,27 @@ func testFiberNoCgo(t *testing.T, opts *testOptions) {
}, opts)
}
func TestApacheRequestHeaders_module(t *testing.T) { testApacheRequestHeaders(t, &testOptions{}) }
func TestApacheRequestHeaders_worker(t *testing.T) {
testApacheRequestHeaders(t, &testOptions{workerScript: "apache-request-headers.php"})
}
func testApacheRequestHeaders(t *testing.T, opts *testOptions) {
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
req := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/apache-request-headers.php?i=%d", i), nil)
req.Header.Add("Content-Type", "text/plain")
req.Header.Add("Frankenphp-I", strconv.Itoa(i))
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
body, _ := io.ReadAll(resp.Body)
assert.Contains(t, string(body), "[Content-Type] => text/plain")
assert.Contains(t, string(body), fmt.Sprintf("[Frankenphp-I] => %d", i))
}, opts)
}
func TestExecuteScriptCLI(t *testing.T) {
if _, err := os.Stat("internal/testcli/testcli"); err != nil {
t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`")

7
testdata/apache-request-headers.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
require_once __DIR__.'/_executor.php';
return function() {
print_r(apache_request_headers());
};