MFB: Added header_remove() (chsc at peytz dotdk, Arnaud)

This commit is contained in:
Arnaud Le Blanc 2008-11-13 10:33:08 +00:00
parent f24bc6f421
commit 047870a61f
9 changed files with 300 additions and 71 deletions

View file

@ -1678,6 +1678,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_header, 0, 0, 1)
ZEND_ARG_INFO(0, http_response_code) ZEND_ARG_INFO(0, http_response_code)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_header_remove, 0, 0, 0)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
static static
ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie, 0, 0, 1)
ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, name)
@ -3479,6 +3484,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FE(setcookie, arginfo_setcookie) PHP_FE(setcookie, arginfo_setcookie)
PHP_FE(setrawcookie, arginfo_setrawcookie) PHP_FE(setrawcookie, arginfo_setrawcookie)
PHP_FE(header, arginfo_header) PHP_FE(header, arginfo_header)
PHP_FE(header_remove, arginfo_header_remove)
PHP_FE(headers_sent, arginfo_headers_sent) PHP_FE(headers_sent, arginfo_headers_sent)
PHP_FE(headers_list, arginfo_headers_list) PHP_FE(headers_list, arginfo_headers_list)

View file

@ -50,6 +50,20 @@ PHP_FUNCTION(header)
} }
/* }}} */ /* }}} */
/* {{{ proto void header_remove([string name]) U
Removes an HTTP header previously set using header() */
PHP_FUNCTION(header_remove)
{
sapi_header_line ctr = {0};
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s&", &ctr.line,
&ctr.line_len, UG(ascii_conv)) == FAILURE)
return;
sapi_header_op(ZEND_NUM_ARGS() == 0 ? SAPI_HEADER_DELETE_ALL : SAPI_HEADER_DELETE, &ctr TSRMLS_CC);
}
/* }}} */
PHPAPI int php_header(TSRMLS_D) /* {{{ */ PHPAPI int php_header(TSRMLS_D) /* {{{ */
{ {
if (sapi_send_headers(TSRMLS_C)==FAILURE || SG(request_info).headers_only) { if (sapi_send_headers(TSRMLS_C)==FAILURE || SG(request_info).headers_only) {

View file

@ -23,6 +23,7 @@
extern PHP_RINIT_FUNCTION(head); extern PHP_RINIT_FUNCTION(head);
PHP_FUNCTION(header); PHP_FUNCTION(header);
PHP_FUNCTION(header_remove);
PHP_FUNCTION(setcookie); PHP_FUNCTION(setcookie);
PHP_FUNCTION(setrawcookie); PHP_FUNCTION(setrawcookie);
PHP_FUNCTION(headers_sent); PHP_FUNCTION(headers_sent);

View file

@ -520,7 +520,8 @@ static void sapi_update_response_code(int ncode TSRMLS_DC)
static int sapi_find_matching_header(void *element1, void *element2) static int sapi_find_matching_header(void *element1, void *element2)
{ {
return strncasecmp(((sapi_header_struct*)element1)->header, (char*)element2, strlen((char*)element2)) == 0; int len = strlen((char*)element2);
return strncasecmp(((sapi_header_struct*)element1)->header, (char*)element2, len) == 0 && ((sapi_header_struct*)element1)->header[len] == ':';
} }
SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC) SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC)
@ -548,7 +549,6 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
long myuid = 0L; long myuid = 0L;
char *header_line; char *header_line;
uint header_line_len; uint header_line_len;
zend_bool replace;
int http_response_code; int http_response_code;
if (SG(headers_sent) && !SG(request_info).no_headers) { if (SG(headers_sent) && !SG(request_info).no_headers) {
@ -569,8 +569,9 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
sapi_update_response_code((int)(zend_intptr_t) arg TSRMLS_CC); sapi_update_response_code((int)(zend_intptr_t) arg TSRMLS_CC);
return SUCCESS; return SUCCESS;
case SAPI_HEADER_ADD:
case SAPI_HEADER_REPLACE: case SAPI_HEADER_REPLACE:
case SAPI_HEADER_ADD: { case SAPI_HEADER_DELETE: {
sapi_header_line *p = arg; sapi_header_line *p = arg;
if (!p->line || !p->line_len) { if (!p->line || !p->line_len) {
@ -579,10 +580,16 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
header_line = p->line; header_line = p->line;
header_line_len = p->line_len; header_line_len = p->line_len;
http_response_code = p->response_code; http_response_code = p->response_code;
replace = (op == SAPI_HEADER_REPLACE);
break; break;
} }
case SAPI_HEADER_DELETE_ALL:
if (sapi_module.header_handler) {
sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC);
}
zend_llist_clean(&SG(sapi_headers).headers);
return SUCCESS;
default: default:
return FAILURE; return FAILURE;
} }
@ -593,8 +600,14 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
while(header_line_len && isspace(header_line[header_line_len-1])) while(header_line_len && isspace(header_line[header_line_len-1]))
header_line[--header_line_len]='\0'; header_line[--header_line_len]='\0';
/* new line safety check */ if (op == SAPI_HEADER_DELETE) {
{ if (strchr(header_line, ':')) {
efree(header_line);
sapi_module.sapi_error(E_WARNING, "Header to delete may not contain colon.");
return FAILURE;
}
} else {
/* new line safety check */
char *s = header_line, *e = header_line + header_line_len, *p; char *s = header_line, *e = header_line + header_line_len, *p;
while (s < e && (p = memchr(s, '\n', (e - s)))) { while (s < e && (p = memchr(s, '\n', (e - s)))) {
if (*(p + 1) == ' ' || *(p + 1) == '\t') { if (*(p + 1) == ' ' || *(p + 1) == '\t') {
@ -609,7 +622,15 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
sapi_header.header = header_line; sapi_header.header = header_line;
sapi_header.header_len = header_line_len; sapi_header.header_len = header_line_len;
sapi_header.replace = replace;
if (op == SAPI_HEADER_DELETE) {
if (sapi_module.header_handler) {
sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC);
}
zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header);
sapi_free_header(&sapi_header);
return SUCCESS;
}
/* Check the header for a few cases that we have special support for in SAPI */ /* Check the header for a few cases that we have special support for in SAPI */
if (header_line_len>=5 if (header_line_len>=5
@ -688,20 +709,16 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
sapi_update_response_code(http_response_code TSRMLS_CC); sapi_update_response_code(http_response_code TSRMLS_CC);
} }
if (sapi_module.header_handler) { if (sapi_module.header_handler) {
retval = sapi_module.header_handler(&sapi_header, &SG(sapi_headers) TSRMLS_CC); retval = sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC);
} else { } else {
retval = SAPI_HEADER_ADD; retval = SAPI_HEADER_ADD;
} }
if (retval & SAPI_HEADER_DELETE_ALL) {
zend_llist_clean(&SG(sapi_headers).headers);
}
if (retval & SAPI_HEADER_ADD) { if (retval & SAPI_HEADER_ADD) {
/* in replace mode first remove the header if it already exists in the headers llist */ /* in replace mode first remove the header if it already exists in the headers llist */
if (replace) { if (op == SAPI_HEADER_REPLACE) {
colon_offset = strchr(sapi_header.header, ':'); colon_offset = strchr(sapi_header.header, ':');
if (colon_offset) { if (colon_offset) {
char sav; char sav;
colon_offset++;
sav = *colon_offset; sav = *colon_offset;
*colon_offset = 0; *colon_offset = 0;
zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header); zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header);
@ -710,6 +727,8 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
} }
zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header);
} else {
sapi_free_header(&sapi_header);
} }
return SUCCESS; return SUCCESS;
} }

View file

@ -50,7 +50,6 @@
typedef struct { typedef struct {
char *header; char *header;
uint header_len; uint header_len;
zend_bool replace;
} sapi_header_struct; } sapi_header_struct;
@ -170,6 +169,8 @@ typedef struct {
typedef enum { /* Parameter: */ typedef enum { /* Parameter: */
SAPI_HEADER_REPLACE, /* sapi_header_line* */ SAPI_HEADER_REPLACE, /* sapi_header_line* */
SAPI_HEADER_ADD, /* sapi_header_line* */ SAPI_HEADER_ADD, /* sapi_header_line* */
SAPI_HEADER_DELETE, /* sapi_header_line* */
SAPI_HEADER_DELETE_ALL, /* void */
SAPI_HEADER_SET_STATUS /* int */ SAPI_HEADER_SET_STATUS /* int */
} sapi_header_op_enum; } sapi_header_op_enum;
@ -228,7 +229,7 @@ struct _sapi_module_struct {
void (*sapi_error)(int type, const char *error_msg, ...); void (*sapi_error)(int type, const char *error_msg, ...);
int (*header_handler)(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC); int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC); int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC); void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);
@ -277,8 +278,6 @@ struct _sapi_post_entry {
/* header_handler() constants */ /* header_handler() constants */
#define SAPI_HEADER_ADD (1<<0) #define SAPI_HEADER_ADD (1<<0)
#define SAPI_HEADER_DELETE_ALL (1<<1)
#define SAPI_HEADER_SEND_NOW (1<<2)
#define SAPI_HEADER_SENT_SUCCESSFULLY 1 #define SAPI_HEADER_SENT_SUCCESSFULLY 1

View file

@ -35,7 +35,7 @@ static void php_save_umask(void);
static void php_restore_umask(void); static void php_restore_umask(void);
static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC); static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC);
static char *sapi_apache_read_cookies(TSRMLS_D); static char *sapi_apache_read_cookies(TSRMLS_D);
static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC); static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
static int send_php(request_rec *r, int display_source_mode, char *filename); static int send_php(request_rec *r, int display_source_mode, char *filename);
static int send_parsed_php(request_rec * r); static int send_parsed_php(request_rec * r);
@ -163,41 +163,54 @@ static char *sapi_apache_read_cookies(TSRMLS_D)
/* {{{ sapi_apache_header_handler /* {{{ sapi_apache_header_handler
*/ */
static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC) static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
{ {
char *header_name, *header_content, *p; char *header_name, *header_content, *p;
request_rec *r = (request_rec *) SG(server_context); request_rec *r = (request_rec *) SG(server_context);
if(!r) { if(!r) {
efree(sapi_header->header);
return 0; return 0;
} }
header_name = sapi_header->header; switch(op) {
case SAPI_HEADER_DELETE_ALL:
clear_table(r->headers_out);
return 0;
header_content = p = strchr(header_name, ':'); case SAPI_HEADER_DELETE:
if (!p) { table_unset(r->headers_out, sapi_header->header);
efree(sapi_header->header); return 0;
return 0;
case SAPI_HEADER_ADD:
case SAPI_HEADER_REPLACE:
header_name = sapi_header->header;
header_content = p = strchr(header_name, ':');
if (!p) {
return 0;
}
*p = 0;
do {
header_content++;
} while (*header_content==' ');
if (!strcasecmp(header_name, "Content-Type")) {
r->content_type = pstrdup(r->pool, header_content);
} else if (!strcasecmp(header_name, "Set-Cookie")) {
table_add(r->headers_out, header_name, header_content);
} else if (op == SAPI_HEADER_REPLACE) {
table_set(r->headers_out, header_name, header_content);
} else {
table_add(r->headers_out, header_name, header_content);
}
*p = ':'; /* a well behaved header handler shouldn't change its original arguments */
return SAPI_HEADER_ADD;
default:
return 0;
} }
*p = 0;
do {
header_content++;
} while (*header_content==' ');
if (!strcasecmp(header_name, "Content-Type")) {
r->content_type = pstrdup(r->pool, header_content);
} else if (!strcasecmp(header_name, "Set-Cookie")) {
table_add(r->headers_out, header_name, header_content);
} else if (sapi_header->replace) {
table_set(r->headers_out, header_name, header_content);
} else {
table_add(r->headers_out, header_name, header_content);
}
*p = ':'; /* a well behaved header handler shouldn't change its original arguments */
return SAPI_HEADER_ADD;
} }
/* }}} */ /* }}} */

View file

@ -83,40 +83,55 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
} }
static int static int
php_apache_sapi_header_handler(sapi_header_struct *sapi_header,sapi_headers_struct *sapi_headers TSRMLS_DC) php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
{ {
php_struct *ctx; php_struct *ctx;
char *val, *ptr; char *val, *ptr;
ctx = SG(server_context); ctx = SG(server_context);
val = strchr(sapi_header->header, ':'); switch (op) {
case SAPI_HEADER_DELETE:
apr_table_unset(ctx->r->headers_out, sapi_header->header);
return 0;
if (!val) { case SAPI_HEADER_DELETE_ALL:
sapi_free_header(sapi_header); apr_table_clear(ctx->r->headers_out);
return 0; return 0;
case SAPI_HEADER_ADD:
case SAPI_HEADER_REPLACE:
val = strchr(sapi_header->header, ':');
if (!val) {
return 0;
}
ptr = val;
*val = '\0';
do {
val++;
} while (*val == ' ');
if (!strcasecmp(sapi_header->header, "content-type")) {
if (ctx->content_type) {
efree(ctx->content_type);
}
ctx->content_type = estrdup(val);
} else if (op == SAPI_HEADER_REPLACE) {
apr_table_set(ctx->r->headers_out, sapi_header->header, val);
} else {
apr_table_add(ctx->r->headers_out, sapi_header->header, val);
}
*ptr = ':';
return SAPI_HEADER_ADD;
default:
return 0;
} }
ptr = val;
*val = '\0';
do {
val++;
} while (*val == ' ');
if (!strcasecmp(sapi_header->header, "content-type")) {
if (ctx->content_type) {
efree(ctx->content_type);
}
ctx->content_type = estrdup(val);
} else if (sapi_header->replace) {
apr_table_set(ctx->r->headers_out, sapi_header->header, val);
} else {
apr_table_add(ctx->r->headers_out, sapi_header->header, val);
}
*ptr = ':';
return SAPI_HEADER_ADD;
} }
static int static int

165
sapi/cgi/tests/011.phpt Normal file
View file

@ -0,0 +1,165 @@
--TEST--
header_remove()
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
include "include.inc";
$php = get_cgi_path();
reset_env_vars();
$f = tempnam(sys_get_temp_dir(), 'cgitest');
function test($script) {
file_put_contents($GLOBALS['f'], $script);
$cmd = escapeshellcmd($GLOBALS['php']);
$cmd .= ' -n -dreport_zend_debug=0 -dhtml_errors=0 ' . escapeshellarg($GLOBALS['f']);
echo "----------\n";
echo rtrim($script) . "\n";
echo "----------\n";
passthru($cmd);
}
test('<?php ?>');
test('<?php header_remove(); ?>');
test('<?php header_remove("X-Foo"); ?>');
test('<?php
header("X-Foo: Bar");
?>');
test('<?php
header("X-Foo: Bar");
header("X-Bar: Baz");
header_remove("X-Foo");
?>');
test('<?php
header("X-Foo: Bar");
header_remove("X-Foo: Bar");
?>');
test('<?php
header("X-Foo: Bar");
header_remove("X-Foo:");
?>');
test('<?php
header("X-Foo: Bar");
header_remove();
?>');
test('<?php
header_remove("");
?>');
test('<?php
header_remove(":");
?>');
test('<?php
header("X-Foo: Bar");
echo "flush\n";
flush();
header_remove("X-Foo");
?>');
@unlink($f);
?>
--EXPECTF--
----------
<?php ?>
----------
X-Powered-By: PHP/%s
Content-type: %s
----------
<?php header_remove(); ?>
----------
Content-type: %s
----------
<?php header_remove("X-Foo"); ?>
----------
X-Powered-By: PHP/%s
Content-type: %s
----------
<?php
header("X-Foo: Bar");
?>
----------
X-Powered-By: PHP/%s
X-Foo: Bar
Content-type: %s
----------
<?php
header("X-Foo: Bar");
header("X-Bar: Baz");
header_remove("X-Foo");
?>
----------
X-Powered-By: PHP/%s
X-Bar: Baz
Content-type: %s
----------
<?php
header("X-Foo: Bar");
header_remove("X-Foo: Bar");
?>
----------
X-Powered-By: PHP/%s
X-Foo: Bar
Content-type: %s
Warning: Header to delete may not contain colon. in %s on line 3
----------
<?php
header("X-Foo: Bar");
header_remove("X-Foo:");
?>
----------
X-Powered-By: PHP/%s
X-Foo: Bar
Content-type: %s
Warning: Header to delete may not contain colon. in %s on line 3
----------
<?php
header("X-Foo: Bar");
header_remove();
?>
----------
Content-type: %s
----------
<?php
header_remove("");
?>
----------
X-Powered-By: PHP/%s
Content-type: %s
----------
<?php
header_remove(":");
?>
----------
X-Powered-By: PHP/%s
Content-type: %s
Warning: Header to delete may not contain colon. in %s on line 2
----------
<?php
header("X-Foo: Bar");
echo "flush\n";
flush();
header_remove("X-Foo");
?>
----------
X-Powered-By: PHP/%s
X-Foo: Bar
Content-type: %s
flush
Warning: Cannot modify header information - headers already sent by (output started at %s:3) in %s on line 5

View file

@ -351,11 +351,8 @@ static char* sapi_cli_read_cookies(TSRMLS_D) /* {{{ */
} }
/* }}} */ /* }}} */
static int sapi_cli_header_handler(sapi_header_struct *h, sapi_headers_struct *s TSRMLS_DC) /* {{{ */ static int sapi_cli_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */
{ {
/* free allocated header line */
efree(h->header);
/* avoid pushing headers into SAPI headers list */
return 0; return 0;
} }
/* }}} */ /* }}} */