Sending empty response from http_upstream
mdounin at mdounin.ru
Sat Jan 14 18:15:10 UTC 2023
On Fri, Jan 13, 2023 at 11:19:14PM -0500, Jeff Heisz wrote:
> I've been trying to debug an issue with a custom nginx module, which
> is working 'properly' with desktop browsers but failing on Apple
> browsers with an 'invalid response' error message.
> I don't know exactly what it doesn't like (so much fun debugging on
> i-devices) but I am seeing one specific oddity when tracing the
> response from nginx through the desktop debugging console that might
> be the cause.
> My module communicates with an upstream manager process that signals
> the kind of response to be sent along with associated data. The
> response that has the issue is when the manager signals a redirect
> instead of a content response.
> The module extracts the data from the upstream buffer and pushes a
> req->headers_out.location header record with the URL from the
> upstream. It then sets up the response as:
> upstr->headers_in.content_length_n = 0;
> // (tried this) ngx_http_clear_content_length(req);
> upstr->headers_in.status_n = NGX_HTTP_MOVED_TEMPORARILY;
> upstr->state->status = NGX_HTTP_MOVED_TEMPORARILY;
> upstr->buffer.pos += url_len;
> req->header_only = 1;
> upstr->keepalive = 1;
At least "req->header_only = 1;" looks problematic.
In general, you shouldn't touch request properties from an
upstream protocol module. Instead, you should set appropriate
upstream's header_in data, so the upstream module will copy
relevant information when needed (and if needed).
Further, req->header_only is almost always wrong when you are
trying to set it yourself. It is expected to be set for responses
without response body, such as responses to HEAD requests and
304/204 responses, but this is something nginx header filter will
do for you. For other responses, such as 302 in your code, it's
incorrect: these are responses with body, and the response body is
expected to be sent. With content length set to 0 things might
appear to work in some simple cases, but will break nicely if the
content length is removed for some reason (e.g., due to potential
response modifications by some filter, such as SSI or gzip).
That is, things might appear to work correctly with
req->header_only explicitly set to 1, but most likely they aren't.
Further, the fact it's there suggests it was added to "fix"
something, and this in turn might indicate you are doing something
> Basically, a response of zero length and a 302 response code,
> advancing the upstream buffer position to the end of the URL (no
> additional data in the upstream buffer to be processed).
> However, when I examine the response through the debugger, I see the
> appropriate location header but the content-length is always 32677!
> Not zero. Attempting to look at the response shows no data. This is
> highly suspicious as to why an error is occuring on the i-device. I
> should also mention that the upstream response with the redirect URL
> is only 489 bytes, so that's not the source of the value.
> What I don't understand is where that content-length is coming from.
> Other cases where I set a non-zero length and have data in the
> upstream buffer works properly. But for the zero case it doesn't send
> any content but sends this non-zero length. From the code above you
> can see that I tried deleting the response content length information
> in case it was already in place from somewhere but that had no effect.
> Any thoughts? I've tried doing some tracing in the nginx core code
> itself but so far have not been able to see where this length is
> originating from.
It is hard to say anything beyond the above without seeing the
code, but first thing I would recommend to do is to look into
nginx debug logs, see here for details:
Debug logs contain detailed information about request processing,
including all the headers sent, and should be helpful to
understand what's going on.
More information about the nginx-devel