(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
When a header has been set using header('Foo: bar') it can be replaced with
another value, but it cannot be removed.
Hah! Hit that one last Friday too, gosh that was annoying!
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 existingheader()
function rather than adding a new function,
because the existing function already supports multiple actions (add, append
and replace).
That isn't very intuitive. I would think it was a typo when reading
such code and fix the header line...
I'd suggest explicit header_remove("Vary");
-Hannes
Hi,
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 existingheader()
function rather than adding a new function,
because the existing function already supports multiple actions (add, append
and replace).That isn't very intuitive. I would think it was a typo when reading
such code and fix the header line...
I'd suggest explicit header_remove("Vary");
Agreed, a specific function with a clear name sounds better.
Christian, can you update the patch? - thanks!
johannes
That isn't very intuitive. I would think it was a typo when reading
such code and fix the header line...
I'd suggest explicit header_remove("Vary");Agreed, a specific function with a clear name sounds better.
Christian, can you update the patch? - thanks!
johannes
I suggest header_remove('*') or simply header_remove()
/no param/ removes
all headers (including the one PHP sets by default), so we can start with a
clear state.
Regards,
Stan Vassilev
Stan Vassilev | FM wrote:
I suggest header_remove('*') or simply
header_remove()
/no param/
removes all headers (including the one PHP sets by default), so we can
start with a clear state.
I added header_remove('Foo'). header_remove()
without arguments removes
all headers (though Apache still adds some headers that you cannot remove).
I have tested with apache2handler and cgi. I had to change the signature
of SAPI header_handler function and sapi_header_struct, so the other
SAPIs should be updated for this. I am not sure how to test all these?
Creating a testing environment for all those webservers seems like a
huge task.
I am not comfortable with the size of this patch, given my understanding
of the PHP source code and my general C skills, so I am posting this
patch hoping that somebody will pick it up or help me get it into shape.
Christian
On Sunday 09 November 2008 19:51:31 Christian Schmidt wrote:
Stan Vassilev | FM wrote:
I suggest header_remove('*') or simply
header_remove()
/no param/
removes all headers (including the one PHP sets by default), so we can
start with a clear state.I added header_remove('Foo').
header_remove()
without arguments removes
all headers (though Apache still adds some headers that you cannot remove).I have tested with apache2handler and cgi. I had to change the signature
of SAPI header_handler function and sapi_header_struct, so the other
SAPIs should be updated for this. I am not sure how to test all these?
Creating a testing environment for all those webservers seems like a
huge task.I am not comfortable with the size of this patch, given my understanding
of the PHP source code and my general C skills, so I am posting this
patch hoping that somebody will pick it up or help me get it into shape.
It looks good. The signature change is not that bad if it forces all SAPIs to
be updated and ensures that PHP behaves the same way with all SAPIs.
It is also possible to add something like header_delete_handler() or
header_handler_ex() to sapi_module_struct if the signature change is to be
avoided.
Regards,
Arnaud
+1
I have no problem with implementing this for NSAPI after the patch is
committed to CVS, just keep me informed about this.
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany
-----Original Message-----
From: Arnaud LB [mailto:arnaud.lb@gmail.com] On Behalf Of Arnaud Le Blanc
Sent: Sunday, November 09, 2008 10:02 PM
To: internals@lists.php.net
Cc: Christian Schmidt
Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
usingheader()On Sunday 09 November 2008 19:51:31 Christian Schmidt wrote:
Stan Vassilev | FM wrote:
I suggest header_remove('*') or simply
header_remove()
/no param/
removes all headers (including the one PHP sets by default), so we can
start with a clear state.I added header_remove('Foo').
header_remove()
without arguments removes
all headers (though Apache still adds some headers that you cannot
remove).I have tested with apache2handler and cgi. I had to change the signature
of SAPI header_handler function and sapi_header_struct, so the other
SAPIs should be updated for this. I am not sure how to test all these?
Creating a testing environment for all those webservers seems like a
huge task.I am not comfortable with the size of this patch, given my understanding
of the PHP source code and my general C skills, so I am posting this
patch hoping that somebody will pick it up or help me get it into shape.It looks good. The signature change is not that bad if it forces all SAPIs
to
be updated and ensures that PHP behaves the same way with all SAPIs.It is also possible to add something like header_delete_handler() or
header_handler_ex() to sapi_module_struct if the signature change is to be
avoided.Regards,
Arnaud
Hi,
Committed, thanks Christian :)
apache2handler, apache2filter, apache, apache_hooks, cli and cgi SAPIs have
been updated.
The following SAPIs need to be updated in PHP_5_3 and HEAD:
aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen. (I'm CC-ing
known maintainers)
More informations on the change can be found in the commit message:
http://news.php.net/php.cvs/54228
Regards,
Arnaud
On Sunday 09 November 2008 22:49:47 Uwe Schindler wrote:
+1
I have no problem with implementing this for NSAPI after the patch is
committed to CVS, just keep me informed about this.
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany-----Original Message-----
From: Arnaud LB [mailto:arnaud.lb@gmail.com] On Behalf Of Arnaud Le Blanc
Sent: Sunday, November 09, 2008 10:02 PM
To: internals@lists.php.net
Cc: Christian Schmidt
Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
usingheader()On Sunday 09 November 2008 19:51:31 Christian Schmidt wrote:
Stan Vassilev | FM wrote:
I suggest header_remove('*') or simply
header_remove()
/no param/
removes all headers (including the one PHP sets by default), so we can
start with a clear state.I added header_remove('Foo').
header_remove()
without arguments removes
all headers (though Apache still adds some headers that you cannot
remove).I have tested with apache2handler and cgi. I had to change the signature
of SAPI header_handler function and sapi_header_struct, so the other
SAPIs should be updated for this. I am not sure how to test all these?
Creating a testing environment for all those webservers seems like a
huge task.I am not comfortable with the size of this patch, given my understanding
of the PHP source code and my general C skills, so I am posting this
patch hoping that somebody will pick it up or help me get it into shape.It looks good. The signature change is not that bad if it forces all SAPIs
to
be updated and ensures that PHP behaves the same way with all SAPIs.It is also possible to add something like header_delete_handler() or
header_handler_ex() to sapi_module_struct if the signature change is to be
avoided.Regards,
Arnaud
Hi,
Committed, thanks Christian :)
apache2handler, apache2filter, apache, apache_hooks, cli and cgi
SAPIs have
been updated.The following SAPIs need to be updated in PHP_5_3 and HEAD:
aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen.
(I'm CC-ing
known maintainers)More informations on the change can be found in the commit message:
http://news.php.net/php.cvs/54228
err .. whats the status here?
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Still working on it, hadn't have enough time for it until now, I try to do
it as soon as possible!
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany
From: Lukas Kahwe Smith [mailto:mls@pooteeweet.org]
Sent: Friday, November 28, 2008 2:07 PM
To: Arnaud Le Blanc
Cc: Uwe Schindler; internals@lists.php.net; 'Christian Schmidt'; Alex
Leigh; George Wang
Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
usingheader()Hi,
Committed, thanks Christian :)
apache2handler, apache2filter, apache, apache_hooks, cli and cgi
SAPIs have
been updated.The following SAPIs need to be updated in PHP_5_3 and HEAD:
aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen.
(I'm CC-ing
known maintainers)More informations on the change can be found in the commit message:
http://news.php.net/php.cvs/54228err .. whats the status here?
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Just one question here:
When implementing this into NSAPI, I found the following problem:
NSAPI does not directly allows to remove all headers, you can only do this
step by step. So there are three possibilities to ship around this problem:
a) when SAPI_HEADER_DELETE_ALL is given, in header_handler, do the following
(using the sapi_header_struct given as last parameter):
zend_llist_apply(sapi_headers->headers, (llist_apply_func_t)
php_nsapi_remove_header TSRMLS_CC);
with:
static int php_nsapi_remove_header(sapi_header_struct *sapi_header
TSRMLS_DC)
{
char *header_name, *p;
nsapi_request_context *rc = (nsapi_request_context
*)SG(server_context);
header_name = nsapi_strdup(sapi_header->header);
if (p = strchr(header_name, ':')) *p = 0;
...
param_free(pblock_remove(header_name, rc->rq->srvhdrs));
nsapi_free(header_name);
return ZEND_HASH_APPLY_KEEP;
}
This would remove all headers, set by PHP. Headers embedded by the server
itself would not be deleted (e.g. "Server:" etc.)
b) Use some "hack" to get rid of all headers from the server's hashtable
(like apr_table_clear()). This would remove all headers and also some
important headers set by the server itself (like Server:, Chunked response
headers, etc). I am not sure if this would be good, in my opinion
SAPI_HEADER_DELETE_ALL should only remove headers set by PHP itself! What
does the other SAPI developers think, is it safe to remove apaches default
headers????? I am not sure and tend to say: NO!
c) Completely ignore sapi_header_handler like in CGI and set the headers
later in sapi_send_headers() using the given struct with zend_llist_apply().
Then I do not have to take care of adding/removing headers, I set them to
Sun Webserver shortly before starting the response. Why the difference
between header_handler and send_headers? Is it ok to remove header_handler
(like in CGI) and simply feed all headers in send_headers? This would make
life for SAPI developers easier (also maybe in Apache). What is the idea to
respond after each header change?
What do you think?
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany
-----Original Message-----
From: Lukas Kahwe Smith [mailto:mls@pooteeweet.org]
Sent: Friday, November 28, 2008 2:07 PM
To: Arnaud Le Blanc
Cc: Uwe Schindler; internals@lists.php.net; 'Christian Schmidt'; Alex
Leigh; George Wang
Subject: Re: [PHP-DEV] [PATCH] Allow unsetting headers previously set
usingheader()Hi,
Committed, thanks Christian :)
apache2handler, apache2filter, apache, apache_hooks, cli and cgi
SAPIs have
been updated.The following SAPIs need to be updated in PHP_5_3 and HEAD:
aolserver, continuity, litespeed, nsapi, caudium, phttpd, roxen.
(I'm CC-ing
known maintainers)More informations on the change can be found in the commit message:
http://news.php.net/php.cvs/54228err .. whats the status here?
regards,
Lukas Kahwe Smith
mls@pooteeweet.org
Hi,
On Friday 28 November 2008 18:24:38 Uwe Schindler wrote:
Just one question here:
When implementing this into NSAPI, I found the following problem:NSAPI does not directly allows to remove all headers, you can only do this
step by step. So there are three possibilities to ship around this problem:a) when SAPI_HEADER_DELETE_ALL is given, in header_handler, do the following
(using the sapi_header_struct given as last parameter):zend_llist_apply(sapi_headers->headers, (llist_apply_func_t)
php_nsapi_remove_header TSRMLS_CC);with:
static int php_nsapi_remove_header(sapi_header_struct *sapi_header
TSRMLS_DC)
{
char *header_name, *p;
nsapi_request_context *rc = (nsapi_request_context
*)SG(server_context);header_name = nsapi_strdup(sapi_header->header);
if (p = strchr(header_name, ':')) *p = 0;...
param_free(pblock_remove(header_name, rc->rq->srvhdrs));
nsapi_free(header_name);return ZEND_HASH_APPLY_KEEP;
}This would remove all headers, set by PHP. Headers embedded by the server
itself would not be deleted (e.g. "Server:" etc.)b) Use some "hack" to get rid of all headers from the server's hashtable
(like apr_table_clear()). This would remove all headers and also some
important headers set by the server itself (like Server:, Chunked response
headers, etc). I am not sure if this would be good, in my opinion
SAPI_HEADER_DELETE_ALL should only remove headers set by PHP itself! What
does the other SAPI developers think, is it safe to remove apaches default
headers????? I am not sure and tend to say: NO!c) Completely ignore sapi_header_handler like in CGI and set the headers
later in sapi_send_headers() using the given struct with zend_llist_apply().
Then I do not have to take care of adding/removing headers, I set them to
Sun Webserver shortly before starting the response. Why the difference
between header_handler and send_headers? Is it ok to remove header_handler
(like in CGI) and simply feed all headers in send_headers? This would make
life for SAPI developers easier (also maybe in Apache). What is the idea to
respond after each header change?
I believe that Apache does sets its headers just before sending them, so when
PHP deletes all headers in Apache's hashtable this does not removes "Server",
"Date", etc. If this is not the case for NSAPI, solution a) seems good, but
also allows to remove "Date" by setting it before ;)
One reason for having header_handler() and send_headers() is for example that
flush()
does not sends headers explicitly, but lets the server send them based
on what header_handler() has set.
Regards,
Arnaud
Hallo Arnaud,
I believe that Apache does sets its headers just before sending them, so
when
PHP deletes all headers in Apache's hashtable this does not removes
"Server",
"Date", etc. If this is not the case for NSAPI, solution a) seems good,
but
also allows to remove "Date" by setting it before ;)
That is correct, you are able to remove the headers. For removing the Date
header it would be enough to just use the header_remove()
without setting it
before. But if somebody does this, he knows what he is doing. I want to
prevent PHP from doing things that the user cannot control or understand.
Sun Webserver for example is able to compress output automatically and other
things. So I am not sure what happens, if you remove headers. As source code
of this webserver is not yet available (Sun wants to release it soon), I do
not know when nsapi puts entries in his internal header hash table. Maybe
its in protocol_start_response(), if so everything would be ok, and I can
manually cleanup the complete webserver's hash table (solution b). But
nsapi_response_headers from PHP called shows, that e.g. Date and Server are
set before the request starts.
In my opinion, a call to header_remove()
from PHP should only remove headers
set by PHP (e.g. revert to the state, before the PHP script started).
One reason for having header_handler() and send_headers() is for example
that
flush()
does not sends headers explicitly, but lets the server send them
based
on what header_handler() has set.
That's not correct for at least apache:
php_apache_sapi_flush(void *server_context)
{
...
sapi_send_headers(TSRMLS_C);
r->status = SG(sapi_headers).http_response_code;
SG(headers_sent) = 1;
if (ap_rflush(r) < 0 || r->connection->aborted) {
php_handle_aborted_connection();
}
}
The flush function just calls sapi_send_headers so it explicitely send the
headers, so my approach of do not implementing a header_handler would also
correctly send the headers in this case. For NSAPI it is not even possible
to send headers explicitely, you can only set them in an internal hashtable
of the web server.
For a typical NSAPI module the workflow is the following:
Any "Service" handler from webserver's config sets headers in the internal
hash table. Before it starts to write output, it may also set the response
code. All this is not sent to the client immediately. The headers and
everything is sent on protocol_start_response(). After that you cannot set
any headers anymore and you have to write to the output stream.
The PHP module calls the webserver's protocol_start_response() in
sapi_send_headers(), so even when you flush, this must be called (in the
current version of output buffering there is still a bug about this, which
is fixed in the new version, I think). I repaired this by also implementing
flush for nsapi in my new version, which calls like apache
sapi_send_headers(TSRMLS_C). This fixes a very old bug for NSAPI.
So in my opinion for the server it is no difference when to set the headers
(immediately after the call to header()
) or shortly before starting the
reponse. And this is how CGI works - and this would be enough for NSAPI,
too.
The only thing that would not work is explicitely removing headers like
"Date:", but for this use case header_handler() could only be implemented
for the op SAPI_HEADER_DELETE. But with CGI this would not be possible.
Uwe
I implemented and tested now version a), works fine! header_remove()
now
only removes headers set/modified by PHP.
I tested the case to remove all headers by cleaning the server's header
hash table, but this breaks server hard:
Best example: Removing the "Transfer-encoding: chunked" header, the server
inserts at the beginning of the request before PHP is executed, is not
restored on protocol_start_response, but the server still sends chunked data
-> the browser will not understand it.
Solutions c), as I told before would also be good, but then you would not
see PHP's headers in get_response_headers(). And code is not much shorter or
simplier.
Uwe Schindler
thetaphi@php.net - http://www.php.net
NSAPI SAPI developer
Bremen, Germany
-----Original Message-----
From: Uwe Schindler [mailto:thetaphi@php.net]
Sent: Saturday, November 29, 2008 1:09 PM
To: 'Arnaud Le Blanc'
Cc: 'Lukas Kahwe Smith'; internals@lists.php.net; 'Christian Schmidt';
'Alex Leigh'; 'George Wang'
Subject: RE: [PHP-DEV] [PATCH] Allow unsetting headers previously set
usingheader()Hallo Arnaud,
I believe that Apache does sets its headers just before sending them, so
when
PHP deletes all headers in Apache's hashtable this does not removes
"Server",
"Date", etc. If this is not the case for NSAPI, solution a) seems good,
but
also allows to remove "Date" by setting it before ;)That is correct, you are able to remove the headers. For removing the Date
header it would be enough to just use theheader_remove()
without setting
it
before. But if somebody does this, he knows what he is doing. I want to
prevent PHP from doing things that the user cannot control or understand.
Sun Webserver for example is able to compress output automatically and
other
things. So I am not sure what happens, if you remove headers. As source
code
of this webserver is not yet available (Sun wants to release it soon), I
do
not know when nsapi puts entries in his internal header hash table.
Maybe
its in protocol_start_response(), if so everything would be ok, and I can
manually cleanup the complete webserver's hash table (solution b). But
nsapi_response_headers from PHP called shows, that e.g. Date and Server
are
set before the request starts.In my opinion, a call to
header_remove()
from PHP should only remove
headers
set by PHP (e.g. revert to the state, before the PHP script started).One reason for having header_handler() and send_headers() is for example
that
flush()
does not sends headers explicitly, but lets the server send them
based
on what header_handler() has set.That's not correct for at least apache:
php_apache_sapi_flush(void *server_context)
{
...
sapi_send_headers(TSRMLS_C);r->status = SG(sapi_headers).http_response_code;
SG(headers_sent) = 1;if (ap_rflush(r) < 0 || r->connection->aborted) {
php_handle_aborted_connection();
}
}The flush function just calls sapi_send_headers so it explicitely send the
headers, so my approach of do not implementing a header_handler would also
correctly send the headers in this case. For NSAPI it is not even possible
to send headers explicitely, you can only set them in an internal
hashtable
of the web server.For a typical NSAPI module the workflow is the following:
Any "Service" handler from webserver's config sets headers in the internal
hash table. Before it starts to write output, it may also set the response
code. All this is not sent to the client immediately. The headers and
everything is sent on protocol_start_response(). After that you cannot set
any headers anymore and you have to write to the output stream.The PHP module calls the webserver's protocol_start_response() in
sapi_send_headers(), so even when you flush, this must be called (in the
current version of output buffering there is still a bug about this, which
is fixed in the new version, I think). I repaired this by also
implementing
flush for nsapi in my new version, which calls like apache
sapi_send_headers(TSRMLS_C). This fixes a very old bug for NSAPI.So in my opinion for the server it is no difference when to set the
headers
(immediately after the call toheader()
) or shortly before starting the
reponse. And this is how CGI works - and this would be enough for NSAPI,
too.The only thing that would not work is explicitely removing headers like
"Date:", but for this use case header_handler() could only be implemented
for the op SAPI_HEADER_DELETE. But with CGI this would not be possible.Uwe