Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
0014 
0015 typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
0016     ngx_http_header_val_t *hv, ngx_str_t *value);
0017 
0018 
0019 typedef struct {
0020     ngx_str_t                  name;
0021     ngx_uint_t                 offset;
0022     ngx_http_set_header_pt     handler;
0023 } ngx_http_set_header_t;
0024 
0025 
0026 struct ngx_http_header_val_s {
0027     ngx_http_complex_value_t   value;
0028     ngx_str_t                  key;
0029     ngx_http_set_header_pt     handler;
0030     ngx_uint_t                 offset;
0031     ngx_uint_t                 always;  /* unsigned  always:1 */
0032 };
0033 
0034 
0035 typedef enum {
0036     NGX_HTTP_EXPIRES_OFF,
0037     NGX_HTTP_EXPIRES_EPOCH,
0038     NGX_HTTP_EXPIRES_MAX,
0039     NGX_HTTP_EXPIRES_ACCESS,
0040     NGX_HTTP_EXPIRES_MODIFIED,
0041     NGX_HTTP_EXPIRES_DAILY,
0042     NGX_HTTP_EXPIRES_UNSET
0043 } ngx_http_expires_t;
0044 
0045 
0046 typedef struct {
0047     ngx_http_expires_t         expires;
0048     time_t                     expires_time;
0049     ngx_http_complex_value_t  *expires_value;
0050     ngx_array_t               *headers;
0051     ngx_array_t               *trailers;
0052 } ngx_http_headers_conf_t;
0053 
0054 
0055 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
0056     ngx_http_headers_conf_t *conf);
0057 static ngx_int_t ngx_http_parse_expires(ngx_str_t *value,
0058     ngx_http_expires_t *expires, time_t *expires_time, char **err);
0059 static ngx_int_t ngx_http_add_multi_header_lines(ngx_http_request_t *r,
0060     ngx_http_header_val_t *hv, ngx_str_t *value);
0061 static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
0062     ngx_http_header_val_t *hv, ngx_str_t *value);
0063 static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
0064     ngx_http_header_val_t *hv, ngx_str_t *value);
0065 static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
0066     ngx_http_header_val_t *hv, ngx_str_t *value);
0067 
0068 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
0069 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
0070     void *parent, void *child);
0071 static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
0072 static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
0073     void *conf);
0074 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
0075     void *conf);
0076 
0077 
0078 static ngx_http_set_header_t  ngx_http_set_headers[] = {
0079 
0080     { ngx_string("Cache-Control"),
0081                  offsetof(ngx_http_headers_out_t, cache_control),
0082                  ngx_http_add_multi_header_lines },
0083 
0084     { ngx_string("Link"),
0085                  offsetof(ngx_http_headers_out_t, link),
0086                  ngx_http_add_multi_header_lines },
0087 
0088     { ngx_string("Last-Modified"),
0089                  offsetof(ngx_http_headers_out_t, last_modified),
0090                  ngx_http_set_last_modified },
0091 
0092     { ngx_string("ETag"),
0093                  offsetof(ngx_http_headers_out_t, etag),
0094                  ngx_http_set_response_header },
0095 
0096     { ngx_null_string, 0, NULL }
0097 };
0098 
0099 
0100 static ngx_command_t  ngx_http_headers_filter_commands[] = {
0101 
0102     { ngx_string("expires"),
0103       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
0104                         |NGX_CONF_TAKE12,
0105       ngx_http_headers_expires,
0106       NGX_HTTP_LOC_CONF_OFFSET,
0107       0,
0108       NULL },
0109 
0110     { ngx_string("add_header"),
0111       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
0112                         |NGX_CONF_TAKE23,
0113       ngx_http_headers_add,
0114       NGX_HTTP_LOC_CONF_OFFSET,
0115       offsetof(ngx_http_headers_conf_t, headers),
0116       NULL },
0117 
0118     { ngx_string("add_trailer"),
0119       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
0120                         |NGX_CONF_TAKE23,
0121       ngx_http_headers_add,
0122       NGX_HTTP_LOC_CONF_OFFSET,
0123       offsetof(ngx_http_headers_conf_t, trailers),
0124       NULL },
0125 
0126       ngx_null_command
0127 };
0128 
0129 
0130 static ngx_http_module_t  ngx_http_headers_filter_module_ctx = {
0131     NULL,                                  /* preconfiguration */
0132     ngx_http_headers_filter_init,          /* postconfiguration */
0133 
0134     NULL,                                  /* create main configuration */
0135     NULL,                                  /* init main configuration */
0136 
0137     NULL,                                  /* create server configuration */
0138     NULL,                                  /* merge server configuration */
0139 
0140     ngx_http_headers_create_conf,          /* create location configuration */
0141     ngx_http_headers_merge_conf            /* merge location configuration */
0142 };
0143 
0144 
0145 ngx_module_t  ngx_http_headers_filter_module = {
0146     NGX_MODULE_V1,
0147     &ngx_http_headers_filter_module_ctx,   /* module context */
0148     ngx_http_headers_filter_commands,      /* module directives */
0149     NGX_HTTP_MODULE,                       /* module type */
0150     NULL,                                  /* init master */
0151     NULL,                                  /* init module */
0152     NULL,                                  /* init process */
0153     NULL,                                  /* init thread */
0154     NULL,                                  /* exit thread */
0155     NULL,                                  /* exit process */
0156     NULL,                                  /* exit master */
0157     NGX_MODULE_V1_PADDING
0158 };
0159 
0160 
0161 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0162 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0163 
0164 
0165 static ngx_int_t
0166 ngx_http_headers_filter(ngx_http_request_t *r)
0167 {
0168     ngx_str_t                 value;
0169     ngx_uint_t                i, safe_status;
0170     ngx_http_header_val_t    *h;
0171     ngx_http_headers_conf_t  *conf;
0172 
0173     if (r != r->main) {
0174         return ngx_http_next_header_filter(r);
0175     }
0176 
0177     conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
0178 
0179     if (conf->expires == NGX_HTTP_EXPIRES_OFF
0180         && conf->headers == NULL
0181         && conf->trailers == NULL)
0182     {
0183         return ngx_http_next_header_filter(r);
0184     }
0185 
0186     switch (r->headers_out.status) {
0187 
0188     case NGX_HTTP_OK:
0189     case NGX_HTTP_CREATED:
0190     case NGX_HTTP_NO_CONTENT:
0191     case NGX_HTTP_PARTIAL_CONTENT:
0192     case NGX_HTTP_MOVED_PERMANENTLY:
0193     case NGX_HTTP_MOVED_TEMPORARILY:
0194     case NGX_HTTP_SEE_OTHER:
0195     case NGX_HTTP_NOT_MODIFIED:
0196     case NGX_HTTP_TEMPORARY_REDIRECT:
0197     case NGX_HTTP_PERMANENT_REDIRECT:
0198         safe_status = 1;
0199         break;
0200 
0201     default:
0202         safe_status = 0;
0203         break;
0204     }
0205 
0206     if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
0207         if (ngx_http_set_expires(r, conf) != NGX_OK) {
0208             return NGX_ERROR;
0209         }
0210     }
0211 
0212     if (conf->headers) {
0213         h = conf->headers->elts;
0214         for (i = 0; i < conf->headers->nelts; i++) {
0215 
0216             if (!safe_status && !h[i].always) {
0217                 continue;
0218             }
0219 
0220             if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
0221                 return NGX_ERROR;
0222             }
0223 
0224             if (h[i].handler(r, &h[i], &value) != NGX_OK) {
0225                 return NGX_ERROR;
0226             }
0227         }
0228     }
0229 
0230     if (conf->trailers) {
0231         h = conf->trailers->elts;
0232         for (i = 0; i < conf->trailers->nelts; i++) {
0233 
0234             if (!safe_status && !h[i].always) {
0235                 continue;
0236             }
0237 
0238             r->expect_trailers = 1;
0239             break;
0240         }
0241     }
0242 
0243     return ngx_http_next_header_filter(r);
0244 }
0245 
0246 
0247 static ngx_int_t
0248 ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
0249 {
0250     ngx_str_t                 value;
0251     ngx_uint_t                i, safe_status;
0252     ngx_chain_t              *cl;
0253     ngx_table_elt_t          *t;
0254     ngx_http_header_val_t    *h;
0255     ngx_http_headers_conf_t  *conf;
0256 
0257     conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
0258 
0259     if (in == NULL
0260         || conf->trailers == NULL
0261         || !r->expect_trailers
0262         || r->header_only)
0263     {
0264         return ngx_http_next_body_filter(r, in);
0265     }
0266 
0267     for (cl = in; cl; cl = cl->next) {
0268         if (cl->buf->last_buf) {
0269             break;
0270         }
0271     }
0272 
0273     if (cl == NULL) {
0274         return ngx_http_next_body_filter(r, in);
0275     }
0276 
0277     switch (r->headers_out.status) {
0278 
0279     case NGX_HTTP_OK:
0280     case NGX_HTTP_CREATED:
0281     case NGX_HTTP_NO_CONTENT:
0282     case NGX_HTTP_PARTIAL_CONTENT:
0283     case NGX_HTTP_MOVED_PERMANENTLY:
0284     case NGX_HTTP_MOVED_TEMPORARILY:
0285     case NGX_HTTP_SEE_OTHER:
0286     case NGX_HTTP_NOT_MODIFIED:
0287     case NGX_HTTP_TEMPORARY_REDIRECT:
0288     case NGX_HTTP_PERMANENT_REDIRECT:
0289         safe_status = 1;
0290         break;
0291 
0292     default:
0293         safe_status = 0;
0294         break;
0295     }
0296 
0297     h = conf->trailers->elts;
0298     for (i = 0; i < conf->trailers->nelts; i++) {
0299 
0300         if (!safe_status && !h[i].always) {
0301             continue;
0302         }
0303 
0304         if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
0305             return NGX_ERROR;
0306         }
0307 
0308         if (value.len) {
0309             t = ngx_list_push(&r->headers_out.trailers);
0310             if (t == NULL) {
0311                 return NGX_ERROR;
0312             }
0313 
0314             t->key = h[i].key;
0315             t->value = value;
0316             t->hash = 1;
0317         }
0318     }
0319 
0320     return ngx_http_next_body_filter(r, in);
0321 }
0322 
0323 
0324 static ngx_int_t
0325 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
0326 {
0327     char                *err;
0328     size_t               len;
0329     time_t               now, expires_time, max_age;
0330     ngx_str_t            value;
0331     ngx_int_t            rc;
0332     ngx_uint_t           i;
0333     ngx_table_elt_t     *e, *cc, **ccp;
0334     ngx_http_expires_t   expires;
0335 
0336     expires = conf->expires;
0337     expires_time = conf->expires_time;
0338 
0339     if (conf->expires_value != NULL) {
0340 
0341         if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {
0342             return NGX_ERROR;
0343         }
0344 
0345         rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);
0346 
0347         if (rc != NGX_OK) {
0348             return NGX_OK;
0349         }
0350 
0351         if (expires == NGX_HTTP_EXPIRES_OFF) {
0352             return NGX_OK;
0353         }
0354     }
0355 
0356     e = r->headers_out.expires;
0357 
0358     if (e == NULL) {
0359 
0360         e = ngx_list_push(&r->headers_out.headers);
0361         if (e == NULL) {
0362             return NGX_ERROR;
0363         }
0364 
0365         r->headers_out.expires = e;
0366 
0367         e->hash = 1;
0368         ngx_str_set(&e->key, "Expires");
0369     }
0370 
0371     len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
0372     e->value.len = len - 1;
0373 
0374     ccp = r->headers_out.cache_control.elts;
0375 
0376     if (ccp == NULL) {
0377 
0378         if (ngx_array_init(&r->headers_out.cache_control, r->pool,
0379                            1, sizeof(ngx_table_elt_t *))
0380             != NGX_OK)
0381         {
0382             return NGX_ERROR;
0383         }
0384 
0385         cc = ngx_list_push(&r->headers_out.headers);
0386         if (cc == NULL) {
0387             return NGX_ERROR;
0388         }
0389 
0390         cc->hash = 1;
0391         ngx_str_set(&cc->key, "Cache-Control");
0392 
0393         ccp = ngx_array_push(&r->headers_out.cache_control);
0394         if (ccp == NULL) {
0395             return NGX_ERROR;
0396         }
0397 
0398         *ccp = cc;
0399 
0400     } else {
0401         for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
0402             ccp[i]->hash = 0;
0403         }
0404 
0405         cc = ccp[0];
0406     }
0407 
0408     if (expires == NGX_HTTP_EXPIRES_EPOCH) {
0409         e->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
0410         ngx_str_set(&cc->value, "no-cache");
0411         return NGX_OK;
0412     }
0413 
0414     if (expires == NGX_HTTP_EXPIRES_MAX) {
0415         e->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
0416         /* 10 years */
0417         ngx_str_set(&cc->value, "max-age=315360000");
0418         return NGX_OK;
0419     }
0420 
0421     e->value.data = ngx_pnalloc(r->pool, len);
0422     if (e->value.data == NULL) {
0423         return NGX_ERROR;
0424     }
0425 
0426     if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {
0427         ngx_memcpy(e->value.data, ngx_cached_http_time.data,
0428                    ngx_cached_http_time.len + 1);
0429         ngx_str_set(&cc->value, "max-age=0");
0430         return NGX_OK;
0431     }
0432 
0433     now = ngx_time();
0434 
0435     if (expires == NGX_HTTP_EXPIRES_DAILY) {
0436         expires_time = ngx_next_time(expires_time);
0437         max_age = expires_time - now;
0438 
0439     } else if (expires == NGX_HTTP_EXPIRES_ACCESS
0440                || r->headers_out.last_modified_time == -1)
0441     {
0442         max_age = expires_time;
0443         expires_time += now;
0444 
0445     } else {
0446         expires_time += r->headers_out.last_modified_time;
0447         max_age = expires_time - now;
0448     }
0449 
0450     ngx_http_time(e->value.data, expires_time);
0451 
0452     if (conf->expires_time < 0 || max_age < 0) {
0453         ngx_str_set(&cc->value, "no-cache");
0454         return NGX_OK;
0455     }
0456 
0457     cc->value.data = ngx_pnalloc(r->pool,
0458                                  sizeof("max-age=") + NGX_TIME_T_LEN + 1);
0459     if (cc->value.data == NULL) {
0460         return NGX_ERROR;
0461     }
0462 
0463     cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
0464                     - cc->value.data;
0465 
0466     return NGX_OK;
0467 }
0468 
0469 
0470 static ngx_int_t
0471 ngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,
0472     time_t *expires_time, char **err)
0473 {
0474     ngx_uint_t  minus;
0475 
0476     if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {
0477 
0478         if (value->len == 5 && ngx_strncmp(value->data, "epoch", 5) == 0) {
0479             *expires = NGX_HTTP_EXPIRES_EPOCH;
0480             return NGX_OK;
0481         }
0482 
0483         if (value->len == 3 && ngx_strncmp(value->data, "max", 3) == 0) {
0484             *expires = NGX_HTTP_EXPIRES_MAX;
0485             return NGX_OK;
0486         }
0487 
0488         if (value->len == 3 && ngx_strncmp(value->data, "off", 3) == 0) {
0489             *expires = NGX_HTTP_EXPIRES_OFF;
0490             return NGX_OK;
0491         }
0492     }
0493 
0494     if (value->len && value->data[0] == '@') {
0495         value->data++;
0496         value->len--;
0497         minus = 0;
0498 
0499         if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {
0500             *err = "daily time cannot be used with \"modified\" parameter";
0501             return NGX_ERROR;
0502         }
0503 
0504         *expires = NGX_HTTP_EXPIRES_DAILY;
0505 
0506     } else if (value->len && value->data[0] == '+') {
0507         value->data++;
0508         value->len--;
0509         minus = 0;
0510 
0511     } else if (value->len && value->data[0] == '-') {
0512         value->data++;
0513         value->len--;
0514         minus = 1;
0515 
0516     } else {
0517         minus = 0;
0518     }
0519 
0520     *expires_time = ngx_parse_time(value, 1);
0521 
0522     if (*expires_time == (time_t) NGX_ERROR) {
0523         *err = "invalid value";
0524         return NGX_ERROR;
0525     }
0526 
0527     if (*expires == NGX_HTTP_EXPIRES_DAILY
0528         && *expires_time > 24 * 60 * 60)
0529     {
0530         *err = "daily time value must be less than 24 hours";
0531         return NGX_ERROR;
0532     }
0533 
0534     if (minus) {
0535         *expires_time = - *expires_time;
0536     }
0537 
0538     return NGX_OK;
0539 }
0540 
0541 
0542 static ngx_int_t
0543 ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
0544     ngx_str_t *value)
0545 {
0546     ngx_table_elt_t  *h;
0547 
0548     if (value->len) {
0549         h = ngx_list_push(&r->headers_out.headers);
0550         if (h == NULL) {
0551             return NGX_ERROR;
0552         }
0553 
0554         h->hash = 1;
0555         h->key = hv->key;
0556         h->value = *value;
0557     }
0558 
0559     return NGX_OK;
0560 }
0561 
0562 
0563 static ngx_int_t
0564 ngx_http_add_multi_header_lines(ngx_http_request_t *r,
0565     ngx_http_header_val_t *hv, ngx_str_t *value)
0566 {
0567     ngx_array_t      *pa;
0568     ngx_table_elt_t  *h, **ph;
0569 
0570     if (value->len == 0) {
0571         return NGX_OK;
0572     }
0573 
0574     pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);
0575 
0576     if (pa->elts == NULL) {
0577         if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)
0578         {
0579             return NGX_ERROR;
0580         }
0581     }
0582 
0583     h = ngx_list_push(&r->headers_out.headers);
0584     if (h == NULL) {
0585         return NGX_ERROR;
0586     }
0587 
0588     h->hash = 1;
0589     h->key = hv->key;
0590     h->value = *value;
0591 
0592     ph = ngx_array_push(pa);
0593     if (ph == NULL) {
0594         return NGX_ERROR;
0595     }
0596 
0597     *ph = h;
0598 
0599     return NGX_OK;
0600 }
0601 
0602 
0603 static ngx_int_t
0604 ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
0605     ngx_str_t *value)
0606 {
0607     if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
0608         return NGX_ERROR;
0609     }
0610 
0611     r->headers_out.last_modified_time =
0612         (value->len) ? ngx_parse_http_time(value->data, value->len) : -1;
0613 
0614     return NGX_OK;
0615 }
0616 
0617 
0618 static ngx_int_t
0619 ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
0620     ngx_str_t *value)
0621 {
0622     ngx_table_elt_t  *h, **old;
0623 
0624     old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
0625 
0626     if (value->len == 0) {
0627         if (*old) {
0628             (*old)->hash = 0;
0629             *old = NULL;
0630         }
0631 
0632         return NGX_OK;
0633     }
0634 
0635     if (*old) {
0636         h = *old;
0637 
0638     } else {
0639         h = ngx_list_push(&r->headers_out.headers);
0640         if (h == NULL) {
0641             return NGX_ERROR;
0642         }
0643 
0644         *old = h;
0645     }
0646 
0647     h->hash = 1;
0648     h->key = hv->key;
0649     h->value = *value;
0650 
0651     return NGX_OK;
0652 }
0653 
0654 
0655 static void *
0656 ngx_http_headers_create_conf(ngx_conf_t *cf)
0657 {
0658     ngx_http_headers_conf_t  *conf;
0659 
0660     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
0661     if (conf == NULL) {
0662         return NULL;
0663     }
0664 
0665     /*
0666      * set by ngx_pcalloc():
0667      *
0668      *     conf->headers = NULL;
0669      *     conf->trailers = NULL;
0670      *     conf->expires_time = 0;
0671      *     conf->expires_value = NULL;
0672      */
0673 
0674     conf->expires = NGX_HTTP_EXPIRES_UNSET;
0675 
0676     return conf;
0677 }
0678 
0679 
0680 static char *
0681 ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0682 {
0683     ngx_http_headers_conf_t *prev = parent;
0684     ngx_http_headers_conf_t *conf = child;
0685 
0686     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
0687         conf->expires = prev->expires;
0688         conf->expires_time = prev->expires_time;
0689         conf->expires_value = prev->expires_value;
0690 
0691         if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
0692             conf->expires = NGX_HTTP_EXPIRES_OFF;
0693         }
0694     }
0695 
0696     if (conf->headers == NULL) {
0697         conf->headers = prev->headers;
0698     }
0699 
0700     if (conf->trailers == NULL) {
0701         conf->trailers = prev->trailers;
0702     }
0703 
0704     return NGX_CONF_OK;
0705 }
0706 
0707 
0708 static ngx_int_t
0709 ngx_http_headers_filter_init(ngx_conf_t *cf)
0710 {
0711     ngx_http_next_header_filter = ngx_http_top_header_filter;
0712     ngx_http_top_header_filter = ngx_http_headers_filter;
0713 
0714     ngx_http_next_body_filter = ngx_http_top_body_filter;
0715     ngx_http_top_body_filter = ngx_http_trailers_filter;
0716 
0717     return NGX_OK;
0718 }
0719 
0720 
0721 static char *
0722 ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0723 {
0724     ngx_http_headers_conf_t *hcf = conf;
0725 
0726     char                              *err;
0727     ngx_str_t                         *value;
0728     ngx_int_t                          rc;
0729     ngx_uint_t                         n;
0730     ngx_http_complex_value_t           cv;
0731     ngx_http_compile_complex_value_t   ccv;
0732 
0733     if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
0734         return "is duplicate";
0735     }
0736 
0737     value = cf->args->elts;
0738 
0739     if (cf->args->nelts == 2) {
0740 
0741         hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
0742 
0743         n = 1;
0744 
0745     } else { /* cf->args->nelts == 3 */
0746 
0747         if (ngx_strcmp(value[1].data, "modified") != 0) {
0748             return "invalid value";
0749         }
0750 
0751         hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
0752 
0753         n = 2;
0754     }
0755 
0756     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0757 
0758     ccv.cf = cf;
0759     ccv.value = &value[n];
0760     ccv.complex_value = &cv;
0761 
0762     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0763         return NGX_CONF_ERROR;
0764     }
0765 
0766     if (cv.lengths != NULL) {
0767 
0768         hcf->expires_value = ngx_palloc(cf->pool,
0769                                         sizeof(ngx_http_complex_value_t));
0770         if (hcf->expires_value == NULL) {
0771             return NGX_CONF_ERROR;
0772         }
0773 
0774         *hcf->expires_value = cv;
0775 
0776         return NGX_CONF_OK;
0777     }
0778 
0779     rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,
0780                                 &err);
0781     if (rc != NGX_OK) {
0782         return err;
0783     }
0784 
0785     return NGX_CONF_OK;
0786 }
0787 
0788 
0789 static char *
0790 ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0791 {
0792     ngx_http_headers_conf_t *hcf = conf;
0793 
0794     ngx_str_t                          *value;
0795     ngx_uint_t                          i;
0796     ngx_array_t                       **headers;
0797     ngx_http_header_val_t              *hv;
0798     ngx_http_set_header_t              *set;
0799     ngx_http_compile_complex_value_t    ccv;
0800 
0801     value = cf->args->elts;
0802 
0803     headers = (ngx_array_t **) ((char *) hcf + cmd->offset);
0804 
0805     if (*headers == NULL) {
0806         *headers = ngx_array_create(cf->pool, 1,
0807                                     sizeof(ngx_http_header_val_t));
0808         if (*headers == NULL) {
0809             return NGX_CONF_ERROR;
0810         }
0811     }
0812 
0813     hv = ngx_array_push(*headers);
0814     if (hv == NULL) {
0815         return NGX_CONF_ERROR;
0816     }
0817 
0818     hv->key = value[1];
0819     hv->handler = NULL;
0820     hv->offset = 0;
0821     hv->always = 0;
0822 
0823     if (headers == &hcf->headers) {
0824         hv->handler = ngx_http_add_header;
0825 
0826         set = ngx_http_set_headers;
0827         for (i = 0; set[i].name.len; i++) {
0828             if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
0829                 continue;
0830             }
0831 
0832             hv->offset = set[i].offset;
0833             hv->handler = set[i].handler;
0834 
0835             break;
0836         }
0837     }
0838 
0839     if (value[2].len == 0) {
0840         ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
0841 
0842     } else {
0843         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0844 
0845         ccv.cf = cf;
0846         ccv.value = &value[2];
0847         ccv.complex_value = &hv->value;
0848 
0849         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0850             return NGX_CONF_ERROR;
0851         }
0852     }
0853 
0854     if (cf->args->nelts == 3) {
0855         return NGX_CONF_OK;
0856     }
0857 
0858     if (ngx_strcmp(value[3].data, "always") != 0) {
0859         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0860                            "invalid parameter \"%V\"", &value[3]);
0861         return NGX_CONF_ERROR;
0862     }
0863 
0864     hv->always = 1;
0865 
0866     return NGX_CONF_OK;
0867 }