Skip to content

Commit

Permalink
feat: add apache_response_headers() function (#530)
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Jan 31, 2024
1 parent 3d9f344 commit 5a8e5f9
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 17 deletions.
62 changes: 59 additions & 3 deletions frankenphp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -265,6 +265,58 @@ 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;
Expand Down Expand Up @@ -784,8 +836,12 @@ 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;
Expand Down
15 changes: 14 additions & 1 deletion frankenphp.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

35 changes: 27 additions & 8 deletions frankenphp_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* 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)
Expand All @@ -16,23 +16,42 @@ 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_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};
31 changes: 26 additions & 5 deletions frankenphp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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))

Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions testdata/response-headers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

require_once __DIR__.'/_executor.php';

return function () {
header('Foo: bar');
header('Foo2: bar2');
header('Invalid');
header('I: ' . ($_GET['i'] ?? 'i not set'));
http_response_code(201);

var_export(apache_response_headers());
};

0 comments on commit 5a8e5f9

Please sign in to comment.