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

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

Version: ~ [ nginx-0.8.20 ] ~ [ nginx-0.7.62 ] ~ [ nginx-0.6.39 ] ~

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

~ [ 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.