Welcome! Log In Create A New Profile

Advanced

[nginx] Added support for trailers in HTTP responses.

Maxim Dounin
June 15, 2017 11:46AM
details: http://hg.nginx.org/nginx/rev/1b068a4e82d8
branches:
changeset: 7034:1b068a4e82d8
user: Piotr Sikora <piotrsikora@google.com>
date: Fri Mar 24 03:37:34 2017 -0700
description:
Added support for trailers in HTTP responses.

Example:

ngx_table_elt_t *h;

h = ngx_list_push(&r->headers_out.trailers);
if (h == NULL) {
return NGX_ERROR;
}

ngx_str_set(&h->key, "Fun");
ngx_str_set(&h->value, "with trailers");
h->hash = ngx_hash_key_lc(h->key.data, h->key.len);

The code above adds "Fun: with trailers" trailer to the response.

Modules that want to emit trailers must set r->expect_trailers = 1
in header filter, otherwise they might not be emitted for HTTP/1.1
responses that aren't already chunked.

This change also adds $sent_trailer_* variables.

Signed-off-by: Piotr Sikora <piotrsikora@google.com>

diffstat:

src/http/modules/ngx_http_chunked_filter_module.c | 154 ++++++++++++++++++----
src/http/ngx_http_core_module.c | 7 +
src/http/ngx_http_request.c | 8 +
src/http/ngx_http_request.h | 2 +
src/http/ngx_http_variables.c | 15 ++
5 files changed, 158 insertions(+), 28 deletions(-)

diffs (291 lines):

diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -17,6 +17,8 @@ typedef struct {


static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
+static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r,
+ ngx_http_chunked_filter_ctx_t *ctx);


static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
@@ -69,27 +71,29 @@ ngx_http_chunked_header_filter(ngx_http_
return ngx_http_next_header_filter(r);
}

- if (r->headers_out.content_length_n == -1) {
- if (r->http_version < NGX_HTTP_VERSION_11) {
- r->keepalive = 0;
-
- } else {
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
- if (clcf->chunked_transfer_encoding) {
- r->chunked = 1;
+ if (r->headers_out.content_length_n == -1
+ || r->expect_trailers)
+ {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

- ctx = ngx_pcalloc(r->pool,
- sizeof(ngx_http_chunked_filter_ctx_t));
- if (ctx == NULL) {
- return NGX_ERROR;
- }
+ if (r->http_version >= NGX_HTTP_VERSION_11
+ && clcf->chunked_transfer_encoding)
+ {
+ if (r->expect_trailers) {
+ ngx_http_clear_content_length(r);
+ }

- ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
+ r->chunked = 1;

- } else {
- r->keepalive = 0;
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
}
+
+ ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
+
+ } else if (r->headers_out.content_length_n == -1) {
+ r->keepalive = 0;
}
}

@@ -179,26 +183,17 @@ ngx_http_chunked_body_filter(ngx_http_re
}

if (cl->buf->last_buf) {
- tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ tl = ngx_http_chunked_create_trailers(r, ctx);
if (tl == NULL) {
return NGX_ERROR;
}

- b = tl->buf;
-
- b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
- b->temporary = 0;
- b->memory = 1;
- b->last_buf = 1;
- b->pos = (u_char *) CRLF "0" CRLF CRLF;
- b->last = b->pos + 7;
-
cl->buf->last_buf = 0;

*ll = tl;

if (size == 0) {
- b->pos += 2;
+ tl->buf->pos += 2;
}

} else if (size > 0) {
@@ -230,6 +225,109 @@ ngx_http_chunked_body_filter(ngx_http_re
}


+static ngx_chain_t *
+ngx_http_chunked_create_trailers(ngx_http_request_t *r,
+ ngx_http_chunked_filter_ctx_t *ctx)
+{
+ size_t len;
+ ngx_buf_t *b;
+ ngx_uint_t i;
+ ngx_chain_t *cl;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+
+ len = 0;
+
+ part = &r->headers_out.trailers.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;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ len += header[i].key.len + sizeof(": ") - 1
+ + header[i].value.len + sizeof(CRLF) - 1;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ b = cl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->temporary = 0;
+ b->memory = 1;
+ b->last_buf = 1;
+
+ if (len == 0) {
+ b->pos = (u_char *) CRLF "0" CRLF CRLF;
+ b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1;
+ return cl;
+ }
+
+ len += sizeof(CRLF "0" CRLF CRLF) - 1;
+
+ b->pos = ngx_palloc(r->pool, len);
+ if (b->pos == NULL) {
+ return NULL;
+ }
+
+ b->last = b->pos;
+
+ *b->last++ = CR; *b->last++ = LF;
+ *b->last++ = '0';
+ *b->last++ = CR; *b->last++ = LF;
+
+ part = &r->headers_out.trailers.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;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http trailer: \"%V: %V\"",
+ &header[i].key, &header[i].value);
+
+ b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
+ *b->last++ = ':'; *b->last++ = ' ';
+
+ b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ *b->last++ = CR; *b->last++ = LF;
+
+ return cl;
+}
+
+
static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2485,6 +2485,13 @@ ngx_http_subrequest(ngx_http_request_t *
return NGX_ERROR;
}

+ if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
sr->main_conf = cscf->ctx->main_conf;
sr->srv_conf = cscf->ctx->srv_conf;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t
return NULL;
}

+ if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_destroy_pool(r->pool);
+ return NULL;
+ }
+
r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (r->ctx == NULL) {
ngx_destroy_pool(r->pool);
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -252,6 +252,7 @@ typedef struct {

typedef struct {
ngx_list_t headers;
+ ngx_list_t trailers;

ngx_uint_t status;
ngx_str_t status_line;
@@ -514,6 +515,7 @@ struct ngx_http_request_s {
unsigned pipeline:1;
unsigned chunked:1;
unsigned header_only:1;
+ unsigned expect_trailers:1;
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned discard_body:1;
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
@@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor
{ ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out,
0, NGX_HTTP_VAR_PREFIX, 0 },

+ { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out,
+ 0, NGX_HTTP_VAR_PREFIX, 0 },
+
{ ngx_string("cookie_"), NULL, ngx_http_variable_cookie,
0, NGX_HTTP_VAR_PREFIX, 0 },

@@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx
}


+static ngx_int_t
+ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_out.trailers.part,
+ sizeof("sent_trailer_") - 1);
+}
+
+
ngx_int_t
ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
ngx_list_part_t *part, size_t prefix)
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[nginx] Added support for trailers in HTTP responses.

Maxim Dounin 392 June 15, 2017 11:46AM



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

Online Users

Guests: 179
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready