Mercurial > hg > nginx
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; |