[PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-13 Thread Piotr Sikora via nginx-devel
# HG changeset patch
# User Piotr Sikora 
# Date 1490351854 25200
#  Fri Mar 24 03:37:34 2017 -0700
# Node ID 46150fb672e7b92cfbe678bad71df187fcb25ae6
# Parent  73f67e06ab103e0368d1810c6f8cac5c70c4e246
Headers filter: added "add_trailer" directive.

Trailers added using this directive are evaluated after response body
is processed by output filters (but before it's written to the wire),
so it's possible to use variables calculated from the response body
as the trailer value.

Signed-off-by: Piotr Sikora 

diff -r 73f67e06ab10 -r 46150fb672e7 
src/http/modules/ngx_http_headers_filter_module.c
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -48,6 +48,7 @@ typedef struct {
 time_t expires_time;
 ngx_http_complex_value_t  *expires_value;
 ngx_array_t   *headers;
+ngx_array_t   *trailers;
 } ngx_http_headers_conf_t;
 
 
@@ -105,7 +106,15 @@ static ngx_command_t  ngx_http_headers_f
 |NGX_CONF_TAKE23,
   ngx_http_headers_add,
   NGX_HTTP_LOC_CONF_OFFSET,
-  0,
+  offsetof(ngx_http_headers_conf_t, headers),
+  NULL },
+
+{ ngx_string("add_trailer"),
+  NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+|NGX_CONF_TAKE23,
+  ngx_http_headers_add,
+  NGX_HTTP_LOC_CONF_OFFSET,
+  offsetof(ngx_http_headers_conf_t, trailers),
   NULL },
 
   ngx_null_command
@@ -144,6 +153,7 @@ ngx_module_t  ngx_http_headers_filter_mo
 
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_ptngx_http_next_body_filter;
 
 
 static ngx_int_t
@@ -154,10 +164,15 @@ ngx_http_headers_filter(ngx_http_request
 ngx_http_header_val_t*h;
 ngx_http_headers_conf_t  *conf;
 
+if (r != r->main) {
+return ngx_http_next_header_filter(r);
+}
+
 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
 
-if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
-|| r != r->main)
+if (conf->expires == NGX_HTTP_EXPIRES_OFF
+&& conf->headers == NULL
+&& conf->trailers == NULL)
 {
 return ngx_http_next_header_filter(r);
 }
@@ -206,11 +221,101 @@ ngx_http_headers_filter(ngx_http_request
 }
 }
 
+if (conf->trailers) {
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+r->expect_trailers = 1;
+break;
+}
+}
+
 return ngx_http_next_header_filter(r);
 }
 
 
 static ngx_int_t
+ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ngx_str_t value;
+ngx_uint_ti, safe_status;
+ngx_chain_t  *cl;
+ngx_table_elt_t  *t;
+ngx_http_header_val_t*h;
+ngx_http_headers_conf_t  *conf;
+
+conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+if (in == NULL
+|| conf->trailers == NULL
+|| !r->expect_trailers
+|| r->header_only)
+{
+return ngx_http_next_body_filter(r, in);
+}
+
+for (cl = in; cl; cl = cl->next) {
+if (cl->buf->last_buf) {
+break;
+}
+}
+
+if (cl == NULL) {
+return ngx_http_next_body_filter(r, in);
+}
+
+switch (r->headers_out.status) {
+
+case NGX_HTTP_OK:
+case NGX_HTTP_CREATED:
+case NGX_HTTP_NO_CONTENT:
+case NGX_HTTP_PARTIAL_CONTENT:
+case NGX_HTTP_MOVED_PERMANENTLY:
+case NGX_HTTP_MOVED_TEMPORARILY:
+case NGX_HTTP_SEE_OTHER:
+case NGX_HTTP_NOT_MODIFIED:
+case NGX_HTTP_TEMPORARY_REDIRECT:
+case NGX_HTTP_PERMANENT_REDIRECT:
+safe_status = 1;
+break;
+
+default:
+safe_status = 0;
+break;
+}
+
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+if (ngx_http_complex_value(r, [i].value, ) != NGX_OK) {
+return NGX_ERROR;
+}
+
+if (value.len) {
+t = ngx_list_push(>headers_out.trailers);
+if (t == NULL) {
+return NGX_ERROR;
+}
+
+t->key = h[i].key;
+t->value = value;
+t->hash = 1;
+}
+}
+
+return ngx_http_next_body_filter(r, in);
+}
+
+
+static ngx_int_t
 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
 {
 char*err;
@@ -557,6 +662,7 @@ ngx_http_headers_create_conf(ngx_conf_t 
  * set by ngx_pcalloc():
  *
  * conf->headers = NULL;
+ * conf->trailers = NULL;
  * conf->expires_time = 0;
  * 

Re: [PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-06 Thread Maxim Dounin
Hello!

On Mon, Jun 05, 2017 at 09:59:45PM -0700, Piotr Sikora via nginx-devel wrote:

> Hey Maxim,
> 
> > It doesn't look like "if (h[i].value.value.len)" is needed here.
> > It is either true, or the "add_trailer" directive is nop and we
> > already know this while parsing the configuration.
> >
> > -if (h[i].value.value.len) {
> > -r->expect_trailers = 1;
> > -break;
> > -}
> > +r->expect_trailers = 1;
> > +break;
> 
> Well, both "add_header" and "add_trailer" allow setting something like:
> 
> add_trailer Empty "";
> 
> which will get added to headers / trailers list.
> 
> I've added this extra check to avoid forcing chunked encoding with
> such configuration.
> 
> Maybe we should reject it during configuration instead, or ignore this
> case and let it force chunked encoding? Which one do you prefer?

I think it is perfectly ok to ignore this and let if force chunked 
encoding (and this is what the suggested change does).

-- 
Maxim Dounin
http://nginx.org/
___
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel


Re: [PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-05 Thread Piotr Sikora via nginx-devel
Hey Maxim,

> It doesn't look like "if (h[i].value.value.len)" is needed here.
> It is either true, or the "add_trailer" directive is nop and we
> already know this while parsing the configuration.
>
> -if (h[i].value.value.len) {
> -r->expect_trailers = 1;
> -break;
> -}
> +r->expect_trailers = 1;
> +break;

Well, both "add_header" and "add_trailer" allow setting something like:

add_trailer Empty "";

which will get added to headers / trailers list.

I've added this extra check to avoid forcing chunked encoding with
such configuration.

Maybe we should reject it during configuration instead, or ignore this
case and let it force chunked encoding? Which one do you prefer?

Best regards,
Piotr Sikora
___
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel


Re: [PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-05 Thread Maxim Dounin
Hello!

On Fri, Jun 02, 2017 at 08:33:47PM -0700, Piotr Sikora via nginx-devel wrote:

> # HG changeset patch
> # User Piotr Sikora 
> # Date 1490351854 25200
> #  Fri Mar 24 03:37:34 2017 -0700
> # Node ID acdc80c0d4ef8aa2519e2882ff1a3bd4a316ad81
> # Parent  8d74ff6c2015180f5c1f399f492214d7d0a52b3f
> Headers filter: added "add_trailer" directive.
> 
> Trailers added using this directive are evaluated after response body
> is processed by output filters (but before it's written to the wire),
> so it's possible to use variables calculated from the response body
> as the trailer value.
> 
> Signed-off-by: Piotr Sikora 

Overall looks good, see some comments below.

[...]

> @@ -206,11 +223,103 @@ ngx_http_headers_filter(ngx_http_request
>  }
>  }
>  
> +if (conf->trailers) {
> +h = conf->trailers->elts;
> +for (i = 0; i < conf->trailers->nelts; i++) {
> +
> +if (!safe_status && !h[i].always) {
> +continue;
> +}
> +
> +if (h[i].value.value.len) {
> +r->expect_trailers = 1;
> +break;
> +}

It doesn't look like "if (h[i].value.value.len)" is needed here.  
It is either true, or the "add_trailer" directive is nop and we 
already know this while parsing the configuration.

-if (h[i].value.value.len) {
-r->expect_trailers = 1;
-break;
-}
+r->expect_trailers = 1;
+break;
 
[...]

> @@ -741,3 +858,63 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
>  
>  return NGX_CONF_OK;
>  }
> +
> +
> +static char *
> +ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> +{
> +ngx_http_headers_conf_t *hcf = conf;
> +
> +ngx_str_t *value;
> +ngx_http_header_val_t *hv;
> +ngx_http_compile_complex_value_t   ccv;
> +
> +value = cf->args->elts;
> +
> +if (hcf->trailers == NULL) {
> +hcf->trailers = ngx_array_create(cf->pool, 1,
> + sizeof(ngx_http_header_val_t));
> +if (hcf->trailers == NULL) {
> +return NGX_CONF_ERROR;
> +}
> +}
> +
> +hv = ngx_array_push(hcf->trailers);
> +if (hv == NULL) {
> +return NGX_CONF_ERROR;
> +}

[...]

It looks like the ngx_http_headers_add_trailer() function is 
almost exact copy of the ngx_http_headers_add().  It might worth 
to use single function to handle both directives.

diff --git a/src/http/modules/ngx_http_headers_filter_module.c 
b/src/http/modules/ngx_http_headers_filter_module.c
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -73,8 +73,6 @@ static char *ngx_http_headers_expires(ng
 void *conf);
 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
 void *conf);
-static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd,
-void *conf);
 
 
 static ngx_http_set_header_t  ngx_http_set_headers[] = {
@@ -108,15 +106,15 @@ static ngx_command_t  ngx_http_headers_f
 |NGX_CONF_TAKE23,
   ngx_http_headers_add,
   NGX_HTTP_LOC_CONF_OFFSET,
-  0,
+  offsetof(ngx_http_headers_conf_t, headers),
   NULL },
 
 { ngx_string("add_trailer"),
   NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
 |NGX_CONF_TAKE23,
-  ngx_http_headers_add_trailer,
+  ngx_http_headers_add,
   NGX_HTTP_LOC_CONF_OFFSET,
-  0,
+  offsetof(ngx_http_headers_conf_t, trailers),
   NULL },
 
   ngx_null_command
@@ -231,10 +229,8 @@ ngx_http_headers_filter(ngx_http_request
 continue;
 }
 
-if (h[i].value.value.len) {
-r->expect_trailers = 1;
-break;
-}
+r->expect_trailers = 1;
+break;
 }
 }
 
@@ -791,42 +787,49 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
 {
 ngx_http_headers_conf_t *hcf = conf;
 
-ngx_str_t *value;
-ngx_uint_t i;
-ngx_http_header_val_t *hv;
-ngx_http_set_header_t *set;
-ngx_http_compile_complex_value_t   ccv;
+ngx_str_t  *value;
+ngx_uint_t  i;
+ngx_array_t   **headers;
+ngx_http_header_val_t  *hv;
+ngx_http_set_header_t  *set;
+ngx_http_compile_complex_value_tccv;
 
 value = cf->args->elts;
 
-if (hcf->headers == NULL) {
-hcf->headers = ngx_array_create(cf->pool, 1,
-sizeof(ngx_http_header_val_t));
-if (hcf->headers == NULL) {
+headers = (ngx_array_t **) ((char *) hcf + cmd->offset);
+
+if (*headers == NULL) {
+*headers = 

[PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-02 Thread Piotr Sikora via nginx-devel
# HG changeset patch
# User Piotr Sikora 
# Date 1490351854 25200
#  Fri Mar 24 03:37:34 2017 -0700
# Node ID acdc80c0d4ef8aa2519e2882ff1a3bd4a316ad81
# Parent  8d74ff6c2015180f5c1f399f492214d7d0a52b3f
Headers filter: added "add_trailer" directive.

Trailers added using this directive are evaluated after response body
is processed by output filters (but before it's written to the wire),
so it's possible to use variables calculated from the response body
as the trailer value.

Signed-off-by: Piotr Sikora 

diff -r 8d74ff6c2015 -r acdc80c0d4ef 
src/http/modules/ngx_http_headers_filter_module.c
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -48,6 +48,7 @@ typedef struct {
 time_t expires_time;
 ngx_http_complex_value_t  *expires_value;
 ngx_array_t   *headers;
+ngx_array_t   *trailers;
 } ngx_http_headers_conf_t;
 
 
@@ -72,6 +73,8 @@ static char *ngx_http_headers_expires(ng
 void *conf);
 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
 void *conf);
+static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd,
+void *conf);
 
 
 static ngx_http_set_header_t  ngx_http_set_headers[] = {
@@ -108,6 +111,14 @@ static ngx_command_t  ngx_http_headers_f
   0,
   NULL },
 
+{ ngx_string("add_trailer"),
+  NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+|NGX_CONF_TAKE23,
+  ngx_http_headers_add_trailer,
+  NGX_HTTP_LOC_CONF_OFFSET,
+  0,
+  NULL },
+
   ngx_null_command
 };
 
@@ -144,6 +155,7 @@ ngx_module_t  ngx_http_headers_filter_mo
 
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_ptngx_http_next_body_filter;
 
 
 static ngx_int_t
@@ -154,10 +166,15 @@ ngx_http_headers_filter(ngx_http_request
 ngx_http_header_val_t*h;
 ngx_http_headers_conf_t  *conf;
 
+if (r != r->main) {
+return ngx_http_next_header_filter(r);
+}
+
 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
 
-if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
-|| r != r->main)
+if (conf->expires == NGX_HTTP_EXPIRES_OFF
+&& conf->headers == NULL
+&& conf->trailers == NULL)
 {
 return ngx_http_next_header_filter(r);
 }
@@ -206,11 +223,103 @@ ngx_http_headers_filter(ngx_http_request
 }
 }
 
+if (conf->trailers) {
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+if (h[i].value.value.len) {
+r->expect_trailers = 1;
+break;
+}
+}
+}
+
 return ngx_http_next_header_filter(r);
 }
 
 
 static ngx_int_t
+ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ngx_str_t value;
+ngx_uint_ti, safe_status;
+ngx_chain_t  *cl;
+ngx_table_elt_t  *t;
+ngx_http_header_val_t*h;
+ngx_http_headers_conf_t  *conf;
+
+conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+if (in == NULL
+|| conf->trailers == NULL
+|| !r->expect_trailers
+|| r->header_only)
+{
+return ngx_http_next_body_filter(r, in);
+}
+
+for (cl = in; cl; cl = cl->next) {
+if (cl->buf->last_buf) {
+break;
+}
+}
+
+if (cl == NULL) {
+return ngx_http_next_body_filter(r, in);
+}
+
+switch (r->headers_out.status) {
+
+case NGX_HTTP_OK:
+case NGX_HTTP_CREATED:
+case NGX_HTTP_NO_CONTENT:
+case NGX_HTTP_PARTIAL_CONTENT:
+case NGX_HTTP_MOVED_PERMANENTLY:
+case NGX_HTTP_MOVED_TEMPORARILY:
+case NGX_HTTP_SEE_OTHER:
+case NGX_HTTP_NOT_MODIFIED:
+case NGX_HTTP_TEMPORARY_REDIRECT:
+case NGX_HTTP_PERMANENT_REDIRECT:
+safe_status = 1;
+break;
+
+default:
+safe_status = 0;
+break;
+}
+
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+if (ngx_http_complex_value(r, [i].value, ) != NGX_OK) {
+return NGX_ERROR;
+}
+
+if (value.len) {
+t = ngx_list_push(>headers_out.trailers);
+if (t == NULL) {
+return NGX_ERROR;
+}
+
+t->key = h[i].key;
+t->value = value;
+t->hash = 1;
+}
+}
+
+return ngx_http_next_body_filter(r, in);
+}
+
+
+static ngx_int_t
 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
 {
 char*err;
@@ -557,6 +666,7 @@ 

[PATCH 3 of 3] Headers filter: added "add_trailer" directive

2017-06-02 Thread Piotr Sikora via nginx-devel
# HG changeset patch
# User Piotr Sikora 
# Date 1490351854 25200
#  Fri Mar 24 03:37:34 2017 -0700
# Node ID 52201fff87e8c5a096287cf206cdcc14c0d81ce7
# Parent  e84aa49c5bc7a3250d4844b581e4bf3ed42db5f5
Headers filter: added "add_trailer" directive.

Trailers added using this directive are evaluated after response body
is processed by output filters (but before it's written to the wire),
so it's possible to use variables calculated from the response body
as the trailer value.

Signed-off-by: Piotr Sikora 

diff -r e84aa49c5bc7 -r 52201fff87e8 
src/http/modules/ngx_http_headers_filter_module.c
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -48,6 +48,7 @@ typedef struct {
 time_t expires_time;
 ngx_http_complex_value_t  *expires_value;
 ngx_array_t   *headers;
+ngx_array_t   *trailers;
 } ngx_http_headers_conf_t;
 
 
@@ -72,6 +73,8 @@ static char *ngx_http_headers_expires(ng
 void *conf);
 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
 void *conf);
+static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd,
+void *conf);
 
 
 static ngx_http_set_header_t  ngx_http_set_headers[] = {
@@ -108,6 +111,14 @@ static ngx_command_t  ngx_http_headers_f
   0,
   NULL },
 
+{ ngx_string("add_trailer"),
+  NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+|NGX_CONF_TAKE23,
+  ngx_http_headers_add_trailer,
+  NGX_HTTP_LOC_CONF_OFFSET,
+  0,
+  NULL },
+
   ngx_null_command
 };
 
@@ -144,20 +155,30 @@ ngx_module_t  ngx_http_headers_filter_mo
 
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_ptngx_http_next_body_filter;
 
 
 static ngx_int_t
 ngx_http_headers_filter(ngx_http_request_t *r)
 {
-ngx_str_t value;
-ngx_uint_ti, safe_status;
-ngx_http_header_val_t*h;
-ngx_http_headers_conf_t  *conf;
+u_char*p, *data;
+size_t len;
+ngx_str_t  value;
+ngx_uint_t i, safe_status;
+ngx_table_elt_t   *t;
+ngx_http_header_val_t *h;
+ngx_http_headers_conf_t   *conf;
+ngx_http_core_loc_conf_t  *clcf;
+
+if (r != r->main) {
+return ngx_http_next_header_filter(r);
+}
 
 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
 
-if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
-|| r != r->main)
+if (conf->expires == NGX_HTTP_EXPIRES_OFF
+&& conf->headers == NULL
+&& conf->trailers == NULL)
 {
 return ngx_http_next_header_filter(r);
 }
@@ -206,11 +227,162 @@ ngx_http_headers_filter(ngx_http_request
 }
 }
 
+if (conf->trailers) {
+
+if (r->header_only
+|| r->headers_out.status == NGX_HTTP_NOT_MODIFIED
+|| r->headers_out.status == NGX_HTTP_NO_CONTENT
+|| r->headers_out.status < NGX_HTTP_OK
+|| r->method == NGX_HTTP_HEAD)
+{
+   return ngx_http_next_header_filter(r);
+}
+
+if (r->http_version < NGX_HTTP_VERSION_20) {
+clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+if (!clcf->chunked_transfer_encoding) {
+return ngx_http_next_header_filter(r);
+}
+}
+
+len = 0;
+
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+if (h[i].value.value.len) {
+len += h[i].key.len + sizeof(", ") - 1;
+}
+}
+
+if (len == 0) {
+return ngx_http_next_header_filter(r);
+}
+
+len -= sizeof(", ") - 1;
+
+data = ngx_pnalloc(r->pool, len);
+if (data == NULL) {
+return NGX_ERROR;
+}
+
+t = ngx_list_push(>headers_out.headers);
+if (t == NULL) {
+return NGX_ERROR;
+}
+
+p = data;
+
+h = conf->trailers->elts;
+for (i = 0; i < conf->trailers->nelts; i++) {
+
+if (!safe_status && !h[i].always) {
+continue;
+}
+
+if (h[i].value.value.len) {
+p = ngx_copy(p, h[i].key.data, h[i].key.len);
+
+if (p == data + len) {
+break;
+}
+
+*p++ = ','; *p++ = ' ';
+}
+}
+
+ngx_str_set(>key, "Trailer");
+t->value.data = data;
+t->value.len = len;
+t->hash = 1;
+
+r->expect_trailers = 1;
+}
+
 return ngx_http_next_header_filter(r);
 }
 
 
 static ngx_int_t