Re: How to read data in a request handler and then return DECLINED without consuming the data in the bucket brigade?
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?
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?
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?
> 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?
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?
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; } }