Subrequest using Parent Request Body

Hi,

I’m trying to send a POST Subrequest using the same request_body as the
Parent Request.
The subrequest is sent first, and once it has been finalized the parent
request goes through.
(this is inspired on Maxim’s ngx_http_auth_request_module)

It works almost fine, but if I try to make this subrequest to a location
containing a proxy_pass, I get some weird behavior. (The connection
takes
forever to terminate).

My guess is that the request_body gets read by the upstream, and
discarded.
When the parent request goes through, that data is not available
anymore.
If I create a dummy request_body for the subrequest with temporary
buffers,
it seems to work fine.

I think I might be able to get around this by reading first the original
request_body using ngx_http_read_client_request_body(),
and then copying the content of r->request_body to temporary buffers in
my
subrequest.

But I’m not sure that would be the best way to solve this problem.

Any ideas ?

Thank you,

Matthieu.

matthieu,

i may be experiencing the same issue. built a filter which sends a
subrequest to a location different from the parent request. in my
scenario i’ve seen both the parent and subrequest called first
(varnishlog), haven’t been able to figure out why the order in which
requests are called is not consistent. from the nginx error log it
appears that parent request is written to a temporary file and when the
subrequest is returned from upstream my page will hang. there must be a
way to finalize the request and force the buffer to written to the
client.

peter

Posted at Nginx Forum:

On Mon, May 10, 2010 at 8:30 AM, peterjanovsky [email protected]
wrote:

i may be experiencing the same issue. built a filter which sends a
subrequest to a location different from the parent request. in my scenario
i’ve seen both the parent and subrequest called first (varnishlog), haven’t
been able to figure out why the order in which requests are called is not
consistent.

Because non-blocking I/O takes action here (well, merely a guess,
because I haven’t seen your code and config) :slight_smile:

There’s a consistent “active request” relay chain going on here
though, i.e., the pointer of r->connection->data. One should not take
certain actions when the current “active request” is not the one that
is being operated in most cases, or he will run into troubles :slight_smile: Also,
be very careful with r->main->count, if one does not get it right,
mysterious issues like response truncating and request hanging are not
unusual at all :slight_smile: (In those cases, --with-debug is your friend :))

There’re a bunch of nginx modules that take advantage of subrequests,
like ngx_addition, ngx_ssi, ngx_eval, ngx_echo, ngx_auth_request (and
more are coming!)

Cheers,
-agentzh

Hello!

On Fri, May 07, 2010 at 03:38:54PM -0700, Matthieu T. wrote:

My guess is that the request_body gets read by the upstream, and discarded.
When the parent request goes through, that data is not available anymore.

If you issue subrequest before request body was read by main
request, you likely get SIGSEGV on an attempt to read it. If you
didn’t - you probably just didn’t used -DDEBUG_MALLOC while
compiling nginx (or appropriate malloc options in your OS). It’s
only possible to read request body in main request.

If I create a dummy request_body for the subrequest with temporary buffers,
it seems to work fine.

I think I might be able to get around this by reading first the original
request_body using ngx_http_read_client_request_body(),
and then copying the content of r->request_body to temporary buffers in my
subrequest.

There is no good/reliable way to preserve request body and use it
for two upstream requests. There are no problems with in-memory
buffer (at least I’m not aware of), but once your request body is
buffered to temporary file - it will be released by first
successful upstream request (see ngx_http_upstream_send_response()
in ngx_http_upstream.c).

Using dupfd() on temporary file descriptor may be an option, but
it’s your responsibility to do it properly.

Maxim D.

On Mon, May 10, 2010 at 6:41 PM, Maxim D. [email protected]
wrote:

It works almost fine, but if I try to make this subrequest to a location
compiling nginx (or appropriate malloc options in your OS). It’s
subrequest.

Maxim D.

Hi,

So the way I solved this is to make a full copy of r->request_body into
temporary bufs of the subrequest.

I would create a subrequest using ngx_http_subrequest(),
then use ngx_http_read_client_request_body(r, post_handler), to read
r->request_body and set a function to copy it in the subrquest as the
post_handler.

I was wondering if this could be dangerous, if the subrequest could be
fired
before r->request_body would be completely read, or is it done in a
sequential order ?

Concerning malloc, is :
env MALLOC_OPTIONS=J;
in nginx.conf sufficient ?

Also, is it possible to something such as Valgrind on nginx workers ?

Thank you,

Matthieu.

Hello!

On Wed, May 12, 2010 at 12:15:22PM -0700, Matthieu T. wrote:

[…]

sequential order ?
You have to read body and call ngx_http_subrequest() only when
it’s fully read (i.e. from post_handler).

Concerning malloc, is :
env MALLOC_OPTIONS=J;
in nginx.conf sufficient ?

No. Under FreeBSD nginx respects MALLOC_OPTIONS=J in environment
and activates the same logic as with -DNGX_DEBUG_MALLOC, but it
has to be already set at master process startup.

On other OSes the only available option is
./configure --with-cc-opts="-D NGX_DEBUG_MALLOC".

Also, is it possible to something such as Valgrind on nginx workers ?

Never tried. Though you may disable daemon mode and master process
completely with “daemon off; master_process off;”. This turns
nginx into simple single-process programm which may be
debugged/profiled by almost anything. There are some limitations
(reconfiguration and binary update doesn’t work), but they are
minimal.

Maxim D.