httpd-modules-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Eric Johanson" <er...@valmarc.com>
Subject RE: output filter needs to redirect to 503 error status
Date Fri, 17 Oct 2014 12:55:29 GMT
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
>>
>>
>
>



Mime
View raw message