src/http/modules/ngx_http_headers_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct ngx_http_header_val_s  ngx_http_header_val_t;

  9. typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
  10.     ngx_http_header_val_t *hv, ngx_str_t *value);


  11. typedef struct {
  12.     ngx_str_t                  name;
  13.     ngx_uint_t                 offset;
  14.     ngx_http_set_header_pt     handler;
  15. } ngx_http_set_header_t;


  16. struct ngx_http_header_val_s {
  17.     ngx_http_complex_value_t   value;
  18.     ngx_str_t                  key;
  19.     ngx_http_set_header_pt     handler;
  20.     ngx_uint_t                 offset;
  21.     ngx_uint_t                 always;  /* unsigned  always:1 */
  22. };


  23. typedef enum {
  24.     NGX_HTTP_EXPIRES_OFF,
  25.     NGX_HTTP_EXPIRES_EPOCH,
  26.     NGX_HTTP_EXPIRES_MAX,
  27.     NGX_HTTP_EXPIRES_ACCESS,
  28.     NGX_HTTP_EXPIRES_MODIFIED,
  29.     NGX_HTTP_EXPIRES_DAILY,
  30.     NGX_HTTP_EXPIRES_UNSET
  31. } ngx_http_expires_t;


  32. typedef struct {
  33.     ngx_http_expires_t         expires;
  34.     time_t                     expires_time;
  35.     ngx_http_complex_value_t  *expires_value;
  36.     ngx_array_t               *headers;
  37. } ngx_http_headers_conf_t;


  38. static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
  39.     ngx_http_headers_conf_t *conf);
  40. static ngx_int_t ngx_http_parse_expires(ngx_str_t *value,
  41.     ngx_http_expires_t *expires, time_t *expires_time, char **err);
  42. static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
  43.     ngx_http_header_val_t *hv, ngx_str_t *value);
  44. static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
  45.     ngx_http_header_val_t *hv, ngx_str_t *value);
  46. static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
  47.     ngx_http_header_val_t *hv, ngx_str_t *value);
  48. static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
  49.     ngx_http_header_val_t *hv, ngx_str_t *value);

  50. static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
  51. static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
  52.     void *parent, void *child);
  53. static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
  54. static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
  55.     void *conf);
  56. static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
  57.     void *conf);


  58. static ngx_http_set_header_t  ngx_http_set_headers[] = {

  59.     { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },

  60.     { ngx_string("Last-Modified"),
  61.                  offsetof(ngx_http_headers_out_t, last_modified),
  62.                  ngx_http_set_last_modified },

  63.     { ngx_string("ETag"),
  64.                  offsetof(ngx_http_headers_out_t, etag),
  65.                  ngx_http_set_response_header },

  66.     { ngx_null_string, 0, NULL }
  67. };


  68. static ngx_command_t  ngx_http_headers_filter_commands[] = {

  69.     { ngx_string("expires"),
  70.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
  71.                         |NGX_CONF_TAKE12,
  72.       ngx_http_headers_expires,
  73.       NGX_HTTP_LOC_CONF_OFFSET,
  74.       0,
  75.       NULL},

  76.     { ngx_string("add_header"),
  77.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
  78.                         |NGX_CONF_TAKE23,
  79.       ngx_http_headers_add,
  80.       NGX_HTTP_LOC_CONF_OFFSET,
  81.       0,
  82.       NULL},

  83.       ngx_null_command
  84. };


  85. static ngx_http_module_t  ngx_http_headers_filter_module_ctx = {
  86.     NULL,                                  /* preconfiguration */
  87.     ngx_http_headers_filter_init,          /* postconfiguration */

  88.     NULL,                                  /* create main configuration */
  89.     NULL,                                  /* init main configuration */

  90.     NULL,                                  /* create server configuration */
  91.     NULL,                                  /* merge server configuration */

  92.     ngx_http_headers_create_conf,          /* create location configuration */
  93.     ngx_http_headers_merge_conf            /* merge location configuration */
  94. };


  95. ngx_module_t  ngx_http_headers_filter_module = {
  96.     NGX_MODULE_V1,
  97.     &ngx_http_headers_filter_module_ctx,   /* module context */
  98.     ngx_http_headers_filter_commands,      /* module directives */
  99.     NGX_HTTP_MODULE,                       /* module type */
  100.     NULL,                                  /* init master */
  101.     NULL,                                  /* init module */
  102.     NULL,                                  /* init process */
  103.     NULL,                                  /* init thread */
  104.     NULL,                                  /* exit thread */
  105.     NULL,                                  /* exit process */
  106.     NULL,                                  /* exit master */
  107.     NGX_MODULE_V1_PADDING
  108. };


  109. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;


  110. static ngx_int_t
  111. ngx_http_headers_filter(ngx_http_request_t *r)
  112. {
  113.     ngx_str_t                 value;
  114.     ngx_uint_t                i, safe_status;
  115.     ngx_http_header_val_t    *h;
  116.     ngx_http_headers_conf_t  *conf;

  117.     conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);

  118.     if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
  119.         || r != r->main)
  120.     {
  121.         return ngx_http_next_header_filter(r);
  122.     }

  123.     switch (r->headers_out.status) {

  124.     case NGX_HTTP_OK:
  125.     case NGX_HTTP_CREATED:
  126.     case NGX_HTTP_NO_CONTENT:
  127.     case NGX_HTTP_PARTIAL_CONTENT:
  128.     case NGX_HTTP_MOVED_PERMANENTLY:
  129.     case NGX_HTTP_MOVED_TEMPORARILY:
  130.     case NGX_HTTP_SEE_OTHER:
  131.     case NGX_HTTP_NOT_MODIFIED:
  132.     case NGX_HTTP_TEMPORARY_REDIRECT:
  133.         safe_status = 1;
  134.         break;

  135.     default:
  136.         safe_status = 0;
  137.         break;
  138.     }

  139.     if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
  140.         if (ngx_http_set_expires(r, conf) != NGX_OK) {
  141.             return NGX_ERROR;
  142.         }
  143.     }

  144.     if (conf->headers) {
  145.         h = conf->headers->elts;
  146.         for (i = 0; i < conf->headers->nelts; i++) {

  147.             if (!safe_status && !h[i].always) {
  148.                 continue;
  149.             }

  150.             if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
  151.                 return NGX_ERROR;
  152.             }

  153.             if (h[i].handler(r, &h[i], &value) != NGX_OK) {
  154.                 return NGX_ERROR;
  155.             }
  156.         }
  157.     }

  158.     return ngx_http_next_header_filter(r);
  159. }


  160. static ngx_int_t
  161. ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
  162. {
  163.     char                *err;
  164.     size_t               len;
  165.     time_t               now, expires_time, max_age;
  166.     ngx_str_t            value;
  167.     ngx_int_t            rc;
  168.     ngx_uint_t           i;
  169.     ngx_table_elt_t     *e, *cc, **ccp;
  170.     ngx_http_expires_t   expires;

  171.     expires = conf->expires;
  172.     expires_time = conf->expires_time;

  173.     if (conf->expires_value != NULL) {

  174.         if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {
  175.             return NGX_ERROR;
  176.         }

  177.         rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);

  178.         if (rc != NGX_OK) {
  179.             return NGX_OK;
  180.         }

  181.         if (expires == NGX_HTTP_EXPIRES_OFF) {
  182.             return NGX_OK;
  183.         }
  184.     }

  185.     e = r->headers_out.expires;

  186.     if (e == NULL) {

  187.         e = ngx_list_push(&r->headers_out.headers);
  188.         if (e == NULL) {
  189.             return NGX_ERROR;
  190.         }

  191.         r->headers_out.expires = e;

  192.         e->hash = 1;
  193.         ngx_str_set(&e->key, "Expires");
  194.     }

  195.     len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
  196.     e->value.len = len - 1;

  197.     ccp = r->headers_out.cache_control.elts;

  198.     if (ccp == NULL) {

  199.         if (ngx_array_init(&r->headers_out.cache_control, r->pool,
  200.                            1, sizeof(ngx_table_elt_t *))
  201.             != NGX_OK)
  202.         {
  203.             return NGX_ERROR;
  204.         }

  205.         ccp = ngx_array_push(&r->headers_out.cache_control);
  206.         if (ccp == NULL) {
  207.             return NGX_ERROR;
  208.         }

  209.         cc = ngx_list_push(&r->headers_out.headers);
  210.         if (cc == NULL) {
  211.             return NGX_ERROR;
  212.         }

  213.         cc->hash = 1;
  214.         ngx_str_set(&cc->key, "Cache-Control");
  215.         *ccp = cc;

  216.     } else {
  217.         for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
  218.             ccp[i]->hash = 0;
  219.         }

  220.         cc = ccp[0];
  221.     }

  222.     if (expires == NGX_HTTP_EXPIRES_EPOCH) {
  223.         e->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
  224.         ngx_str_set(&cc->value, "no-cache");
  225.         return NGX_OK;
  226.     }

  227.     if (expires == NGX_HTTP_EXPIRES_MAX) {
  228.         e->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
  229.         /* 10 years */
  230.         ngx_str_set(&cc->value, "max-age=315360000");
  231.         return NGX_OK;
  232.     }

  233.     e->value.data = ngx_pnalloc(r->pool, len);
  234.     if (e->value.data == NULL) {
  235.         return NGX_ERROR;
  236.     }

  237.     if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {
  238.         ngx_memcpy(e->value.data, ngx_cached_http_time.data,
  239.                    ngx_cached_http_time.len + 1);
  240.         ngx_str_set(&cc->value, "max-age=0");
  241.         return NGX_OK;
  242.     }

  243.     now = ngx_time();

  244.     if (expires == NGX_HTTP_EXPIRES_DAILY) {
  245.         expires_time = ngx_next_time(expires_time);
  246.         max_age = expires_time - now;

  247.     } else if (expires == NGX_HTTP_EXPIRES_ACCESS
  248.                || r->headers_out.last_modified_time == -1)
  249.     {
  250.         max_age = expires_time;
  251.         expires_time += now;

  252.     } else {
  253.         expires_time += r->headers_out.last_modified_time;
  254.         max_age = expires_time - now;
  255.     }

  256.     ngx_http_time(e->value.data, expires_time);

  257.     if (conf->expires_time < 0 || max_age < 0) {
  258.         ngx_str_set(&cc->value, "no-cache");
  259.         return NGX_OK;
  260.     }

  261.     cc->value.data = ngx_pnalloc(r->pool,
  262.                                  sizeof("max-age=") + NGX_TIME_T_LEN + 1);
  263.     if (cc->value.data == NULL) {
  264.         return NGX_ERROR;
  265.     }

  266.     cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
  267.                     - cc->value.data;

  268.     return NGX_OK;
  269. }


  270. static ngx_int_t
  271. ngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,
  272.     time_t *expires_time, char **err)
  273. {
  274.     ngx_uint_t  minus;

  275.     if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {

  276.         if (value->len == 5 && ngx_strncmp(value->data, "epoch", 5) == 0) {
  277.             *expires = NGX_HTTP_EXPIRES_EPOCH;
  278.             return NGX_OK;
  279.         }

  280.         if (value->len == 3 && ngx_strncmp(value->data, "max", 3) == 0) {
  281.             *expires = NGX_HTTP_EXPIRES_MAX;
  282.             return NGX_OK;
  283.         }

  284.         if (value->len == 3 && ngx_strncmp(value->data, "off", 3) == 0) {
  285.             *expires = NGX_HTTP_EXPIRES_OFF;
  286.             return NGX_OK;
  287.         }
  288.     }

  289.     if (value->data[0] == '@') {
  290.         value->data++;
  291.         value->len--;
  292.         minus = 0;

  293.         if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {
  294.             *err = "daily time cannot be used with \"modified\" parameter";
  295.             return NGX_ERROR;
  296.         }

  297.         *expires = NGX_HTTP_EXPIRES_DAILY;

  298.     } else if (value->data[0] == '+') {
  299.         value->data++;
  300.         value->len--;
  301.         minus = 0;

  302.     } else if (value->data[0] == '-') {
  303.         value->data++;
  304.         value->len--;
  305.         minus = 1;

  306.     } else {
  307.         minus = 0;
  308.     }

  309.     *expires_time = ngx_parse_time(value, 1);

  310.     if (*expires_time == (time_t) NGX_ERROR) {
  311.         *err = "invalid value";
  312.         return NGX_ERROR;
  313.     }

  314.     if (*expires == NGX_HTTP_EXPIRES_DAILY
  315.         && *expires_time > 24 * 60 * 60)
  316.     {
  317.         *err = "daily time value must be less than 24 hours";
  318.         return NGX_ERROR;
  319.     }

  320.     if (minus) {
  321.         *expires_time = - *expires_time;
  322.     }

  323.     return NGX_OK;
  324. }


  325. static ngx_int_t
  326. ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
  327.     ngx_str_t *value)
  328. {
  329.     ngx_table_elt_t  *h;

  330.     if (value->len) {
  331.         h = ngx_list_push(&r->headers_out.headers);
  332.         if (h == NULL) {
  333.             return NGX_ERROR;
  334.         }

  335.         h->hash = 1;
  336.         h->key = hv->key;
  337.         h->value = *value;
  338.     }

  339.     return NGX_OK;
  340. }


  341. static ngx_int_t
  342. ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
  343.     ngx_str_t *value)
  344. {
  345.     ngx_table_elt_t  *cc, **ccp;

  346.     if (value->len == 0) {
  347.         return NGX_OK;
  348.     }

  349.     ccp = r->headers_out.cache_control.elts;

  350.     if (ccp == NULL) {

  351.         if (ngx_array_init(&r->headers_out.cache_control, r->pool,
  352.                            1, sizeof(ngx_table_elt_t *))
  353.             != NGX_OK)
  354.         {
  355.             return NGX_ERROR;
  356.         }
  357.     }

  358.     ccp = ngx_array_push(&r->headers_out.cache_control);
  359.     if (ccp == NULL) {
  360.         return NGX_ERROR;
  361.     }

  362.     cc = ngx_list_push(&r->headers_out.headers);
  363.     if (cc == NULL) {
  364.         return NGX_ERROR;
  365.     }

  366.     cc->hash = 1;
  367.     ngx_str_set(&cc->key, "Cache-Control");
  368.     cc->value = *value;

  369.     *ccp = cc;

  370.     return NGX_OK;
  371. }


  372. static ngx_int_t
  373. ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
  374.     ngx_str_t *value)
  375. {
  376.     if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
  377.         return NGX_ERROR;
  378.     }

  379.     r->headers_out.last_modified_time =
  380.         (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;

  381.     return NGX_OK;
  382. }


  383. static ngx_int_t
  384. ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
  385.     ngx_str_t *value)
  386. {
  387.     ngx_table_elt_t  *h, **old;

  388.     old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);

  389.     if (value->len == 0) {
  390.         if (*old) {
  391.             (*old)->hash = 0;
  392.             *old = NULL;
  393.         }

  394.         return NGX_OK;
  395.     }

  396.     if (*old) {
  397.         h = *old;

  398.     } else {
  399.         h = ngx_list_push(&r->headers_out.headers);
  400.         if (h == NULL) {
  401.             return NGX_ERROR;
  402.         }

  403.         *old = h;
  404.     }

  405.     h->hash = 1;
  406.     h->key = hv->key;
  407.     h->value = *value;

  408.     return NGX_OK;
  409. }


  410. static void *
  411. ngx_http_headers_create_conf(ngx_conf_t *cf)
  412. {
  413.     ngx_http_headers_conf_t  *conf;

  414.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
  415.     if (conf == NULL) {
  416.         return NULL;
  417.     }

  418.     /*
  419.      * set by ngx_pcalloc():
  420.      *
  421.      *     conf->headers = NULL;
  422.      *     conf->expires_time = 0;
  423.      *     conf->expires_value = NULL;
  424.      */

  425.     conf->expires = NGX_HTTP_EXPIRES_UNSET;

  426.     return conf;
  427. }


  428. static char *
  429. ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  430. {
  431.     ngx_http_headers_conf_t *prev = parent;
  432.     ngx_http_headers_conf_t *conf = child;

  433.     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
  434.         conf->expires = prev->expires;
  435.         conf->expires_time = prev->expires_time;
  436.         conf->expires_value = prev->expires_value;

  437.         if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
  438.             conf->expires = NGX_HTTP_EXPIRES_OFF;
  439.         }
  440.     }

  441.     if (conf->headers == NULL) {
  442.         conf->headers = prev->headers;
  443.     }

  444.     return NGX_CONF_OK;
  445. }


  446. static ngx_int_t
  447. ngx_http_headers_filter_init(ngx_conf_t *cf)
  448. {
  449.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  450.     ngx_http_top_header_filter = ngx_http_headers_filter;

  451.     return NGX_OK;
  452. }


  453. static char *
  454. ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  455. {
  456.     ngx_http_headers_conf_t *hcf = conf;

  457.     char                              *err;
  458.     ngx_str_t                         *value;
  459.     ngx_int_t                          rc;
  460.     ngx_uint_t                         n;
  461.     ngx_http_complex_value_t           cv;
  462.     ngx_http_compile_complex_value_t   ccv;

  463.     if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
  464.         return "is duplicate";
  465.     }

  466.     value = cf->args->elts;

  467.     if (cf->args->nelts == 2) {

  468.         hcf->expires = NGX_HTTP_EXPIRES_ACCESS;

  469.         n = 1;

  470.     } else { /* cf->args->nelts == 3 */

  471.         if (ngx_strcmp(value[1].data, "modified") != 0) {
  472.             return "invalid value";
  473.         }

  474.         hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;

  475.         n = 2;
  476.     }

  477.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  478.     ccv.cf = cf;
  479.     ccv.value = &value[n];
  480.     ccv.complex_value = &cv;

  481.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  482.         return NGX_CONF_ERROR;
  483.     }

  484.     if (cv.lengths != NULL) {

  485.         hcf->expires_value = ngx_palloc(cf->pool,
  486.                                         sizeof(ngx_http_complex_value_t));
  487.         if (hcf->expires_value == NULL) {
  488.             return NGX_CONF_ERROR;
  489.         }

  490.         *hcf->expires_value = cv;

  491.         return NGX_CONF_OK;
  492.     }

  493.     rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,
  494.                                 &err);
  495.     if (rc != NGX_OK) {
  496.         return err;
  497.     }

  498.     return NGX_CONF_OK;
  499. }


  500. static char *
  501. ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  502. {
  503.     ngx_http_headers_conf_t *hcf = conf;

  504.     ngx_str_t                         *value;
  505.     ngx_uint_t                         i;
  506.     ngx_http_header_val_t             *hv;
  507.     ngx_http_set_header_t             *set;
  508.     ngx_http_compile_complex_value_t   ccv;

  509.     value = cf->args->elts;

  510.     if (hcf->headers == NULL) {
  511.         hcf->headers = ngx_array_create(cf->pool, 1,
  512.                                         sizeof(ngx_http_header_val_t));
  513.         if (hcf->headers == NULL) {
  514.             return NGX_CONF_ERROR;
  515.         }
  516.     }

  517.     hv = ngx_array_push(hcf->headers);
  518.     if (hv == NULL) {
  519.         return NGX_CONF_ERROR;
  520.     }

  521.     hv->key = value[1];
  522.     hv->handler = ngx_http_add_header;
  523.     hv->offset = 0;
  524.     hv->always = 0;

  525.     set = ngx_http_set_headers;
  526.     for (i = 0; set[i].name.len; i++) {
  527.         if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
  528.             continue;
  529.         }

  530.         hv->offset = set[i].offset;
  531.         hv->handler = set[i].handler;

  532.         break;
  533.     }

  534.     if (value[2].len == 0) {
  535.         ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
  536.         return NGX_CONF_OK;
  537.     }

  538.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  539.     ccv.cf = cf;
  540.     ccv.value = &value[2];
  541.     ccv.complex_value = &hv->value;

  542.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  543.         return NGX_CONF_ERROR;
  544.     }

  545.     if (cf->args->nelts == 3) {
  546.         return NGX_CONF_OK;
  547.     }

  548.     if (ngx_strcmp(value[3].data, "always") != 0) {
  549.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  550.                            "invalid parameter \"%V\"", &value[3]);
  551.         return NGX_CONF_ERROR;
  552.     }

  553.     hv->always = 1;

  554.     return NGX_CONF_OK;
  555. }