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