comparison src/http/modules/ngx_http_chunked_filter_module.c @ 7034:1b068a4e82d8

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>
author Piotr Sikora <piotrsikora@google.com>
date Fri, 24 Mar 2017 03:37:34 -0700
parents f01ab2dbcfdc
children 46e3542d51b3
comparison
equal deleted inserted replaced
7033:ab5117642647 7034:1b068a4e82d8
15 ngx_chain_t *busy; 15 ngx_chain_t *busy;
16 } ngx_http_chunked_filter_ctx_t; 16 } ngx_http_chunked_filter_ctx_t;
17 17
18 18
19 static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); 19 static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
20 static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r,
21 ngx_http_chunked_filter_ctx_t *ctx);
20 22
21 23
22 static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { 24 static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
23 NULL, /* preconfiguration */ 25 NULL, /* preconfiguration */
24 ngx_http_chunked_filter_init, /* postconfiguration */ 26 ngx_http_chunked_filter_init, /* postconfiguration */
67 || r->method == NGX_HTTP_HEAD) 69 || r->method == NGX_HTTP_HEAD)
68 { 70 {
69 return ngx_http_next_header_filter(r); 71 return ngx_http_next_header_filter(r);
70 } 72 }
71 73
72 if (r->headers_out.content_length_n == -1) { 74 if (r->headers_out.content_length_n == -1
73 if (r->http_version < NGX_HTTP_VERSION_11) { 75 || r->expect_trailers)
76 {
77 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
78
79 if (r->http_version >= NGX_HTTP_VERSION_11
80 && clcf->chunked_transfer_encoding)
81 {
82 if (r->expect_trailers) {
83 ngx_http_clear_content_length(r);
84 }
85
86 r->chunked = 1;
87
88 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t));
89 if (ctx == NULL) {
90 return NGX_ERROR;
91 }
92
93 ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
94
95 } else if (r->headers_out.content_length_n == -1) {
74 r->keepalive = 0; 96 r->keepalive = 0;
75
76 } else {
77 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
78
79 if (clcf->chunked_transfer_encoding) {
80 r->chunked = 1;
81
82 ctx = ngx_pcalloc(r->pool,
83 sizeof(ngx_http_chunked_filter_ctx_t));
84 if (ctx == NULL) {
85 return NGX_ERROR;
86 }
87
88 ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
89
90 } else {
91 r->keepalive = 0;
92 }
93 } 97 }
94 } 98 }
95 99
96 return ngx_http_next_header_filter(r); 100 return ngx_http_next_header_filter(r);
97 } 101 }
177 tl->next = out; 181 tl->next = out;
178 out = tl; 182 out = tl;
179 } 183 }
180 184
181 if (cl->buf->last_buf) { 185 if (cl->buf->last_buf) {
182 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); 186 tl = ngx_http_chunked_create_trailers(r, ctx);
183 if (tl == NULL) { 187 if (tl == NULL) {
184 return NGX_ERROR; 188 return NGX_ERROR;
185 } 189 }
186 190
187 b = tl->buf;
188
189 b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
190 b->temporary = 0;
191 b->memory = 1;
192 b->last_buf = 1;
193 b->pos = (u_char *) CRLF "0" CRLF CRLF;
194 b->last = b->pos + 7;
195
196 cl->buf->last_buf = 0; 191 cl->buf->last_buf = 0;
197 192
198 *ll = tl; 193 *ll = tl;
199 194
200 if (size == 0) { 195 if (size == 0) {
201 b->pos += 2; 196 tl->buf->pos += 2;
202 } 197 }
203 198
204 } else if (size > 0) { 199 } else if (size > 0) {
205 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); 200 tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
206 if (tl == NULL) { 201 if (tl == NULL) {
228 223
229 return rc; 224 return rc;
230 } 225 }
231 226
232 227
228 static ngx_chain_t *
229 ngx_http_chunked_create_trailers(ngx_http_request_t *r,
230 ngx_http_chunked_filter_ctx_t *ctx)
231 {
232 size_t len;
233 ngx_buf_t *b;
234 ngx_uint_t i;
235 ngx_chain_t *cl;
236 ngx_list_part_t *part;
237 ngx_table_elt_t *header;
238
239 len = 0;
240
241 part = &r->headers_out.trailers.part;
242 header = part->elts;
243
244 for (i = 0; /* void */; i++) {
245
246 if (i >= part->nelts) {
247 if (part->next == NULL) {
248 break;
249 }
250
251 part = part->next;
252 header = part->elts;
253 i = 0;
254 }
255
256 if (header[i].hash == 0) {
257 continue;
258 }
259
260 len += header[i].key.len + sizeof(": ") - 1
261 + header[i].value.len + sizeof(CRLF) - 1;
262 }
263
264 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
265 if (cl == NULL) {
266 return NULL;
267 }
268
269 b = cl->buf;
270
271 b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
272 b->temporary = 0;
273 b->memory = 1;
274 b->last_buf = 1;
275
276 if (len == 0) {
277 b->pos = (u_char *) CRLF "0" CRLF CRLF;
278 b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1;
279 return cl;
280 }
281
282 len += sizeof(CRLF "0" CRLF CRLF) - 1;
283
284 b->pos = ngx_palloc(r->pool, len);
285 if (b->pos == NULL) {
286 return NULL;
287 }
288
289 b->last = b->pos;
290
291 *b->last++ = CR; *b->last++ = LF;
292 *b->last++ = '0';
293 *b->last++ = CR; *b->last++ = LF;
294
295 part = &r->headers_out.trailers.part;
296 header = part->elts;
297
298 for (i = 0; /* void */; i++) {
299
300 if (i >= part->nelts) {
301 if (part->next == NULL) {
302 break;
303 }
304
305 part = part->next;
306 header = part->elts;
307 i = 0;
308 }
309
310 if (header[i].hash == 0) {
311 continue;
312 }
313
314 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
315 "http trailer: \"%V: %V\"",
316 &header[i].key, &header[i].value);
317
318 b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
319 *b->last++ = ':'; *b->last++ = ' ';
320
321 b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
322 *b->last++ = CR; *b->last++ = LF;
323 }
324
325 *b->last++ = CR; *b->last++ = LF;
326
327 return cl;
328 }
329
330
233 static ngx_int_t 331 static ngx_int_t
234 ngx_http_chunked_filter_init(ngx_conf_t *cf) 332 ngx_http_chunked_filter_init(ngx_conf_t *cf)
235 { 333 {
236 ngx_http_next_header_filter = ngx_http_top_header_filter; 334 ngx_http_next_header_filter = ngx_http_top_header_filter;
237 ngx_http_top_header_filter = ngx_http_chunked_header_filter; 335 ngx_http_top_header_filter = ngx_http_chunked_header_filter;