~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_headers_filter_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  1 
  2 /*
  3  * Copyright (C) Igor Sysoev
  4  * Copyright (C) Nginx, Inc.
  5  */
  6 
  7 
  8 #include <ngx_config.h>
  9 #include <ngx_core.h>
 10 #include <ngx_http.h>
 11 
 12 
 13 typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
 14 
 15 typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
 16     ngx_http_header_val_t *hv, ngx_str_t *value);
 17 
 18 
 19 typedef struct {
 20     ngx_str_t                  name;
 21     ngx_uint_t                 offset;
 22     ngx_http_set_header_pt     handler;
 23 } ngx_http_set_header_t;
 24 
 25 
 26 struct ngx_http_header_val_s {
 27     ngx_http_complex_value_t   value;
 28     ngx_str_t                  key;
 29     ngx_http_set_header_pt     handler;
 30     ngx_uint_t                 offset;
 31 };
 32 
 33 
 34 typedef enum {
 35     NGX_HTTP_EXPIRES_OFF,
 36     NGX_HTTP_EXPIRES_EPOCH,
 37     NGX_HTTP_EXPIRES_MAX,
 38     NGX_HTTP_EXPIRES_ACCESS,
 39     NGX_HTTP_EXPIRES_MODIFIED,
 40     NGX_HTTP_EXPIRES_DAILY,
 41     NGX_HTTP_EXPIRES_UNSET
 42 } ngx_http_expires_t;
 43 
 44 
 45 typedef struct {
 46     ngx_http_expires_t       expires;
 47     time_t                   expires_time;
 48     ngx_array_t             *headers;
 49 } ngx_http_headers_conf_t;
 50 
 51 
 52 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
 53     ngx_http_headers_conf_t *conf);
 54 static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
 55     ngx_http_header_val_t *hv, ngx_str_t *value);
 56 static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
 57     ngx_http_header_val_t *hv, ngx_str_t *value);
 58 static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
 59     ngx_http_header_val_t *hv, ngx_str_t *value);
 60 static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
 61     ngx_http_header_val_t *hv, ngx_str_t *value);
 62 
 63 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
 64 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
 65     void *parent, void *child);
 66 static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
 67 static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
 68     void *conf);
 69 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
 70     void *conf);
 71 
 72 
 73 static ngx_http_set_header_t  ngx_http_set_headers[] = {
 74 
 75     { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
 76 
 77     { ngx_string("Last-Modified"),
 78                  offsetof(ngx_http_headers_out_t, last_modified),
 79                  ngx_http_set_last_modified },
 80 
 81     { ngx_string("ETag"),
 82                  offsetof(ngx_http_headers_out_t, etag),
 83                  ngx_http_set_response_header },
 84 
 85     { ngx_null_string, 0, NULL }
 86 };
 87 
 88 
 89 static ngx_command_t  ngx_http_headers_filter_commands[] = {
 90 
 91     { ngx_string("expires"),
 92       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
 93                         |NGX_CONF_TAKE12,
 94       ngx_http_headers_expires,
 95       NGX_HTTP_LOC_CONF_OFFSET,
 96       0,
 97       NULL},
 98 
 99     { ngx_string("add_header"),
100       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
101                         |NGX_CONF_TAKE2,
102       ngx_http_headers_add,
103       NGX_HTTP_LOC_CONF_OFFSET,
104       0,
105       NULL},
106 
107       ngx_null_command
108 };
109 
110 
111 static ngx_http_module_t  ngx_http_headers_filter_module_ctx = {
112     NULL,                                  /* preconfiguration */
113     ngx_http_headers_filter_init,          /* postconfiguration */
114 
115     NULL,                                  /* create main configuration */
116     NULL,                                  /* init main configuration */
117 
118     NULL,                                  /* create server configuration */
119     NULL,                                  /* merge server configuration */
120 
121     ngx_http_headers_create_conf,          /* create location configuration */
122     ngx_http_headers_merge_conf            /* merge location configuration */
123 };
124 
125 
126 ngx_module_t  ngx_http_headers_filter_module = {
127     NGX_MODULE_V1,
128     &ngx_http_headers_filter_module_ctx,   /* module context */
129     ngx_http_headers_filter_commands,      /* module directives */
130     NGX_HTTP_MODULE,                       /* module type */
131     NULL,                                  /* init master */
132     NULL,                                  /* init module */
133     NULL,                                  /* init process */
134     NULL,                                  /* init thread */
135     NULL,                                  /* exit thread */
136     NULL,                                  /* exit process */
137     NULL,                                  /* exit master */
138     NGX_MODULE_V1_PADDING
139 };
140 
141 
142 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
143 
144 
145 static ngx_int_t
146 ngx_http_headers_filter(ngx_http_request_t *r)
147 {
148     ngx_str_t                 value;
149     ngx_uint_t                i;
150     ngx_http_header_val_t    *h;
151     ngx_http_headers_conf_t  *conf;
152 
153     conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
154 
155     if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
156         || r != r->main
157         || (r->headers_out.status != NGX_HTTP_OK
158             && r->headers_out.status != NGX_HTTP_CREATED
159             && r->headers_out.status != NGX_HTTP_NO_CONTENT
160             && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
161             && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
162             && r->headers_out.status != NGX_HTTP_MOVED_TEMPORARILY
163             && r->headers_out.status != NGX_HTTP_SEE_OTHER
164             && r->headers_out.status != NGX_HTTP_NOT_MODIFIED
165             && r->headers_out.status != NGX_HTTP_TEMPORARY_REDIRECT))
166     {
167         return ngx_http_next_header_filter(r);
168     }
169 
170     if (conf->expires != NGX_HTTP_EXPIRES_OFF) {
171         if (ngx_http_set_expires(r, conf) != NGX_OK) {
172             return NGX_ERROR;
173         }
174     }
175 
176     if (conf->headers) {
177         h = conf->headers->elts;
178         for (i = 0; i < conf->headers->nelts; i++) {
179 
180             if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
181                 return NGX_ERROR;
182             }
183 
184             if (h[i].handler(r, &h[i], &value) != NGX_OK) {
185                 return NGX_ERROR;
186             }
187         }
188     }
189 
190     return ngx_http_next_header_filter(r);
191 }
192 
193 
194 static ngx_int_t
195 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
196 {
197     size_t            len;
198     time_t            now, expires_time, max_age;
199     ngx_uint_t        i;
200     ngx_table_elt_t  *expires, *cc, **ccp;
201 
202     expires = r->headers_out.expires;
203 
204     if (expires == NULL) {
205 
206         expires = ngx_list_push(&r->headers_out.headers);
207         if (expires == NULL) {
208             return NGX_ERROR;
209         }
210 
211         r->headers_out.expires = expires;
212 
213         expires->hash = 1;
214         ngx_str_set(&expires->key, "Expires");
215     }
216 
217     len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
218     expires->value.len = len - 1;
219 
220     ccp = r->headers_out.cache_control.elts;
221 
222     if (ccp == NULL) {
223 
224         if (ngx_array_init(&r->headers_out.cache_control, r->pool,
225                            1, sizeof(ngx_table_elt_t *))
226             != NGX_OK)
227         {
228             return NGX_ERROR;
229         }
230 
231         ccp = ngx_array_push(&r->headers_out.cache_control);
232         if (ccp == NULL) {
233             return NGX_ERROR;
234         }
235 
236         cc = ngx_list_push(&r->headers_out.headers);
237         if (cc == NULL) {
238             return NGX_ERROR;
239         }
240 
241         cc->hash = 1;
242         ngx_str_set(&cc->key, "Cache-Control");
243         *ccp = cc;
244 
245     } else {
246         for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
247             ccp[i]->hash = 0;
248         }
249 
250         cc = ccp[0];
251     }
252 
253     if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
254         expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
255         ngx_str_set(&cc->value, "no-cache");
256         return NGX_OK;
257     }
258 
259     if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
260         expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
261         /* 10 years */
262         ngx_str_set(&cc->value, "max-age=315360000");
263         return NGX_OK;
264     }
265 
266     expires->value.data = ngx_pnalloc(r->pool, len);
267     if (expires->value.data == NULL) {
268         return NGX_ERROR;
269     }
270 
271     if (conf->expires_time == 0 && conf->expires != NGX_HTTP_EXPIRES_DAILY) {
272         ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
273                    ngx_cached_http_time.len + 1);
274         ngx_str_set(&cc->value, "max-age=0");
275         return NGX_OK;
276     }
277 
278     now = ngx_time();
279 
280     if (conf->expires == NGX_HTTP_EXPIRES_DAILY) {
281         expires_time = ngx_next_time(conf->expires_time);
282         max_age = expires_time - now;
283 
284     } else if (conf->expires == NGX_HTTP_EXPIRES_ACCESS
285                || r->headers_out.last_modified_time == -1)
286     {
287         expires_time = now + conf->expires_time;
288         max_age = conf->expires_time;
289 
290     } else {
291         expires_time = r->headers_out.last_modified_time + conf->expires_time;
292         max_age = expires_time - now;
293     }
294 
295     ngx_http_time(expires->value.data, expires_time);
296 
297     if (conf->expires_time < 0 || max_age < 0) {
298         ngx_str_set(&cc->value, "no-cache");
299         return NGX_OK;
300     }
301 
302     cc->value.data = ngx_pnalloc(r->pool,
303                                  sizeof("max-age=") + NGX_TIME_T_LEN + 1);
304     if (cc->value.data == NULL) {
305         return NGX_ERROR;
306     }
307 
308     cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
309                     - cc->value.data;
310 
311     return NGX_OK;
312 }
313 
314 
315 static ngx_int_t
316 ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
317     ngx_str_t *value)
318 {
319     ngx_table_elt_t  *h;
320 
321     if (value->len) {
322         h = ngx_list_push(&r->headers_out.headers);
323         if (h == NULL) {
324             return NGX_ERROR;
325         }
326 
327         h->hash = 1;
328         h->key = hv->key;
329         h->value = *value;
330     }
331 
332     return NGX_OK;
333 }
334 
335 
336 static ngx_int_t
337 ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
338     ngx_str_t *value)
339 {
340     ngx_table_elt_t  *cc, **ccp;
341 
342     ccp = r->headers_out.cache_control.elts;
343 
344     if (ccp == NULL) {
345 
346         if (ngx_array_init(&r->headers_out.cache_control, r->pool,
347                            1, sizeof(ngx_table_elt_t *))
348             != NGX_OK)
349         {
350             return NGX_ERROR;
351         }
352     }
353 
354     ccp = ngx_array_push(&r->headers_out.cache_control);
355     if (ccp == NULL) {
356         return NGX_ERROR;
357     }
358 
359     cc = ngx_list_push(&r->headers_out.headers);
360     if (cc == NULL) {
361         return NGX_ERROR;
362     }
363 
364     cc->hash = 1;
365     ngx_str_set(&cc->key, "Cache-Control");
366     cc->value = *value;
367 
368     *ccp = cc;
369 
370     return NGX_OK;
371 }
372 
373 
374 static ngx_int_t
375 ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
376     ngx_str_t *value)
377 {
378     if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
379         return NGX_ERROR;
380     }
381 
382     r->headers_out.last_modified_time =
383         (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;
384 
385     return NGX_OK;
386 }
387 
388 
389 static ngx_int_t
390 ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
391     ngx_str_t *value)
392 {
393     ngx_table_elt_t  *h, **old;
394 
395     old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
396 
397     if (value->len == 0) {
398         if (*old) {
399             (*old)->hash = 0;
400             *old = NULL;
401         }
402 
403         return NGX_OK;
404     }
405 
406     if (*old) {
407         h = *old;
408 
409     } else {
410         h = ngx_list_push(&r->headers_out.headers);
411         if (h == NULL) {
412             return NGX_ERROR;
413         }
414 
415         *old = h;
416     }
417 
418     h->hash = 1;
419     h->key = hv->key;
420     h->value = *value;
421 
422     return NGX_OK;
423 }
424 
425 
426 static void *
427 ngx_http_headers_create_conf(ngx_conf_t *cf)
428 {
429     ngx_http_headers_conf_t  *conf;
430 
431     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
432     if (conf == NULL) {
433         return NULL;
434     }
435 
436     /*
437      * set by ngx_pcalloc():
438      *
439      *     conf->headers = NULL;
440      *     conf->expires_time = 0;
441      */
442 
443     conf->expires = NGX_HTTP_EXPIRES_UNSET;
444 
445     return conf;
446 }
447 
448 
449 static char *
450 ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
451 {
452     ngx_http_headers_conf_t *prev = parent;
453     ngx_http_headers_conf_t *conf = child;
454 
455     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
456         conf->expires = prev->expires;
457         conf->expires_time = prev->expires_time;
458 
459         if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
460             conf->expires = NGX_HTTP_EXPIRES_OFF;
461         }
462     }
463 
464     if (conf->headers == NULL) {
465         conf->headers = prev->headers;
466     }
467 
468     return NGX_CONF_OK;
469 }
470 
471 
472 static ngx_int_t
473 ngx_http_headers_filter_init(ngx_conf_t *cf)
474 {
475     ngx_http_next_header_filter = ngx_http_top_header_filter;
476     ngx_http_top_header_filter = ngx_http_headers_filter;
477 
478     return NGX_OK;
479 }
480 
481 
482 static char *
483 ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
484 {
485     ngx_http_headers_conf_t *hcf = conf;
486 
487     ngx_uint_t   minus, n;
488     ngx_str_t   *value;
489 
490     if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
491         return "is duplicate";
492     }
493 
494     value = cf->args->elts;
495 
496     if (cf->args->nelts == 2) {
497 
498         if (ngx_strcmp(value[1].data, "epoch") == 0) {
499             hcf->expires = NGX_HTTP_EXPIRES_EPOCH;
500             return NGX_CONF_OK;
501         }
502 
503         if (ngx_strcmp(value[1].data, "max") == 0) {
504             hcf->expires = NGX_HTTP_EXPIRES_MAX;
505             return NGX_CONF_OK;
506         }
507 
508         if (ngx_strcmp(value[1].data, "off") == 0) {
509             hcf->expires = NGX_HTTP_EXPIRES_OFF;
510             return NGX_CONF_OK;
511         }
512 
513         hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
514 
515         n = 1;
516 
517     } else { /* cf->args->nelts == 3 */
518 
519         if (ngx_strcmp(value[1].data, "modified") != 0) {
520             return "invalid value";
521         }
522 
523         hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
524 
525         n = 2;
526     }
527 
528     if (value[n].data[0] == '@') {
529         value[n].data++;
530         value[n].len--;
531         minus = 0;
532 
533         if (hcf->expires == NGX_HTTP_EXPIRES_MODIFIED) {
534             return "daily time cannot be used with \"modified\" parameter";
535         }
536 
537         hcf->expires = NGX_HTTP_EXPIRES_DAILY;
538 
539     } else if (value[n].data[0] == '+') {
540         value[n].data++;
541         value[n].len--;
542         minus = 0;
543 
544     } else if (value[n].data[0] == '-') {
545         value[n].data++;
546         value[n].len--;
547         minus = 1;
548 
549     } else {
550         minus = 0;
551     }
552 
553     hcf->expires_time = ngx_parse_time(&value[n], 1);
554 
555     if (hcf->expires_time == (time_t) NGX_ERROR) {
556         return "invalid value";
557     }
558 
559     if (hcf->expires == NGX_HTTP_EXPIRES_DAILY
560         && hcf->expires_time > 24 * 60 * 60)
561     {
562         return "daily time value must be less than 24 hours";
563     }
564 
565     if (minus) {
566         hcf->expires_time = - hcf->expires_time;
567     }
568 
569     return NGX_CONF_OK;
570 }
571 
572 
573 static char *
574 ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
575 {
576     ngx_http_headers_conf_t *hcf = conf;
577 
578     ngx_str_t                         *value;
579     ngx_uint_t                         i;
580     ngx_http_header_val_t             *hv;
581     ngx_http_set_header_t             *set;
582     ngx_http_compile_complex_value_t   ccv;
583 
584     value = cf->args->elts;
585 
586     if (hcf->headers == NULL) {
587         hcf->headers = ngx_array_create(cf->pool, 1,
588                                         sizeof(ngx_http_header_val_t));
589         if (hcf->headers == NULL) {
590             return NGX_CONF_ERROR;
591         }
592     }
593 
594     hv = ngx_array_push(hcf->headers);
595     if (hv == NULL) {
596         return NGX_CONF_ERROR;
597     }
598 
599     hv->key = value[1];
600     hv->handler = ngx_http_add_header;
601     hv->offset = 0;
602 
603     set = ngx_http_set_headers;
604     for (i = 0; set[i].name.len; i++) {
605         if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
606             continue;
607         }
608 
609         hv->offset = set[i].offset;
610         hv->handler = set[i].handler;
611 
612         break;
613     }
614 
615     if (value[2].len == 0) {
616         ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
617         return NGX_CONF_OK;
618     }
619 
620     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
621 
622     ccv.cf = cf;
623     ccv.value = &value[2];
624     ccv.complex_value = &hv->value;
625 
626     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
627         return NGX_CONF_ERROR;
628     }
629 
630     return NGX_CONF_OK;
631 }
632 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.