You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modules-dev@httpd.apache.org by Eric Johanson <er...@valmarc.com> on 2014/10/16 15:36:19 UTC

output filter needs to redirect to 503 error status

Hi,
I have an output filter module which is working just fine, but I need to add
a feature so that when certain error conditions occur during processing, the
output filter hook function redirects the whole request to a 503 error
status (service unavailable).  Obviously for a "handler" module this is
trivial to accomplish, but it is not clear how to do this in an output
filter module.

My output filter hooked function is defined as follows:
       apr_status_t mts_out_filter(ap_filter_t *f,apr_bucket_brigade *bb)

I need this function to "do something" that causes the whole request to be
redirected such that the client sees a 503 error status with no
body/content.

Things that I've tried so far:
* Returning HTTP_SERVICE_UNAVAILABLE from the output filter function after
calling "ap_pass_brigade(f->next,bb)"
* Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling
"ap_pass_brigade(f->next,bb)"
* calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"

None of these really seem to behave properly.  I just want the client to
receive a 503 error status with no content body.  There must be a way to
achieve this behavior from within an output filter hook?

Any advice is appreciated.
Thanks, -Eric



Re: output filter needs to redirect to 503 error status

Posted by Nick Kew <ni...@apache.org>.
On Thu, 16 Oct 2014 09:36:19 -0400
"Eric Johanson" <er...@valmarc.com> wrote:

> My output filter hooked function is defined as follows:
>        apr_status_t mts_out_filter(ap_filter_t *f,apr_bucket_brigade *bb)
> 
> I need this function to "do something" that causes the whole request to be
> redirected such that the client sees a 503 error status with no
> body/content.
> 
> Things that I've tried so far:
> * Returning HTTP_SERVICE_UNAVAILABLE from the output filter function after
> calling "ap_pass_brigade(f->next,bb)"

HTTP_SERVICE_UNAVAILABLE isn't an apr_status_t!

> * Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling
> "ap_pass_brigade(f->next,bb)"

after ap_pass_brigade, you've already returned a status down the
line to the client.  It's too late to change it!

> * calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"

That would have to go through the filter chain.

Filters are supposed to be about your data.  They can in limited
circumstances change metadata (e.g. set a 503), but you'd need
at least to set r->status before the first call to f->next.


-- 
Nick Kew

RE: output filter needs to redirect to 503 error status

Posted by Eric Johanson <er...@valmarc.com>.
Actually, I think Sorin's code makes more sense than he gives himself credit
for.  It looks like it basically tries to re-execute the filter chain from
scratch, after removing the failed filter and inserting the error filter.
It doesn't really swap the output and error headers-- it just clears them
both before re-executing the filter chain.  And the reason it calls
ap_pass_brigade on r->output_filters (rather than r->next) is because it is
re-running the whole filter chain from scratch.

But I'm not sure this is the best way to actually accomplish redirection to
an HTTP error status.  It seems a bit convoluted and kludgy.  So I ended up
doing the following:
When my output filter detects that it can't process the data stream, then it
does the following (before calling ap_pass_brigade):
	if (r->args == NULL || r->args[0] == '\0') {
		sprintf(errurl,"%s?_MTSERROR=503",r->uri);
	} else {
		sprintf(errurl,"%s?%s&_MTSERROR=503",r->uri,r->args);
	}
	ap_internal_redirect(errurl,r);
So the above code simply redirects the request the the same URI, except that
a query-parameter called "_MTSERROR" is appended to the original URI.

Then I wrote a simple content-generator handler as follows:
static int mts_error_handler(request_rec *r)
{
int status;

	if (r->args == NULL || r->args[0] == '\0') return DECLINED;
	/* call function to examine r->args to look for _MTSERROR query
parameter */
	status = search_args_for_mtserror(r->args);
	return ap_is_HTTP_VALID_RESPONSE(status) ? status : DECLINED);
} /* mts_error_handler */

It works great, and doesn't feel like I'm incestuously married to the inner
workings of the apache server's request processing algorithms.
Theoretically, I suppose I pollute the URL space of the server by having a
handler that claims ownership of any request that has a query-string
parameter of "_MTSERROR", but this is really not a problem for my
application in practice.

Also, it could be argued that a filter has no business redirecting an entire
request, and that a filter should restrict itself to modifying data in the
stream.  However, this particular filter is a critical component in
generating our application's content, and if there is any kind of problem,
then it is true that the whole request cannot (and should not) be processed.
Nor do we want the request to "fail silently"-- we want the user to see a
fatal error on the client side.  Given that, perhaps my module should not
even be a filter at all, but should instead be a content-generation handler.
But a filter provides more flexibility to leverage existing handler modules.

-Eric


-----Original Message-----
From: Sorin Manolache [mailto:sorinm@gmail.com] 
Sent: Thursday, October 16, 2014 6:23 PM
To: modules-dev@httpd.apache.org
Subject: Re: output filter needs to redirect to 503 error status

On 2014-10-16 22:35, Eric Johanson wrote:
> Thank you for the suggestion.  I did try that, but the order in which 
> you set f->r->status and call ap_pass_brigade doesn't seem to really 
> make a difference.
>
> Basically what happens is that the browsers don't like the format of 
> the HTTP response packet.  They complain that there is "extra 
> unexpected data after the end of the response."  Oddly, when I use the 
> Linux "wget" command, it doesn't complain.  I may just write a handler 
> hook specifically for returning error codes.  Then when the filter 
> modules has an error, it can call ap_internal_redirect to a special 
> page whose exclusive purpose is to be captured by my error handler to
return an HTTP status code.
>
> Any other suggestions?

This "expected data" message may be caused by a response body that has a
different (bigger) length than what is announced in the Content-Length
output header.

If possible, try to not pass any data down the filter chain to the network
when you want to set a 503 error. Set f->r->status = 503 and then pass a
brigade that contains only an EOS bucket.

I don't know if it is possible to send an empty body with a correct
Content-Length (i.e. equal to zero) once your filter has already passed some
data down the filter chain.

I found some old code of mine that sends a 5xx code with an empty body when
it detects an error. However my filter buffers all the data sent by a
backend before it passes the filtered response in a single brigade down the
filter chain. I.e. my filter returns APR_SUCCESS without invoking
ap_pass_brigade whenever the brigade passed to my filter does not contain an
EOS bucket. When the brigade contains an EOS bucket my filter passes the
entire filtered response down the filter chain. So in my case, no data has
reached any downstream filters before I detect an error.

        // nominal part
        ...
        // error-handling
        request_rec *r = f->r;
        r->status = HTTP_INTERNAL_SERVER_ERROR;
        ap_remove_output_filter(f);
        f->ctx = 0;
        r->eos_sent = 0;
        apr_table_t *tmp = r->headers_out;
        ap_add_output_filter("error_filter", 0, r, r->connection);
        r->headers_out = r->err_headers_out;
        r->err_headers_out = tmp;
        apr_table_clear(r->err_headers_out);
        return ap_pass_brigade(r->output_filters, bb);

Apparently I remove the "useful" filter when I detect an error
(ap_remove_output_filter(f)) and I replace it with another filter
(ap_add_output_filter), that produces the error response. I suppose you
don't have to do this, I suppose that the effect can be achieved in the same
filter.

I do not really remember why I swap the error and output headers. I suppose
I clear the error headers in order to get rid of a previously computed
Content-Length header.

Also I do not remember why I reset the eos_sent flag, but I think this is
important.

And I'm quite surprised that I pass to the head of the output filter chain
(ap_pass_brigade(r->output_filters, bb) and not f->next).

I'm sorry that my explanations are incomplete, it's really an old code and I
do not remember the details. But it's still in production and does what you
want: it sends an empty-bodied response with a 5xx http status code.

Sorin


>
> Thanks, -Eric
>
>
> -----Original Message-----
> From: Sorin Manolache [mailto:sorinm@gmail.com]
> Sent: Thursday, October 16, 2014 12:59 PM
> To: modules-dev@httpd.apache.org
> Subject: Re: output filter needs to redirect to 503 error status
>
> On 2014-10-16 15:36, Eric Johanson wrote:
>> Hi,
>> I have an output filter module which is working just fine, but I need 
>> to add a feature so that when certain error conditions occur during 
>> processing, the output filter hook function redirects the whole 
>> request to a 503 error status (service unavailable).  Obviously for a 
>> "handler" module this is trivial to accomplish, but it is not clear 
>> how to do this in an output filter module.
>>
>> My output filter hooked function is defined as follows:
>>          apr_status_t mts_out_filter(ap_filter_t 
>> *f,apr_bucket_brigade
>> *bb)
>>
>> I need this function to "do something" that causes the whole request 
>> to be redirected such that the client sees a 503 error status with no 
>> body/content.
>>
>> Things that I've tried so far:
>> * Returning HTTP_SERVICE_UNAVAILABLE from the output filter function 
>> after calling "ap_pass_brigade(f->next,bb)"
>> * Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling 
>> "ap_pass_brigade(f->next,bb)"
>
> Try setting f->r->status _before_ calling ap_pass_brigade.
>
> If you get the 503 but you get the default 503 error response body 
> that is automatically set by apache then replace it by using the 
> ErrorDocument directive 
> http://httpd.apache.org/docs/2.2/mod/core.html#errordocument (or
http://httpd.apache.org/docs/2.4/mod/core.html#errordocument).
>
> Sorin
>
>> * calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"
>>
>> None of these really seem to behave properly.  I just want the client 
>> to receive a 503 error status with no content body.  There must be a 
>> way to achieve this behavior from within an output filter hook?
>>
>> Any advice is appreciated.
>> Thanks, -Eric
>>
>>
>
>



Re: output filter needs to redirect to 503 error status

Posted by Sorin Manolache <so...@gmail.com>.
On 2014-10-16 22:35, Eric Johanson wrote:
> Thank you for the suggestion.  I did try that, but the order in which you
> set f->r->status and call ap_pass_brigade doesn't seem to really make a
> difference.
>
> Basically what happens is that the browsers don't like the format of the
> HTTP response packet.  They complain that there is "extra unexpected data
> after the end of the response."  Oddly, when I use the Linux "wget" command,
> it doesn't complain.  I may just write a handler hook specifically for
> returning error codes.  Then when the filter modules has an error, it can
> call ap_internal_redirect to a special page whose exclusive purpose is to be
> captured by my error handler to return an HTTP status code.
>
> Any other suggestions?

This "expected data" message may be caused by a response body that has a 
different (bigger) length than what is announced in the Content-Length 
output header.

If possible, try to not pass any data down the filter chain to the 
network when you want to set a 503 error. Set f->r->status = 503 and 
then pass a brigade that contains only an EOS bucket.

I don't know if it is possible to send an empty body with a correct 
Content-Length (i.e. equal to zero) once your filter has already passed 
some data down the filter chain.

I found some old code of mine that sends a 5xx code with an empty body 
when it detects an error. However my filter buffers all the data sent by 
a backend before it passes the filtered response in a single brigade 
down the filter chain. I.e. my filter returns APR_SUCCESS without 
invoking ap_pass_brigade whenever the brigade passed to my filter does 
not contain an EOS bucket. When the brigade contains an EOS bucket my 
filter passes the entire filtered response down the filter chain. So in 
my case, no data has reached any downstream filters before I detect an 
error.

        // nominal part
        ...
        // error-handling
        request_rec *r = f->r;
        r->status = HTTP_INTERNAL_SERVER_ERROR;
        ap_remove_output_filter(f);
        f->ctx = 0;
        r->eos_sent = 0;
        apr_table_t *tmp = r->headers_out;
        ap_add_output_filter("error_filter", 0, r, r->connection);
        r->headers_out = r->err_headers_out;
        r->err_headers_out = tmp;
        apr_table_clear(r->err_headers_out);
        return ap_pass_brigade(r->output_filters, bb);

Apparently I remove the "useful" filter when I detect an error 
(ap_remove_output_filter(f)) and I replace it with another filter 
(ap_add_output_filter), that produces the error response. I suppose you 
don't have to do this, I suppose that the effect can be achieved in the 
same filter.

I do not really remember why I swap the error and output headers. I 
suppose I clear the error headers in order to get rid of a previously 
computed Content-Length header.

Also I do not remember why I reset the eos_sent flag, but I think this 
is important.

And I'm quite surprised that I pass to the head of the output filter 
chain (ap_pass_brigade(r->output_filters, bb) and not f->next).

I'm sorry that my explanations are incomplete, it's really an old code 
and I do not remember the details. But it's still in production and does 
what you want: it sends an empty-bodied response with a 5xx http status 
code.

Sorin


>
> Thanks, -Eric
>
>
> -----Original Message-----
> From: Sorin Manolache [mailto:sorinm@gmail.com]
> Sent: Thursday, October 16, 2014 12:59 PM
> To: modules-dev@httpd.apache.org
> Subject: Re: output filter needs to redirect to 503 error status
>
> On 2014-10-16 15:36, Eric Johanson wrote:
>> Hi,
>> I have an output filter module which is working just fine, but I need
>> to add a feature so that when certain error conditions occur during
>> processing, the output filter hook function redirects the whole
>> request to a 503 error status (service unavailable).  Obviously for a
>> "handler" module this is trivial to accomplish, but it is not clear
>> how to do this in an output filter module.
>>
>> My output filter hooked function is defined as follows:
>>          apr_status_t mts_out_filter(ap_filter_t *f,apr_bucket_brigade
>> *bb)
>>
>> I need this function to "do something" that causes the whole request
>> to be redirected such that the client sees a 503 error status with no
>> body/content.
>>
>> Things that I've tried so far:
>> * Returning HTTP_SERVICE_UNAVAILABLE from the output filter function
>> after calling "ap_pass_brigade(f->next,bb)"
>> * Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling
>> "ap_pass_brigade(f->next,bb)"
>
> Try setting f->r->status _before_ calling ap_pass_brigade.
>
> If you get the 503 but you get the default 503 error response body that is
> automatically set by apache then replace it by using the ErrorDocument
> directive http://httpd.apache.org/docs/2.2/mod/core.html#errordocument (or
> http://httpd.apache.org/docs/2.4/mod/core.html#errordocument).
>
> Sorin
>
>> * calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"
>>
>> None of these really seem to behave properly.  I just want the client
>> to receive a 503 error status with no content body.  There must be a
>> way to achieve this behavior from within an output filter hook?
>>
>> Any advice is appreciated.
>> Thanks, -Eric
>>
>>
>
>


RE: output filter needs to redirect to 503 error status

Posted by Eric Johanson <er...@valmarc.com>.
Thank you for the suggestion.  I did try that, but the order in which you
set f->r->status and call ap_pass_brigade doesn't seem to really make a
difference.

Basically what happens is that the browsers don't like the format of the
HTTP response packet.  They complain that there is "extra unexpected data
after the end of the response."  Oddly, when I use the Linux "wget" command,
it doesn't complain.  I may just write a handler hook specifically for
returning error codes.  Then when the filter modules has an error, it can
call ap_internal_redirect to a special page whose exclusive purpose is to be
captured by my error handler to return an HTTP status code.

Any other suggestions?

Thanks, -Eric


-----Original Message-----
From: Sorin Manolache [mailto:sorinm@gmail.com] 
Sent: Thursday, October 16, 2014 12:59 PM
To: modules-dev@httpd.apache.org
Subject: Re: output filter needs to redirect to 503 error status

On 2014-10-16 15:36, Eric Johanson wrote:
> Hi,
> I have an output filter module which is working just fine, but I need 
> to add a feature so that when certain error conditions occur during 
> processing, the output filter hook function redirects the whole 
> request to a 503 error status (service unavailable).  Obviously for a 
> "handler" module this is trivial to accomplish, but it is not clear 
> how to do this in an output filter module.
>
> My output filter hooked function is defined as follows:
>         apr_status_t mts_out_filter(ap_filter_t *f,apr_bucket_brigade 
> *bb)
>
> I need this function to "do something" that causes the whole request 
> to be redirected such that the client sees a 503 error status with no 
> body/content.
>
> Things that I've tried so far:
> * Returning HTTP_SERVICE_UNAVAILABLE from the output filter function 
> after calling "ap_pass_brigade(f->next,bb)"
> * Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling 
> "ap_pass_brigade(f->next,bb)"

Try setting f->r->status _before_ calling ap_pass_brigade.

If you get the 503 but you get the default 503 error response body that is
automatically set by apache then replace it by using the ErrorDocument
directive http://httpd.apache.org/docs/2.2/mod/core.html#errordocument (or
http://httpd.apache.org/docs/2.4/mod/core.html#errordocument).

Sorin

> * calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"
>
> None of these really seem to behave properly.  I just want the client 
> to receive a 503 error status with no content body.  There must be a 
> way to achieve this behavior from within an output filter hook?
>
> Any advice is appreciated.
> Thanks, -Eric
>
>



Re: output filter needs to redirect to 503 error status

Posted by Sorin Manolache <so...@gmail.com>.
On 2014-10-16 15:36, Eric Johanson wrote:
> Hi,
> I have an output filter module which is working just fine, but I need to add
> a feature so that when certain error conditions occur during processing, the
> output filter hook function redirects the whole request to a 503 error
> status (service unavailable).  Obviously for a "handler" module this is
> trivial to accomplish, but it is not clear how to do this in an output
> filter module.
>
> My output filter hooked function is defined as follows:
>         apr_status_t mts_out_filter(ap_filter_t *f,apr_bucket_brigade *bb)
>
> I need this function to "do something" that causes the whole request to be
> redirected such that the client sees a 503 error status with no
> body/content.
>
> Things that I've tried so far:
> * Returning HTTP_SERVICE_UNAVAILABLE from the output filter function after
> calling "ap_pass_brigade(f->next,bb)"
> * Setting f->r->status to HTTP_SERVICE_UNAVAILABLE after calling
> "ap_pass_brigade(f->next,bb)"

Try setting f->r->status _before_ calling ap_pass_brigade.

If you get the 503 but you get the default 503 error response body that 
is automatically set by apache then replace it by using the 
ErrorDocument directive 
http://httpd.apache.org/docs/2.2/mod/core.html#errordocument (or 
http://httpd.apache.org/docs/2.4/mod/core.html#errordocument).

Sorin

> * calling "ap_send_error_response(f->r,HTTP_SERVICE_UNAVAILABLE)"
>
> None of these really seem to behave properly.  I just want the client to
> receive a 503 error status with no content body.  There must be a way to
> achieve this behavior from within an output filter hook?
>
> Any advice is appreciated.
> Thanks, -Eric
>
>