mirror of
https://github.com/nginx/nginx.git
synced 2025-06-06 00:42:40 +08:00
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>
This commit is contained in:
parent
fa0992ed29
commit
cfdce50657
@ -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_request_t *r)
|
||||
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;
|
||||
if (r->headers_out.content_length_n == -1
|
||||
|| r->expect_trailers)
|
||||
{
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
} else {
|
||||
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
||||
|
||||
if (clcf->chunked_transfer_encoding) {
|
||||
r->chunked = 1;
|
||||
|
||||
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 {
|
||||
r->keepalive = 0;
|
||||
if (r->http_version >= NGX_HTTP_VERSION_11
|
||||
&& clcf->chunked_transfer_encoding)
|
||||
{
|
||||
if (r->expect_trailers) {
|
||||
ngx_http_clear_content_length(r);
|
||||
}
|
||||
|
||||
r->chunked = 1;
|
||||
|
||||
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_request_t *r, ngx_chain_t *in)
|
||||
}
|
||||
|
||||
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_request_t *r, ngx_chain_t *in)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -2485,6 +2485,13 @@ ngx_http_subrequest(ngx_http_request_t *r,
|
||||
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;
|
||||
|
@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t *c)
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
|
||||
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_core_variables[] = {
|
||||
{ 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_http_request_t *r,
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user