Welcome! Log In Create A New Profile

Advanced

[nginx] FastCGI: combining headers with identical names (ticket #1724).

Sergey Kandaurov
May 30, 2022 06:34PM
details: https://hg.nginx.org/nginx/rev/f8f6b9fee66a
branches:
changeset: 8020:f8f6b9fee66a
user: Maxim Dounin <mdounin@mdounin.ru>
date: Mon May 30 21:25:27 2022 +0300
description:
FastCGI: combining headers with identical names (ticket #1724).

FastCGI responder is expected to receive CGI/1.1 environment variables
in the parameters (see section "6.2 Responder" of the FastCGI specification).
Obviously enough, there cannot be multiple environment variables with
the same name.

Further, CGI specification (RFC 3875, section "4.1.18. Protocol-Specific
Meta-Variables") explicitly requires to combine headers: "If multiple
header fields with the same field-name are received then the server MUST
rewrite them as a single value having the same semantics".

diffstat:

src/core/ngx_hash.h | 7 ++-
src/http/modules/ngx_http_fastcgi_module.c | 70 +++++++++++++++++++++++-----
src/http/ngx_http_core_module.c | 74 ++++++++++++++++++++++++++++++
src/http/ngx_http_core_module.h | 2 +
src/http/ngx_http_request.h | 2 +
5 files changed, 140 insertions(+), 15 deletions(-)

diffs (264 lines):

diff -r 0e562a332529 -r f8f6b9fee66a src/core/ngx_hash.h
--- a/src/core/ngx_hash.h Mon May 30 21:25:25 2022 +0300
+++ b/src/core/ngx_hash.h Mon May 30 21:25:27 2022 +0300
@@ -89,12 +89,15 @@ typedef struct {
} ngx_hash_keys_arrays_t;


-typedef struct {
+typedef struct ngx_table_elt_s ngx_table_elt_t;
+
+struct ngx_table_elt_s {
ngx_uint_t hash;
ngx_str_t key;
ngx_str_t value;
u_char *lowcase_key;
-} ngx_table_elt_t;
+ ngx_table_elt_t *next;
+};


void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);
diff -r 0e562a332529 -r f8f6b9fee66a src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c Mon May 30 21:25:25 2022 +0300
+++ b/src/http/modules/ngx_http_fastcgi_module.c Mon May 30 21:25:27 2022 +0300
@@ -835,14 +835,14 @@ static ngx_int_t
ngx_http_fastcgi_create_request(ngx_http_request_t *r)
{
off_t file_pos;
- u_char ch, *pos, *lowcase_key;
+ u_char ch, sep, *pos, *lowcase_key;
size_t size, len, key_len, val_len, padding,
allocated;
ngx_uint_t i, n, next, hash, skip_empty, header_params;
ngx_buf_t *b;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
- ngx_table_elt_t *header, **ignored;
+ ngx_table_elt_t *header, *hn, **ignored;
ngx_http_upstream_t *u;
ngx_http_script_code_pt code;
ngx_http_script_engine_t e, le;
@@ -900,7 +900,11 @@ ngx_http_fastcgi_create_request(ngx_http
allocated = 0;
lowcase_key = NULL;

- if (params->number) {
+ if (ngx_http_link_multi_headers(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (params->number || r->headers_in.multi) {
n = 0;
part = &r->headers_in.headers.part;

@@ -930,6 +934,12 @@ ngx_http_fastcgi_create_request(ngx_http
i = 0;
}

+ for (n = 0; n < header_params; n++) {
+ if (&header[i] == ignored[n]) {
+ goto next_length;
+ }
+ }
+
if (params->number) {
if (allocated < header[i].key.len) {
allocated = header[i].key.len + 16;
@@ -959,15 +969,23 @@ ngx_http_fastcgi_create_request(ngx_http
ignored[header_params++] = &header[i];
continue;
}
-
- n += sizeof("HTTP_") - 1;
-
- } else {
- n = sizeof("HTTP_") - 1 + header[i].key.len;
}

- len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)
- + n + header[i].value.len;
+ key_len = sizeof("HTTP_") - 1 + header[i].key.len;
+
+ val_len = header[i].value.len;
+
+ for (hn = header[i].next; hn; hn = hn->next) {
+ val_len += hn->value.len + 2;
+ ignored[header_params++] = hn;
+ }
+
+ len += ((key_len > 127) ? 4 : 1) + key_len
+ + ((val_len > 127) ? 4 : 1) + val_len;
+
+ next_length:
+
+ continue;
}
}

@@ -1109,7 +1127,7 @@ ngx_http_fastcgi_create_request(ngx_http

for (n = 0; n < header_params; n++) {
if (&header[i] == ignored[n]) {
- goto next;
+ goto next_value;
}
}

@@ -1125,6 +1143,11 @@ ngx_http_fastcgi_create_request(ngx_http
}

val_len = header[i].value.len;
+
+ for (hn = header[i].next; hn; hn = hn->next) {
+ val_len += hn->value.len + 2;
+ }
+
if (val_len > 127) {
*b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
*b->last++ = (u_char) ((val_len >> 16) & 0xff);
@@ -1150,13 +1173,34 @@ ngx_http_fastcgi_create_request(ngx_http
*b->last++ = ch;
}

- b->last = ngx_copy(b->last, header[i].value.data, val_len);
+ b->last = ngx_copy(b->last, header[i].value.data,
+ header[i].value.len);
+
+ if (header[i].next) {
+
+ if (header[i].key.len == sizeof("Cookie") - 1
+ && ngx_strncasecmp(header[i].key.data, (u_char *) "Cookie",
+ sizeof("Cookie") - 1)
+ == 0)
+ {
+ sep = ';';
+
+ } else {
+ sep = ',';
+ }
+
+ for (hn = header[i].next; hn; hn = hn->next) {
+ *b->last++ = sep;
+ *b->last++ = ' ';
+ b->last = ngx_copy(b->last, hn->value.data, hn->value.len);
+ }
+ }

ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"fastcgi param: \"%*s: %*s\"",
key_len, b->last - (key_len + val_len),
val_len, b->last - val_len);
- next:
+ next_value:

continue;
}
diff -r 0e562a332529 -r f8f6b9fee66a src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c Mon May 30 21:25:25 2022 +0300
+++ b/src/http/ngx_http_core_module.c Mon May 30 21:25:27 2022 +0300
@@ -2802,6 +2802,80 @@ ngx_http_get_forwarded_addr_internal(ngx
}


+ngx_int_t
+ngx_http_link_multi_headers(ngx_http_request_t *r)
+{
+ ngx_uint_t i, j;
+ ngx_list_part_t *part, *ppart;
+ ngx_table_elt_t *header, *pheader, **ph;
+
+ if (r->headers_in.multi_linked) {
+ return NGX_OK;
+ }
+
+ r->headers_in.multi_linked = 1;
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ header[i].next = NULL;
+
+ /*
+ * search for previous headers with the same name;
+ * if there are any, link to them
+ */
+
+ ppart = &r->headers_in.headers.part;
+ pheader = ppart->elts;
+
+ for (j = 0; /* void */; j++) {
+
+ if (j >= ppart->nelts) {
+ if (ppart->next == NULL) {
+ break;
+ }
+
+ ppart = ppart->next;
+ pheader = ppart->elts;
+ j = 0;
+ }
+
+ if (part == ppart && i == j) {
+ break;
+ }
+
+ if (header[i].key.len == pheader[j].key.len
+ && ngx_strncasecmp(header[i].key.data, pheader[j].key.data,
+ header[i].key.len)
+ == 0)
+ {
+ ph = &pheader[j].next;
+ while (*ph) { ph = &(*ph)->next; }
+ *ph = &header[i];
+
+ r->headers_in.multi = 1;
+
+ break;
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
diff -r 0e562a332529 -r f8f6b9fee66a src/http/ngx_http_core_module.h
--- a/src/http/ngx_http_core_module.h Mon May 30 21:25:25 2022 +0300
+++ b/src/http/ngx_http_core_module.h Mon May 30 21:25:27 2022 +0300
@@ -532,6 +532,8 @@ ngx_int_t ngx_http_get_forwarded_addr(ng
ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
int recursive);

+ngx_int_t ngx_http_link_multi_headers(ngx_http_request_t *r);
+

extern ngx_module_t ngx_http_core_module;

diff -r 0e562a332529 -r f8f6b9fee66a src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h Mon May 30 21:25:25 2022 +0300
+++ b/src/http/ngx_http_request.h Mon May 30 21:25:27 2022 +0300
@@ -242,6 +242,8 @@ typedef struct {

unsigned connection_type:2;
unsigned chunked:1;
+ unsigned multi:1;
+ unsigned multi_linked:1;
unsigned msie:1;
unsigned msie6:1;
unsigned opera:1;
_______________________________________________
nginx-devel mailing list -- nginx-devel@nginx.org
To unsubscribe send an email to nginx-devel-leave@nginx.org
Subject Author Views Posted

[nginx] FastCGI: combining headers with identical names (ticket #1724).

Sergey Kandaurov 106 May 30, 2022 06:34PM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 50
Record Number of Users: 6 on February 13, 2018
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready