comparison src/http/modules/ngx_http_headers_filter_module.c @ 7036:8666da1ecf33

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 <piotrsikora@google.com>
author Piotr Sikora <piotrsikora@google.com>
date Fri, 24 Mar 2017 03:37:34 -0700
parents 057ec63be834
children 6ba68ad8b24c
comparison
equal deleted inserted replaced
7035:4e784e095a97 7036:8666da1ecf33
46 typedef struct { 46 typedef struct {
47 ngx_http_expires_t expires; 47 ngx_http_expires_t expires;
48 time_t expires_time; 48 time_t expires_time;
49 ngx_http_complex_value_t *expires_value; 49 ngx_http_complex_value_t *expires_value;
50 ngx_array_t *headers; 50 ngx_array_t *headers;
51 ngx_array_t *trailers;
51 } ngx_http_headers_conf_t; 52 } ngx_http_headers_conf_t;
52 53
53 54
54 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, 55 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
55 ngx_http_headers_conf_t *conf); 56 ngx_http_headers_conf_t *conf);
103 { ngx_string("add_header"), 104 { ngx_string("add_header"),
104 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF 105 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
105 |NGX_CONF_TAKE23, 106 |NGX_CONF_TAKE23,
106 ngx_http_headers_add, 107 ngx_http_headers_add,
107 NGX_HTTP_LOC_CONF_OFFSET, 108 NGX_HTTP_LOC_CONF_OFFSET,
108 0, 109 offsetof(ngx_http_headers_conf_t, headers),
110 NULL },
111
112 { ngx_string("add_trailer"),
113 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
114 |NGX_CONF_TAKE23,
115 ngx_http_headers_add,
116 NGX_HTTP_LOC_CONF_OFFSET,
117 offsetof(ngx_http_headers_conf_t, trailers),
109 NULL }, 118 NULL },
110 119
111 ngx_null_command 120 ngx_null_command
112 }; 121 };
113 122
142 NGX_MODULE_V1_PADDING 151 NGX_MODULE_V1_PADDING
143 }; 152 };
144 153
145 154
146 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 155 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
156 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
147 157
148 158
149 static ngx_int_t 159 static ngx_int_t
150 ngx_http_headers_filter(ngx_http_request_t *r) 160 ngx_http_headers_filter(ngx_http_request_t *r)
151 { 161 {
152 ngx_str_t value; 162 ngx_str_t value;
153 ngx_uint_t i, safe_status; 163 ngx_uint_t i, safe_status;
154 ngx_http_header_val_t *h; 164 ngx_http_header_val_t *h;
155 ngx_http_headers_conf_t *conf; 165 ngx_http_headers_conf_t *conf;
156 166
167 if (r != r->main) {
168 return ngx_http_next_header_filter(r);
169 }
170
157 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); 171 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
158 172
159 if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) 173 if (conf->expires == NGX_HTTP_EXPIRES_OFF
160 || r != r->main) 174 && conf->headers == NULL
175 && conf->trailers == NULL)
161 { 176 {
162 return ngx_http_next_header_filter(r); 177 return ngx_http_next_header_filter(r);
163 } 178 }
164 179
165 switch (r->headers_out.status) { 180 switch (r->headers_out.status) {
204 return NGX_ERROR; 219 return NGX_ERROR;
205 } 220 }
206 } 221 }
207 } 222 }
208 223
224 if (conf->trailers) {
225 h = conf->trailers->elts;
226 for (i = 0; i < conf->trailers->nelts; i++) {
227
228 if (!safe_status && !h[i].always) {
229 continue;
230 }
231
232 r->expect_trailers = 1;
233 break;
234 }
235 }
236
209 return ngx_http_next_header_filter(r); 237 return ngx_http_next_header_filter(r);
238 }
239
240
241 static ngx_int_t
242 ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
243 {
244 ngx_str_t value;
245 ngx_uint_t i, safe_status;
246 ngx_chain_t *cl;
247 ngx_table_elt_t *t;
248 ngx_http_header_val_t *h;
249 ngx_http_headers_conf_t *conf;
250
251 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
252
253 if (in == NULL
254 || conf->trailers == NULL
255 || !r->expect_trailers
256 || r->header_only)
257 {
258 return ngx_http_next_body_filter(r, in);
259 }
260
261 for (cl = in; cl; cl = cl->next) {
262 if (cl->buf->last_buf) {
263 break;
264 }
265 }
266
267 if (cl == NULL) {
268 return ngx_http_next_body_filter(r, in);
269 }
270
271 switch (r->headers_out.status) {
272
273 case NGX_HTTP_OK:
274 case NGX_HTTP_CREATED:
275 case NGX_HTTP_NO_CONTENT:
276 case NGX_HTTP_PARTIAL_CONTENT:
277 case NGX_HTTP_MOVED_PERMANENTLY:
278 case NGX_HTTP_MOVED_TEMPORARILY:
279 case NGX_HTTP_SEE_OTHER:
280 case NGX_HTTP_NOT_MODIFIED:
281 case NGX_HTTP_TEMPORARY_REDIRECT:
282 case NGX_HTTP_PERMANENT_REDIRECT:
283 safe_status = 1;
284 break;
285
286 default:
287 safe_status = 0;
288 break;
289 }
290
291 h = conf->trailers->elts;
292 for (i = 0; i < conf->trailers->nelts; i++) {
293
294 if (!safe_status && !h[i].always) {
295 continue;
296 }
297
298 if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
299 return NGX_ERROR;
300 }
301
302 if (value.len) {
303 t = ngx_list_push(&r->headers_out.trailers);
304 if (t == NULL) {
305 return NGX_ERROR;
306 }
307
308 t->key = h[i].key;
309 t->value = value;
310 t->hash = 1;
311 }
312 }
313
314 return ngx_http_next_body_filter(r, in);
210 } 315 }
211 316
212 317
213 static ngx_int_t 318 static ngx_int_t
214 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) 319 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
555 660
556 /* 661 /*
557 * set by ngx_pcalloc(): 662 * set by ngx_pcalloc():
558 * 663 *
559 * conf->headers = NULL; 664 * conf->headers = NULL;
665 * conf->trailers = NULL;
560 * conf->expires_time = 0; 666 * conf->expires_time = 0;
561 * conf->expires_value = NULL; 667 * conf->expires_value = NULL;
562 */ 668 */
563 669
564 conf->expires = NGX_HTTP_EXPIRES_UNSET; 670 conf->expires = NGX_HTTP_EXPIRES_UNSET;
585 691
586 if (conf->headers == NULL) { 692 if (conf->headers == NULL) {
587 conf->headers = prev->headers; 693 conf->headers = prev->headers;
588 } 694 }
589 695
696 if (conf->trailers == NULL) {
697 conf->trailers = prev->trailers;
698 }
699
590 return NGX_CONF_OK; 700 return NGX_CONF_OK;
591 } 701 }
592 702
593 703
594 static ngx_int_t 704 static ngx_int_t
595 ngx_http_headers_filter_init(ngx_conf_t *cf) 705 ngx_http_headers_filter_init(ngx_conf_t *cf)
596 { 706 {
597 ngx_http_next_header_filter = ngx_http_top_header_filter; 707 ngx_http_next_header_filter = ngx_http_top_header_filter;
598 ngx_http_top_header_filter = ngx_http_headers_filter; 708 ngx_http_top_header_filter = ngx_http_headers_filter;
709
710 ngx_http_next_body_filter = ngx_http_top_body_filter;
711 ngx_http_top_body_filter = ngx_http_trailers_filter;
599 712
600 return NGX_OK; 713 return NGX_OK;
601 } 714 }
602 715
603 716
672 static char * 785 static char *
673 ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 786 ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
674 { 787 {
675 ngx_http_headers_conf_t *hcf = conf; 788 ngx_http_headers_conf_t *hcf = conf;
676 789
677 ngx_str_t *value; 790 ngx_str_t *value;
678 ngx_uint_t i; 791 ngx_uint_t i;
679 ngx_http_header_val_t *hv; 792 ngx_array_t **headers;
680 ngx_http_set_header_t *set; 793 ngx_http_header_val_t *hv;
681 ngx_http_compile_complex_value_t ccv; 794 ngx_http_set_header_t *set;
795 ngx_http_compile_complex_value_t ccv;
682 796
683 value = cf->args->elts; 797 value = cf->args->elts;
684 798
685 if (hcf->headers == NULL) { 799 headers = (ngx_array_t **) ((char *) hcf + cmd->offset);
686 hcf->headers = ngx_array_create(cf->pool, 1, 800
687 sizeof(ngx_http_header_val_t)); 801 if (*headers == NULL) {
688 if (hcf->headers == NULL) { 802 *headers = ngx_array_create(cf->pool, 1,
803 sizeof(ngx_http_header_val_t));
804 if (*headers == NULL) {
689 return NGX_CONF_ERROR; 805 return NGX_CONF_ERROR;
690 } 806 }
691 } 807 }
692 808
693 hv = ngx_array_push(hcf->headers); 809 hv = ngx_array_push(*headers);
694 if (hv == NULL) { 810 if (hv == NULL) {
695 return NGX_CONF_ERROR; 811 return NGX_CONF_ERROR;
696 } 812 }
697 813
698 hv->key = value[1]; 814 hv->key = value[1];
699 hv->handler = ngx_http_add_header; 815 hv->handler = NULL;
700 hv->offset = 0; 816 hv->offset = 0;
701 hv->always = 0; 817 hv->always = 0;
702 818
703 set = ngx_http_set_headers; 819 if (headers == &hcf->headers) {
704 for (i = 0; set[i].name.len; i++) { 820 hv->handler = ngx_http_add_header;
705 if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { 821
706 continue; 822 set = ngx_http_set_headers;
707 } 823 for (i = 0; set[i].name.len; i++) {
708 824 if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
709 hv->offset = set[i].offset; 825 continue;
710 hv->handler = set[i].handler; 826 }
711 827
712 break; 828 hv->offset = set[i].offset;
829 hv->handler = set[i].handler;
830
831 break;
832 }
713 } 833 }
714 834
715 if (value[2].len == 0) { 835 if (value[2].len == 0) {
716 ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); 836 ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
717 837