summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Dounin <mdounin@mdounin.ru>2018-03-17 23:04:21 +0300
committerMaxim Dounin <mdounin@mdounin.ru>2018-03-17 23:04:21 +0300
commit7ef115a7e864573478e3f88d6a4ef689a1b73b92 (patch)
tree47dcf313d7d2ac63ff2cda3a134b6f0068b90e8b
parentc554dd1434e1378ac5f83a97b6d250b772941498 (diff)
downloadnginx-7ef115a7e864573478e3f88d6a4ef689a1b73b92.tar.gz
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.
-rw-r--r--src/http/ngx_http_upstream.c96
-rw-r--r--src/http/ngx_http_upstream.h2
2 files changed, 98 insertions, 0 deletions
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index fd00ba50a..6d91c7cf5 100644
--- 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_intercept_errors(ngx_http_request_t *r,
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_response_length_variable(
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_upstream_vars[] = {
{ 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_request_t *r, ngx_http_upstream_t *u)
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_request_t *r, ngx_http_upstream_t *u)
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_http_request_t *r, ngx_http_upstream_t *u)
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_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)
+{
+ 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_http_request_t *r,
}
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_http_request_t *r,
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)
{
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 56b54d13b..a56238d37 100644
--- 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;