Browse Source

feat: add go_apache_request_headers()

Kévin Dunglas 1 year ago
parent
commit
49baf02035
7 changed files with 96 additions and 2 deletions
  1. 23 0
      frankenphp.c
  2. 21 0
      frankenphp.go
  3. 5 0
      frankenphp.h
  4. 7 0
      frankenphp.stub.php
  5. 12 2
      frankenphp_arginfo.h
  6. 21 0
      frankenphp_test.go
  7. 7 0
      testdata/apache-request-headers.php

+ 23 - 0
frankenphp.c

@@ -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;

+ 21 - 0
frankenphp.go

@@ -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 {

+ 5 - 0
frankenphp.h

@@ -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;

+ 7 - 0
frankenphp.stub.php

@@ -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 {}

+ 12 - 2
frankenphp_arginfo.h

@@ -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};

+ 21 - 0
frankenphp_test.go

@@ -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 - 0
testdata/apache-request-headers.php

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