changeset 7230:098bbd076a2d

Upstream: trailers support, u->conf->pass_trailers flag. Basic trailer headers support allows one to access response trailers via the $upstream_trailer_* variables. Additionally, the u->conf->pass_trailers flag was introduced. When the flag is set, trailer headers from the upstream response are passed to the client. Like normal headers, trailer headers will be hidden if present in u->conf->hide_headers_hash.
author Maxim Dounin <mdounin@mdounin.ru>
date Sat, 17 Mar 2018 23:04:21 +0300
parents 87e9e4aabf1b
children 22f7bdbd96d3
files src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h
diffstat 2 files changed, 98 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -55,6 +55,8 @@ static ngx_int_t ngx_http_upstream_inter
 static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
 static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_response(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
@@ -164,6 +166,8 @@ static ngx_int_t ngx_http_upstream_respo
     ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
@@ -423,6 +427,9 @@ static ngx_http_variable_t  ngx_http_ups
     { ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable,
       0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
 
+    { ngx_string("upstream_trailer_"), NULL, ngx_http_upstream_trailer_variable,
+      0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
+
     { ngx_string("upstream_cookie_"), NULL, ngx_http_upstream_cookie_variable,
       0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
 
@@ -1046,6 +1053,13 @@ ngx_http_upstream_cache_send(ngx_http_re
         return NGX_ERROR;
     }
 
+    if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+                      sizeof(ngx_table_elt_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     rc = u->process_header(r);
 
     if (rc == NGX_OK) {
@@ -1883,6 +1897,13 @@ ngx_http_upstream_reinit(ngx_http_reques
         return NGX_ERROR;
     }
 
+    if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+                      sizeof(ngx_table_elt_t))
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     /* reinit the request chain */
 
     file_pos = 0;
@@ -2237,6 +2258,15 @@ ngx_http_upstream_process_header(ngx_htt
             return;
         }
 
+        if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+                          sizeof(ngx_table_elt_t))
+            != NGX_OK)
+        {
+            ngx_http_upstream_finalize_request(r, u,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
 #if (NGX_HTTP_CACHE)
 
         if (r->cache) {
@@ -2735,6 +2765,51 @@ ngx_http_upstream_process_headers(ngx_ht
 }
 
 
+static ngx_int_t
+ngx_http_upstream_process_trailers(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    ngx_uint_t        i;
+    ngx_list_part_t  *part;
+    ngx_table_elt_t  *h, *ho;
+
+    if (!u->conf->pass_trailers) {
+        return NGX_OK;
+    }
+
+    part = &u->headers_in.trailers.part;
+    h = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            h = part->elts;
+            i = 0;
+        }
+
+        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+                          h[i].lowcase_key, h[i].key.len))
+        {
+            continue;
+        }
+
+        ho = ngx_list_push(&r->headers_out.trailers);
+        if (ho == NULL) {
+            return NGX_ERROR;
+        }
+
+        *ho = h[i];
+    }
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
@@ -4272,6 +4347,12 @@ ngx_http_upstream_finalize_request(ngx_h
     }
 
     if (rc == 0) {
+
+        if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_ERROR);
+            return;
+        }
+
         rc = ngx_http_send_special(r, NGX_HTTP_LAST);
 
     } else if (flush) {
@@ -5382,6 +5463,21 @@ ngx_http_upstream_header_variable(ngx_ht
 
 
 static ngx_int_t
+ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    if (r->upstream == NULL) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+                                        &r->upstream->headers_in.trailers.part,
+                                        sizeof("upstream_trailer_") - 1);
+}
+
+
+static ngx_int_t
 ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -222,6 +222,7 @@ typedef struct {
     signed                           store:2;
     unsigned                         intercept_404:1;
     unsigned                         change_buffering:1;
+    unsigned                         pass_trailers:1;
 
 #if (NGX_HTTP_SSL || NGX_COMPAT)
     ngx_ssl_t                       *ssl;
@@ -251,6 +252,7 @@ typedef struct {
 
 typedef struct {
     ngx_list_t                       headers;
+    ngx_list_t                       trailers;
 
     ngx_uint_t                       status_n;
     ngx_str_t                        status_line;