Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_headers_filter_module.c
Go to the documentation of this file.
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 
14 
17 
18 
19 typedef struct {
24 
25 
31 };
32 
33 
34 typedef enum {
43 
44 
45 typedef struct {
47  time_t expires_time;
50 
51 
52 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
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"),
94  ngx_http_headers_expires,
96  0,
97  NULL},
98 
99  { ngx_string("add_header"),
102  ngx_http_headers_add,
104  0,
105  NULL},
106 
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 
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 */
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;
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
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 
232  if (ccp == NULL) {
233  return NGX_ERROR;
234  }
235 
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) {
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) {
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 
355  if (ccp == NULL) {
356  return NGX_ERROR;
357  }
358 
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 
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 {
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 {
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 
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) {
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) {
500  return NGX_CONF_OK;
501  }
502 
503  if (ngx_strcmp(value[1].data, "max") == 0) {
505  return NGX_CONF_OK;
506  }
507 
508  if (ngx_strcmp(value[1].data, "off") == 0) {
510  return NGX_CONF_OK;
511  }
512 
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 
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 
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;
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) {
617  return NGX_CONF_OK;
618  }
619 
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 }