Просмотр исходного кода

feat: add apache_response_headers() function

Kévin Dunglas 1 год назад
Родитель
Сommit
7da5d2ff90
5 измененных файлов с 120 добавлено и 35 удалено
  1. 51 3
      frankenphp.c
  2. 14 1
      frankenphp.stub.php
  3. 29 19
      frankenphp_arginfo.h
  4. 26 5
      frankenphp_test.go
  5. 0 7
      testdata/apache-request-headers.php

+ 51 - 3
frankenphp.c

@@ -243,7 +243,7 @@ PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */
 } /* }}} */
 
 /* {{{ Fetch all HTTP request headers */
-PHP_FUNCTION(apache_request_headers) {
+PHP_FUNCTION(frankenphp_request_headers) {
   if (zend_parse_parameters_none() == FAILURE) {
     RETURN_THROWS();
   }
@@ -265,6 +265,51 @@ PHP_FUNCTION(apache_request_headers) {
 }
 /* }}} */
 
+// add_response_header and apache_response_headers are copied from https://github.com/php/php-src/blob/master/sapi/cli/php_cli_server.c
+// Copyright (c) The PHP Group
+// Licensed under The PHP License
+// Original authors: Moriyoshi Koizumi <moriyoshi@php.net> and Xinchen Hui <laruence@php.net>    
+static void add_response_header(sapi_header_struct *h, zval *return_value) /* {{{ */
+{
+	if (h->header_len > 0) {
+		char *s;
+		size_t len = 0;
+		ALLOCA_FLAG(use_heap)
+
+		char *p = strchr(h->header, ':');
+		if (NULL != p) {
+			len = p - h->header;
+		}
+		if (len > 0) {
+			while (len != 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) {
+				len--;
+			}
+			if (len) {
+				s = do_alloca(len + 1, use_heap);
+				memcpy(s, h->header, len);
+				s[len] = 0;
+				do {
+					p++;
+				} while (*p == ' ' || *p == '\t');
+				add_assoc_stringl_ex(return_value, s, len, p, h->header_len - (p - h->header));
+				free_alloca(s, use_heap);
+			}
+		}
+	}
+}
+/* }}} */
+
+PHP_FUNCTION(frankenphp_response_headers) /* {{{ */
+{
+	if (zend_parse_parameters_none() == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	array_init(return_value);
+	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value);
+}
+/* }}} */
+
 PHP_FUNCTION(frankenphp_handle_request) {
   zend_fcall_info fci;
   zend_fcall_info_cache fcc;
@@ -784,8 +829,11 @@ static char *cli_script;
 static int cli_argc;
 static char **cli_argv;
 
-// Adapted from https://github.com/php/php-src/sapi/cli/php_cli.c (The PHP
-// Group, The PHP License)
+// CLI code is adapted from https://github.com/php/php-src/blob/master/sapi/cli/php_cli.c
+// Copyright (c) The PHP Group
+// Licensed under The PHP License
+// Original uthors: Edin Kadribasic <edink@php.net>, Marcus Boerger <helly@php.net> and Johannes Schlueter <johannes@php.net>
+// Parts based on CGI SAPI Module by Rasmus Lerdorf, Stig Bakken and Zeev Suraski
 static void cli_register_file_handles(bool no_close) /* {{{ */
 {
   php_stream *s_in, *s_out, *s_err;

+ 14 - 1
frankenphp.stub.php

@@ -13,9 +13,22 @@ function frankenphp_finish_request(): bool {}
  */
 function fastcgi_finish_request(): bool {}
 
+function frankenphp_request_headers(): array {}
+
+/**
+ * @alias frankenphp_request_headers
+ */
 function apache_request_headers(): array {}
 
 /**
- * @alias apache_request_headers
+ * @alias frankenphp_response_headers
 */
 function getallheaders(): array {}
+
+function frankenphp_response_headers(): array|bool {}
+
+/**
+ * @alias frankenphp_response_headers
+ */
+function apache_response_headers(): array|bool {}
+

+ 29 - 19
frankenphp_arginfo.h

@@ -1,38 +1,48 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f925a1c280fb955eb32d0823cbd4f360b0cbabed */
+ * Stub hash: 467f1406e17d3b8ca67bba5ea367194e60d8dd27 */
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_handle_request, 0, 1,
-                                        _IS_BOOL, 0)
-ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_handle_request, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_headers_send, 0, 0, IS_LONG, 0)
-ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, status, IS_LONG, 0, "200")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, status, IS_LONG, 0, "200")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_finish_request, 0, 0,
-                                        _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_finish_request, 0, 0, _IS_BOOL, 0)
 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_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_request_headers, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_getallheaders arginfo_apache_request_headers
+#define arginfo_apache_request_headers arginfo_frankenphp_request_headers
+
+#define arginfo_getallheaders arginfo_frankenphp_request_headers
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_frankenphp_response_headers, 0, 0, MAY_BE_ARRAY|MAY_BE_BOOL)
+ZEND_END_ARG_INFO()
+
+#define arginfo_apache_response_headers arginfo_frankenphp_response_headers
+
 
 ZEND_FUNCTION(frankenphp_handle_request);
 ZEND_FUNCTION(headers_send);
 ZEND_FUNCTION(frankenphp_finish_request);
-ZEND_FUNCTION(apache_request_headers);
+ZEND_FUNCTION(frankenphp_request_headers);
+ZEND_FUNCTION(frankenphp_response_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(apache_request_headers, arginfo_apache_request_headers)
-                    ZEND_FALIAS(getallheaders, apache_request_headers,
-                                arginfo_getallheaders) ZEND_FE_END};
+	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(frankenphp_request_headers, arginfo_frankenphp_request_headers)
+	ZEND_FALIAS(apache_request_headers, frankenphp_request_headers, arginfo_apache_request_headers)
+	ZEND_FALIAS(getallheaders, frankenphp_response_headers, arginfo_getallheaders)
+	ZEND_FE(frankenphp_response_headers, arginfo_frankenphp_response_headers)
+	ZEND_FALIAS(apache_response_headers, frankenphp_response_headers, arginfo_apache_response_headers)
+	ZEND_FE_END
+};

+ 26 - 5
frankenphp_test.go

@@ -226,6 +226,27 @@ func testHeaders(t *testing.T, opts *testOptions) {
 	}, opts)
 }
 
+func TestResponseHeaders_module(t *testing.T) { testResponseHeaders(t, nil) }
+func TestResponseHeaders_worker(t *testing.T) {
+	testResponseHeaders(t, &testOptions{workerScript: "response-headers.php"})
+}
+func testResponseHeaders(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/response-headers.php?i=%d", i), nil)
+		w := httptest.NewRecorder()
+		handler(w, req)
+
+		resp := w.Result()
+		body, _ := io.ReadAll(resp.Body)
+
+		assert.Contains(t, string(body), "'X-Powered-By' => 'PH")
+		assert.Contains(t, string(body), "'Foo' => 'bar',")
+		assert.Contains(t, string(body), "'Foo2' => 'bar2',")
+		assert.Contains(t, string(body), fmt.Sprintf("'I' => '%d',", i))
+		assert.NotContains(t, string(body), "Invalid")
+	}, opts)
+}
+
 func TestInput_module(t *testing.T) { testInput(t, nil) }
 func TestInput_worker(t *testing.T) { testInput(t, &testOptions{workerScript: "input.php"}) }
 func testInput(t *testing.T, opts *testOptions) {
@@ -553,13 +574,13 @@ 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 TestRequestHeaders_module(t *testing.T) { testRequestHeaders(t, &testOptions{}) }
+func TestRequestHeaders_worker(t *testing.T) {
+	testRequestHeaders(t, &testOptions{workerScript: "request-headers.php"})
 }
-func testApacheRequestHeaders(t *testing.T, opts *testOptions) {
+func testRequestHeaders(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 := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/request-headers.php?i=%d", i), nil)
 		req.Header.Add("Content-Type", "text/plain")
 		req.Header.Add("Frankenphp-I", strconv.Itoa(i))
 

+ 0 - 7
testdata/apache-request-headers.php

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