Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Yann Ylavic
On Mon, Jun 4, 2018 at 7:23 PM, Paul Callahan  wrote:
>
> I did try with input filters.  The reason I'm trying to do this in a
> handler is because I want to return 403 to the browser if the request body
> has something unsavory in it.   With reverse proxied requests, it appears
> the input filter fires too late and if I try to send a bucket with 403 in
> it, it is ignored and a 400 goes back to the browser.

When interrupting the request with an error code, your filter should
do something like:
ap_die(403, r);
return AP_FILTER_ERROR;

>
> btw, Nick I bought your book - it was a great help :)

It once did it for me too :)


Regards,
Yann.


Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Nick Kew
On Mon, 4 Jun 2018 10:23:59 -0700
Paul Callahan  wrote:

> Thank you for your replies.
> 
> I did try with input filters.  The reason I'm trying to do this in a
> handler is because I want to return 403 to the browser if the request
> body has something unsavory in it.   With reverse proxied requests,
> it appears the input filter fires too late and if I try to send a
> bucket with 403 in it, it is ignored and a 400 goes back to the
> browser.   In the debugger, I see the fixup call back being hit
> before my input filter.   If I could get the input filter to trip
> sooner without consuming the request, I could go with that.  If I
> call ap_get_brigade() in an earlier handler to trip the input filter,
> it appears the request body is consumed.

OK, that's actually quite a complex task, especially if
you need to deal with larger requests.

It is, however, as task that's been done in open source
code you can look at, or perhaps use instead of reinventing
their wheel.  Either Ironbee or mod_security will scan a
request body for you.

> btw, Nick I bought your book - it was a great help :)

Thanks :)

-- 
Nick Kew


Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Paul Callahan
Thank you for your replies.

I did try with input filters.  The reason I'm trying to do this in a
handler is because I want to return 403 to the browser if the request body
has something unsavory in it.   With reverse proxied requests, it appears
the input filter fires too late and if I try to send a bucket with 403 in
it, it is ignored and a 400 goes back to the browser.   In the debugger, I
see the fixup call back being hit before my input filter.   If I could get
the input filter to trip sooner without consuming the request, I could go
with that.  If I call ap_get_brigade() in an earlier handler to trip the
input filter, it appears the request body is consumed.


btw, Nick I bought your book - it was a great help :)

On Mon, Jun 4, 2018 at 2:12 AM, Nick Kew  wrote:

>
> > On 4 Jun 2018, at 08:55, Sorin Manolache  wrote:
> >
> > On 2018-06-04 08:27, Paul Callahan wrote:
> >> In apache modules, my understanding is if a handler declines a request,
> the
> >> request is passed on to the next suitable handler.   I'm finding though
> if
> >> I read the bucket_brigade/request body, and then decline the request,
> the
> >> subsequent handler doesn't get any data.  It is like the act of reading
> the
> >> bucket brigade consumes it.
> >> I would like to have a request handler read the data, do some task (in
> this
> >> case just count bytes), and decline the request without consuming the
> data
> >> for the next handler.
>
> Why is that a handler?  An input filter could count the data and pass them
> straight down the chain, avoiding any such problem.  At a glance, your code
> would need very little modification to work as a filter.
>
> Exhortation: work with the server architecture, not against it!
>
> > Hello,
> >
> > As far as I know, there is no simple way to do that.
>
> This is true, in the sense that data get consumed.  To do otherwise would
> be
> monstrously inefficient, and turn a big request body straight into a DoS.
>
> This is why we have mod_request.  It provides an input filter you can use
> explicitly to buffer data which then remain available to the next module
> to read them.
> Use with caution: for example, if you fail to limit request size, you
> could find
> yourself trying to buffer gigabytes of request data.
>
>
> >> } while (!end && (status == APR_SUCCESS));
> >> if (status == APR_SUCCESS) {
> >> return DECLINED;
> >> } else {
> >> return HTTP_INTERNAL_SERVER_ERROR;
> >> }
> >> }
>
> Minor tip there: you're turning EAGAIN into a fatal error.
>
> --
> Nick Kew


Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Nick Kew


> On 4 Jun 2018, at 08:55, Sorin Manolache  wrote:
> 
> On 2018-06-04 08:27, Paul Callahan wrote:
>> In apache modules, my understanding is if a handler declines a request, the
>> request is passed on to the next suitable handler.   I'm finding though if
>> I read the bucket_brigade/request body, and then decline the request, the
>> subsequent handler doesn't get any data.  It is like the act of reading the
>> bucket brigade consumes it.
>> I would like to have a request handler read the data, do some task (in this
>> case just count bytes), and decline the request without consuming the data
>> for the next handler.

Why is that a handler?  An input filter could count the data and pass them
straight down the chain, avoiding any such problem.  At a glance, your code
would need very little modification to work as a filter.

Exhortation: work with the server architecture, not against it!

> Hello,
> 
> As far as I know, there is no simple way to do that.

This is true, in the sense that data get consumed.  To do otherwise would be
monstrously inefficient, and turn a big request body straight into a DoS.

This is why we have mod_request.  It provides an input filter you can use
explicitly to buffer data which then remain available to the next module to 
read them.
Use with caution: for example, if you fail to limit request size, you could find
yourself trying to buffer gigabytes of request data.


>> } while (!end && (status == APR_SUCCESS));
>> if (status == APR_SUCCESS) {
>> return DECLINED;
>> } else {
>> return HTTP_INTERNAL_SERVER_ERROR;
>> }
>> }

Minor tip there: you're turning EAGAIN into a fatal error.

-- 
Nick Kew

Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Sorin Manolache

On 2018-06-04 08:27, Paul Callahan wrote:

In apache modules, my understanding is if a handler declines a request, the
request is passed on to the next suitable handler.   I'm finding though if
I read the bucket_brigade/request body, and then decline the request, the
subsequent handler doesn't get any data.  It is like the act of reading the
bucket brigade consumes it.

I would like to have a request handler read the data, do some task (in this
case just count bytes), and decline the request without consuming the data
for the next handler.


Hello,

As far as I know, there is no simple way to do that.

Other handlers do something similar to what you've done, namely they 
call ap_get_brigade(r->input_filters, ...).


So in order for them to still read something, you'll have to

* write an input filter
* in your handler you read the request body and store it somewhere
* afterwards in your handler you add your input filter to the chain of 
input filters (ap_add_input_filter)

* your handler declines.

Then other handlers will call ap_get_brigade which will call your input 
filter. Your input filter will not call any other filters but will copy 
the stored body to the brigade passed in the call to your filter. Your 
filter will give the illusion to other handlers that they are reading 
from the network.


HTH,
Sorin



Thank you.

 int my_declining_handler(request_rec *r) {
 apr_status_t status;
 int end = 0;
 apr_size_t bytes, count = 0;
 const char *buf;
 apr_bucket *b;
 apr_bucket_brigade *temp_brigade;

 // here: header check for content-length/transfer encoding

 temp_brigade = apr_brigade_create(r->pool,
r->connection->bucket_alloc);
 do {
 status = ap_get_brigade(r->input_filters, temp_brigade,
AP_MODE_READBYTES, APR_BLOCK_READ, BUFLEN);
 if (status == APR_SUCCESS) {/* Loop over the contents of
temp_brigade */
 for (b = APR_BRIGADE_FIRST(temp_brigade);
  b != APR_BRIGADE_SENTINEL(temp_brigade);
  b = APR_BUCKET_NEXT(b)) {
 if (APR_BUCKET_IS_EOS(b)) {
 end = 1;
 break;
 }
 else if (APR_BUCKET_IS_METADATA(b)) {
 continue;
 }
 bytes = BUFLEN;
 status = apr_bucket_read(b, , ,
  APR_BLOCK_READ);
 count += bytes;

 apr_bucket_delete(b);
 }
 }
 apr_brigade_cleanup(temp_brigade);
 } while (!end && (status == APR_SUCCESS));
 if (status == APR_SUCCESS) {
 return DECLINED;
 } else {
 return HTTP_INTERNAL_SERVER_ERROR;
 }
 }





How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?

2018-06-04 Thread Paul Callahan
In apache modules, my understanding is if a handler declines a request, the
request is passed on to the next suitable handler.   I'm finding though if
I read the bucket_brigade/request body, and then decline the request, the
subsequent handler doesn't get any data.  It is like the act of reading the
bucket brigade consumes it.

I would like to have a request handler read the data, do some task (in this
case just count bytes), and decline the request without consuming the data
for the next handler.

Thank you.

int my_declining_handler(request_rec *r) {
apr_status_t status;
int end = 0;
apr_size_t bytes, count = 0;
const char *buf;
apr_bucket *b;
apr_bucket_brigade *temp_brigade;

// here: header check for content-length/transfer encoding

temp_brigade = apr_brigade_create(r->pool,
r->connection->bucket_alloc);
do {
status = ap_get_brigade(r->input_filters, temp_brigade,
AP_MODE_READBYTES, APR_BLOCK_READ, BUFLEN);
if (status == APR_SUCCESS) {/* Loop over the contents of
temp_brigade */
for (b = APR_BRIGADE_FIRST(temp_brigade);
 b != APR_BRIGADE_SENTINEL(temp_brigade);
 b = APR_BUCKET_NEXT(b)) {
if (APR_BUCKET_IS_EOS(b)) {
end = 1;
break;
}
else if (APR_BUCKET_IS_METADATA(b)) {
continue;
}
bytes = BUFLEN;
status = apr_bucket_read(b, , ,
 APR_BLOCK_READ);
count += bytes;

apr_bucket_delete(b);
}
}
apr_brigade_cleanup(temp_brigade);
} while (!end && (status == APR_SUCCESS));
if (status == APR_SUCCESS) {
return DECLINED;
} else {
return HTTP_INTERNAL_SERVER_ERROR;
}
}