status-line trailing SP is missing ?

Sergey Kandaurov pluknet at nginx.com
Mon Aug 28 16:59:28 UTC 2023


> On 26 Aug 2023, at 18:21, Jérémy Lal <kapouer at melix.org> wrote:
> 
> Hi,
> 
> https://bugs.debian.org/1050571
> reports that the Status line doesn't always end with space, which seems contradictory to RFC9112 which states:
> "A server MUST send the space that separates the status-code from the reason-phrase even when the reason-phrase is absent (i.e., the status-line would end with the space)."
> 
> Is it a documented nginx 1.24.0 behavior ?
> 

Note that the response line with empty reason phrase
is properly generated since nginx 1.5.6.
The exception remains is proxying FastCGI responses
as there is no distinguished response line in CGI syntax.

The reason is that Status is a CGI header field, and hence
it is parsed by a generic routine that cuts trailing spaces.
But it can have a trailing space per RFC 3875, section 6.3.3.
So it needs a special treatment to preserve SP before empty reason
phrase.  The below patch should help; although it doesn't look
efficient and can be polished, I think this is quite enough for
valid use cases.

# HG changeset patch
# User Sergey Kandaurov <pluknet at nginx.com>
# Date 1693238094 -14400
#      Mon Aug 28 19:54:54 2023 +0400
# Node ID f621c60dfa277774ab5fadb3c8b805957ca3f281
# Parent  2880f60a80c3e2706151dc7b48dc1267e39c47a9
FastCGI: preserved SP for empty Status header reason phrase.

diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2048,7 +2048,25 @@ ngx_http_fastcgi_process_header(ngx_http
                     }
 
                     u->headers_in.status_n = status;
-                    u->headers_in.status_line = *status_line;
+
+                    if (status_line->len == 3) {
+                        /* preserve SP for empty reason phrase */
+
+                        u->headers_in.status_line.data = ngx_pnalloc(r->pool,
+                                                                     5);
+                        if (u->headers_in.status_line.data == NULL) {
+                            return NGX_ERROR;
+                        }
+
+                        ngx_memcpy(u->headers_in.status_line.data,
+                                   status_line->data, 3);
+                        u->headers_in.status_line.data[3] = ' ';
+                        u->headers_in.status_line.data[4] = '\0';
+                        u->headers_in.status_line.len = 4;
+
+                    } else {
+                        u->headers_in.status_line = *status_line;
+                    }
 
                 } else if (u->headers_in.location) {
                     u->headers_in.status_n = 302;

Alternatively, the reason-phrase value from FastCGI response 
can be ignored, nginx will translate it with its own value:

# HG changeset patch
# User Sergey Kandaurov <pluknet at nginx.com>
# Date 1693241054 -14400
#      Mon Aug 28 20:44:14 2023 +0400
# Node ID f1a452cbd57ff4fba1caf3f36cb3624bd45411ea
# Parent  2880f60a80c3e2706151dc7b48dc1267e39c47a9
FastCGI: preserved SP for empty Status header reason phrase.

As per RFC 3875 sec 6.3.3, the status code is always followed by SP.
The header is parsed by the common routine, and it can be challenging
to properly translate empty reason phrase to HTTP/1.1 status line, as
that would require an additional memory allocation.  For simplicity,
the reason phrase is now ignored; the header filter will take care of
it by inserting its own reason phrase for well known status codes.

diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2048,7 +2048,6 @@ ngx_http_fastcgi_process_header(ngx_http
                     }
 
                     u->headers_in.status_n = status;
-                    u->headers_in.status_line = *status_line;
 
                 } else if (u->headers_in.location) {
                     u->headers_in.status_n = 302;

-- 
Sergey Kandaurov


More information about the nginx mailing list