Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:41281 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 71458 invoked from network); 19 Oct 2008 21:42:14 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 19 Oct 2008 21:42:14 -0000 Authentication-Results: pb1.pair.com smtp.mail=chsc@peytz.dk; spf=softfail; sender-id=softfail Authentication-Results: pb1.pair.com header.from=chsc@peytz.dk; sender-id=softfail Received-SPF: softfail (pb1.pair.com: domain peytz.dk does not designate 195.41.46.235 as permitted sender) X-PHP-List-Original-Sender: chsc@peytz.dk X-Host-Fingerprint: 195.41.46.235 pfepa.post.tele.dk Linux 2.5 (sometimes 2.4) (4) Received: from [195.41.46.235] ([195.41.46.235:36854] helo=pfepa.post.tele.dk) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id CF/F0-29559-3B9ABF84 for ; Sun, 19 Oct 2008 17:42:13 -0400 Received: from [10.0.8.1] (0x573d84a6.vbrnqu1.static.dsl.tele.dk [87.61.132.166]) by pfepa.post.tele.dk (Postfix) with ESMTP id 2B3D9FAC002 for ; Sun, 19 Oct 2008 23:41:27 +0200 (CEST) Message-ID: <48FBA987.5030809@peytz.dk> Date: Sun, 19 Oct 2008 23:41:27 +0200 User-Agent: Icedove 1.5.0.14eol (X11/20080724) MIME-Version: 1.0 To: internals@lists.php.net Content-Type: multipart/mixed; boundary="------------050608060506080509020705" Subject: [PATCH] Allow unsetting headers previously set using header() From: chsc@peytz.dk (Christian Schmidt) --------------050608060506080509020705 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit (This is a repost of a proposal originally sent to the list 6 months ago. It didn't gather much attention back then, but I talked to several people off-list who found it useful and suggested that I should try to bring it up again at a later date - so here goes) When a header has been set using header('Foo: bar') it can be replaced with another value, but it cannot be removed. This ability to remove headers is relevant e.g. if the auto_prepend_file calls header('Vary: Accept-Language'), but later on in the request it is determined that a particular file does not do language-negotiation (e.g. a PHP script outputting an image file). Setting it to the empty string using header('Vary:') is not equivalent to unsetting the header. It is possible to work around this limitation by maintaining a custom header buffer, but since PHP allows replacing and appending to previously set headers, it seems reasonable to also allow unsetting headers. I suggest extending the behaviour of header() so that when the first argument does not contain a colon (and does not begin with "HTTP/"), e.g. header('Vary'), it unsets the header with the specified name. I decided to extend the existing header() function rather than adding a new function, because the existing function already supports multiple actions (add, append and replace). AFAICT this change is backwards compatible. According to the HTTP spec, RFC 2616, section 4.2, all header lines must include a colon, so calling e.g. header('Vary') does currently not generate a valid header: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 The attached patch contains an implementation for the cgi and apache2handler SAPIs for PHP_5_3. The 6 months old patch still applies, though I haven't tested it again except from running "make test". AFAICT these SAPIs don't need to be changed: cli, embed, isapi, milter, pi3web, thttpd, tux, webjames. These SAPIs need additional patching: apache, apache_hooks, apache2filter, continuity/capi, caudium, nsapi, phttpd, roxen I don't have an easy way to test these SAPIs, so any help with adding support for them is appreciated. AFAICT some of these don't have full header() support (in particular for the second $replace parameter), so it may not be a big issue if they aren't patched right away. For some reason I couldn't build apache2filter on my machine. The attached phpt file is intended to be put in ext/standard/tests/general_functions. Christian --------------050608060506080509020705 Content-Type: text/plain; name="header.patch.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="header.patch.txt" Index: sapi/apache2handler/sapi_apache2.c =================================================================== RCS file: /repository/php-src/sapi/apache2handler/sapi_apache2.c,v retrieving revision 1.57.2.10.2.15.2.3 diff -u -9 -p -r1.57.2.10.2.15.2.3 sapi_apache2.c --- sapi/apache2handler/sapi_apache2.c 18 Mar 2008 22:23:20 -0000 1.57.2.10.2.15.2.3 +++ sapi/apache2handler/sapi_apache2.c 27 Mar 2008 21:39:21 -0000 @@ -87,20 +87,20 @@ php_apache_sapi_header_handler(sapi_head { php_struct *ctx; char *val, *ptr; ctx = SG(server_context); val = strchr(sapi_header->header, ':'); if (!val) { - sapi_free_header(sapi_header); - return 0; + apr_table_unset(ctx->r->headers_out, sapi_header->header); + return SAPI_HEADER_ADD; } ptr = val; *val = '\0'; do { val++; } while (*val == ' '); Index: main/SAPI.c =================================================================== RCS file: /repository/php-src/main/SAPI.c,v retrieving revision 1.202.2.7.2.15.2.4 diff -u -9 -p -r1.202.2.7.2.15.2.4 SAPI.c --- main/SAPI.c 18 Mar 2008 21:42:50 -0000 1.202.2.7.2.15.2.4 +++ main/SAPI.c 27 Mar 2008 21:39:21 -0000 @@ -491,19 +491,20 @@ static void sapi_update_response_code(in if (SG(sapi_headers).http_status_line) { efree(SG(sapi_headers).http_status_line); SG(sapi_headers).http_status_line = NULL; } SG(sapi_headers).http_response_code = ncode; } 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_header_line ctr = {0}; int r; ctr.line = header_line; ctr.line_len = header_line_len; @@ -730,32 +731,36 @@ SAPI_API int sapi_header_op(sapi_header_ if (sapi_module.header_handler) { retval = sapi_module.header_handler(&sapi_header, &SG(sapi_headers) TSRMLS_CC); } else { retval = SAPI_HEADER_ADD; } if (retval & SAPI_HEADER_DELETE_ALL) { zend_llist_clean(&SG(sapi_headers).headers); } if (retval & SAPI_HEADER_ADD) { - /* in replace mode first remove the header if it already exists in the headers llist */ - if (replace) { - colon_offset = strchr(sapi_header.header, ':'); - if (colon_offset) { + colon_offset = strchr(sapi_header.header, ':'); + if (!colon_offset) { + /* no colon means delete header */ + zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header); + sapi_free_header(&sapi_header); + + } else { + /* in replace mode first remove the header if it already exists in the headers llist */ + if (replace) { char sav; - colon_offset++; sav = *colon_offset; *colon_offset = 0; zend_llist_del_element(&SG(sapi_headers).headers, sapi_header.header, (int(*)(void*, void*))sapi_find_matching_header); *colon_offset = sav; } - } - zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); + zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); + } } return SUCCESS; } SAPI_API int sapi_send_headers(TSRMLS_D) { int retval; int ret = FAILURE; --------------050608060506080509020705 Content-Type: text/plain; name="head_cgi.phpt.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="head_cgi.phpt.txt" --TEST-- header() and friends --SKIPIF-- --FILE-- --EXPECTF-- CONTENT-type: text/plain Foo1: bar1 2 Foo1: bar1 3 Foo3: bar3 string(4) "foo3" bool(false) string(4) "foo4" bool(true) Warning: Cannot modify header information - headers already sent by (output started at %s:%d) in %s on line %d NULL array(4) { [0]=> string(24) "CONTENT-type: text/plain" [1]=> string(12) "Foo1: bar1 2" [2]=> string(12) "Foo1: bar1 3" [3]=> string(10) "Foo3: bar3" } Done --------------050608060506080509020705--