changeset 596:6c96fdd2dfc3 NGINX_0_8_50

nginx 0.8.50 *) Feature: the "secure_link", "secure_link_md5", and "secure_link_expires" directives of the ngx_http_secure_link_module. *) Feature: the -q switch. Thanks to Gena Makhomed. *) Bugfix: worker processes may got caught in an endless loop during reconfiguration, if a caching was used; the bug had appeared in 0.8.48. *) Bugfix: in the "gzip_disable" directive. Thanks to Derrick Petzold. *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload signals to a process run in other session.
author Igor Sysoev <http://sysoev.ru>
date Thu, 02 Sep 2010 00:00:00 +0400
parents 57dcc025db4f
children 1695031dbfed
files CHANGES CHANGES.ru src/core/nginx.c src/core/nginx.h src/core/ngx_cycle.c src/core/ngx_cycle.h src/core/ngx_string.c src/core/ngx_string.h src/http/modules/ngx_http_secure_link_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_cache.h src/http/ngx_http_core_module.c src/http/ngx_http_file_cache.c src/http/ngx_http_script.c src/http/ngx_http_script.h
diffstat 15 files changed, 349 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,23 @@
 
+Changes with nginx 0.8.50                                        02 Sep 2010
+
+    *) Feature: the "secure_link", "secure_link_md5", and 
+       "secure_link_expires" directives of the ngx_http_secure_link_module.
+
+    *) Feature: the -q switch.
+       Thanks to Gena Makhomed.
+
+    *) Bugfix: worker processes may got caught in an endless loop during 
+       reconfiguration, if a caching was used; the bug had appeared in 
+       0.8.48.
+
+    *) Bugfix: in the "gzip_disable" directive.
+       Thanks to Derrick Petzold.
+
+    *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload 
+       signals to a process run in other session.
+
+
 Changes with nginx 0.8.49                                        09 Aug 2010
 
     *) Feature: the "image_filter_jpeg_quality" directive supports 
@@ -51,8 +70,8 @@ Changes with nginx 0.8.47               
 
     *) Bugfix: errors intercepted by error_page could not be cached.
 
-    *) Bugfix: a cache manager process my got caught in an endless loop, if 
-       max_size parameter was used; the bug had appeared in 0.8.46.
+    *) Bugfix: a cache manager process may got caught in an endless loop, 
+       if max_size parameter was used; the bug had appeared in 0.8.46.
 
 
 Changes with nginx 0.8.46                                        19 Jul 2010
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,22 @@
 
+Изменения в nginx 0.8.50                                          02.09.2010
+
+    *) Добавление: директивы secure_link, secure_link_md5 и 
+       secure_link_expires модуля ngx_http_secure_link_module.
+
+    *) Добавление: ключ -q.
+       Спасибо Геннадию Махомеду.
+
+    *) Исправление: при использовании кэширования рабочие процессы и могли 
+       зациклиться во время переконфигурации; ошибка появилась в 0.8.48.
+
+    *) Исправление: в директиве gzip_disable.
+       Спасибо Derrick Petzold.
+
+    *) Исправление: nginx/Windows не мог посылать сигналы stop, quit, 
+       reopen, reload процессу, запущенному в другой сессии.
+
+
 Изменения в nginx 0.8.49                                          09.08.2010
 
     *) Добавление: директива image_filter_jpeg_quality поддерживает 
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -212,7 +212,7 @@ main(int argc, char *const *argv)
 
         if (ngx_show_help) {
             ngx_log_stderr(0,
-                "Usage: nginx [-?hvVt] [-s signal] [-c filename] "
+                "Usage: nginx [-?hvVtq] [-s signal] [-c filename] "
                              "[-p prefix] [-g directives]" CRLF CRLF
                 "Options:" CRLF
                 "  -?,-h         : this help" CRLF
@@ -220,6 +220,8 @@ main(int argc, char *const *argv)
                 "  -V            : show version and configure options then exit"
                                    CRLF
                 "  -t            : test configuration and exit" CRLF
+                "  -q            : suppress non-error messages "
+                                   "during configuration testing" CRLF
                 "  -s signal     : send signal to a master process: "
                                    "stop, quit, reopen, reload" CRLF
 #ifdef NGX_PREFIX
@@ -332,8 +334,11 @@ main(int argc, char *const *argv)
     }
 
     if (ngx_test_config) {
-        ngx_log_stderr(0, "configuration file %s test is successful",
-                       cycle->conf_file.data);
+        if (!ngx_quiet_mode) {
+            ngx_log_stderr(0, "configuration file %s test is successful",
+                           cycle->conf_file.data);
+        }
+
         return 0;
     }
 
@@ -685,6 +690,10 @@ ngx_get_options(int argc, char *const *a
                 ngx_test_config = 1;
                 break;
 
+            case 'q':
+                ngx_quiet_mode = 1;
+                break;
+
             case 'p':
                 if (*p) {
                     ngx_prefix = p;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8049
-#define NGINX_VERSION      "0.8.49"
+#define nginx_version         8050
+#define NGINX_VERSION      "0.8.50"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -24,6 +24,7 @@ static ngx_pool_t     *ngx_temp_pool;
 static ngx_event_t     ngx_cleaner_event;
 
 ngx_uint_t             ngx_test_config;
+ngx_uint_t             ngx_quiet_mode;
 
 #if (NGX_THREADS)
 ngx_tls_key_t          ngx_core_tls_key;
@@ -266,7 +267,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
         return NULL;
     }
 
-    if (ngx_test_config) {
+    if (ngx_test_config && !ngx_quiet_mode) {
         ngx_log_stderr(0, "the configuration file %s syntax is ok",
                        cycle->conf_file.data);
     }
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -130,6 +130,7 @@ extern volatile ngx_cycle_t  *ngx_cycle;
 extern ngx_array_t            ngx_old_cycles;
 extern ngx_module_t           ngx_core_module;
 extern ngx_uint_t             ngx_test_config;
+extern ngx_uint_t             ngx_quiet_mode;
 #if (NGX_THREADS)
 extern ngx_tls_key_t          ngx_core_tls_key;
 #endif
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -10,6 +10,8 @@
 
 static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,
     u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
+static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src,
+    const u_char *basis);
 
 
 void
@@ -1095,8 +1097,6 @@ ngx_encode_base64(ngx_str_t *dst, ngx_st
 ngx_int_t
 ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src)
 {
-    size_t          len;
-    u_char         *d, *s;
     static u_char   basis64[] = {
         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
@@ -1117,12 +1117,49 @@ ngx_decode_base64(ngx_str_t *dst, ngx_st
         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
     };
 
+    return ngx_decode_base64_internal(dst, src, basis64);
+}
+
+
+ngx_int_t
+ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src)
+{
+    static u_char   basis64[] = {
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
+        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63,
+        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
+
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
+    };
+
+    return ngx_decode_base64_internal(dst, src, basis64);
+}
+
+
+static ngx_int_t
+ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)
+{
+    size_t          len;
+    u_char         *d, *s;
+
     for (len = 0; len < src->len; len++) {
         if (src->data[len] == '=') {
             break;
         }
 
-        if (basis64[src->data[len]] == 77) {
+        if (basis[src->data[len]] == 77) {
             return NGX_ERROR;
         }
     }
@@ -1135,20 +1172,20 @@ ngx_decode_base64(ngx_str_t *dst, ngx_st
     d = dst->data;
 
     while (len > 3) {
-        *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4);
-        *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2);
-        *d++ = (u_char) (basis64[s[2]] << 6 | basis64[s[3]]);
+        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
+        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
+        *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);
 
         s += 4;
         len -= 4;
     }
 
     if (len > 1) {
-        *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4);
+        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
     }
 
     if (len > 2) {
-        *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2);
+        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
     }
 
     dst->len = d - dst->data;
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -178,6 +178,7 @@ u_char *ngx_hex_dump(u_char *dst, u_char
 
 void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
 ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);
+ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);
 
 uint32_t ngx_utf8_decode(u_char **p, size_t n);
 size_t ngx_utf8_length(u_char *p, size_t n);
--- a/src/http/modules/ngx_http_secure_link_module.c
+++ b/src/http/modules/ngx_http_secure_link_module.c
@@ -11,10 +11,23 @@
 
 
 typedef struct {
-    ngx_str_t  secret;
+    ngx_http_complex_value_t  *variable;
+    ngx_http_complex_value_t  *md5;
+    ngx_str_t                  secret;
+    ngx_flag_t                 expires;
 } ngx_http_secure_link_conf_t;
 
 
+typedef struct {
+    ngx_str_t                  expires;
+} ngx_http_secure_link_ctx_t;
+
+
+static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,
+    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+    uintptr_t data);
+static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);
 static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,
     void *child);
@@ -23,6 +36,27 @@ static ngx_int_t ngx_http_secure_link_ad
 
 static ngx_command_t  ngx_http_secure_link_commands[] = {
 
+    { ngx_string("secure_link"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_set_comlex_value_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_secure_link_conf_t, variable),
+      NULL },
+
+    { ngx_string("secure_link_md5"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_set_comlex_value_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_secure_link_conf_t, md5),
+      NULL },
+
+    { ngx_string("secure_link_expires"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_secure_link_conf_t, expires),
+      NULL },
+
     { ngx_string("secure_link_secret"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
@@ -65,27 +99,124 @@ ngx_module_t  ngx_http_secure_link_modul
 };
 
 
-static ngx_str_t  ngx_http_secure_link = ngx_string("secure_link");
+static ngx_str_t  ngx_http_secure_link_name = ngx_string("secure_link");
+static ngx_str_t  ngx_http_secure_link_expires_name =
+    ngx_string("secure_link_expires");
 
 
 static ngx_int_t
 ngx_http_secure_link_variable(ngx_http_request_t *r,
-     ngx_http_variable_value_t *v, uintptr_t data)
+    ngx_http_variable_value_t *v, uintptr_t data)
 {
-    u_char                        *p, *start, *end, *last;
-    size_t                         len;
-    ngx_int_t                      n;
-    ngx_uint_t                     i;
-    ngx_md5_t                      md5;
+    u_char                       *p, *last;
+    ngx_str_t                     val, hash;
+    time_t                        expires;
+    ngx_md5_t                     md5;
+    ngx_http_secure_link_ctx_t   *ctx;
     ngx_http_secure_link_conf_t  *conf;
-    u_char                         hash[16];
+    u_char                        hash_buf[16], md5_buf[16];
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);
 
-    if (conf->secret.len == 0) {
+    if (conf->secret.len) {
+        return ngx_http_secure_link_old_variable(r, conf, v, data);
+    }
+
+    if (conf->variable == NULL || conf->md5 == NULL) {
+        goto not_found;
+    }
+
+    if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "secure link: \"%V\"", &val);
+
+    last = val.data + val.len;
+
+    p = ngx_strlchr(val.data, last, ',');
+    expires = 0;
+
+    if (p) {
+        val.len = p++ - val.data;
+
+        if (conf->expires) {
+            expires = ngx_atotm(p, last - p);
+            if (expires <= 0) {
+                goto not_found;
+            }
+
+            ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));
+            if (ctx == NULL) {
+                return NGX_ERROR;
+            }
+
+            ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);
+
+            ctx->expires.len = last - p;
+            ctx->expires.data = p;
+        }
+    }
+
+    if (val.len > 24) {
         goto not_found;
     }
 
+    hash.len = 16;
+    hash.data = hash_buf;
+
+    if (ngx_decode_base64url(&hash, &val) != NGX_OK) {
+        goto not_found;
+    }
+
+    if (hash.len != 16) {
+        goto not_found;
+    }
+
+    if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "secure link md5: \"%V\"", &val);
+
+    ngx_md5_init(&md5);
+    ngx_md5_update(&md5, val.data, val.len);
+    ngx_md5_final(md5_buf, &md5);
+
+    if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
+        goto not_found;
+    }
+
+    v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
+    v->len = 1;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    return NGX_OK;
+
+not_found:
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_secure_link_old_variable(ngx_http_request_t *r,
+    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    u_char      *p, *start, *end, *last;
+    size_t       len;
+    ngx_int_t    n;
+    ngx_uint_t   i;
+    ngx_md5_t    md5;
+    u_char       hash[16];
+
     p = &r->unparsed_uri.data[1];
     last = r->unparsed_uri.data + r->unparsed_uri.len;
 
@@ -145,6 +276,29 @@ not_found:
 }
 
 
+static ngx_int_t
+ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_secure_link_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);
+
+    if (ctx) {
+        v->len = ctx->expires.len;
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = ctx->expires.data;
+
+    } else {
+        v->not_found = 1;
+    }
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_http_secure_link_create_conf(ngx_conf_t *cf)
 {
@@ -158,9 +312,13 @@ ngx_http_secure_link_create_conf(ngx_con
     /*
      * set by ngx_pcalloc():
      *
-     *     conf->secret = { 0, NULL }
+     *     conf->variable = NULL;
+     *     conf->md5 = NULL;
+     *     conf->secret = { 0, NULL };
      */
 
+    conf->expires = NGX_CONF_UNSET;
+
     return conf;
 }
 
@@ -173,6 +331,16 @@ ngx_http_secure_link_merge_conf(ngx_conf
 
     ngx_conf_merge_str_value(conf->secret, prev->secret, "");
 
+    if (conf->variable == NULL) {
+        conf->variable = prev->variable;
+    }
+
+    if (conf->md5 == NULL) {
+        conf->md5 = prev->md5;
+    }
+
+    ngx_conf_merge_value(conf->expires, prev->expires, 0);
+
     return NGX_CONF_OK;
 }
 
@@ -182,12 +350,19 @@ ngx_http_secure_link_add_variables(ngx_c
 {
     ngx_http_variable_t  *var;
 
-    var = ngx_http_add_variable(cf, &ngx_http_secure_link, NGX_HTTP_VAR_NOHASH);
+    var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);
     if (var == NULL) {
         return NGX_ERROR;
     }
 
     var->get_handler = ngx_http_secure_link_variable;
 
+    var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);
+    if (var == NULL) {
+        return NGX_ERROR;
+    }
+
+    var->get_handler = ngx_http_secure_link_expires_variable;
+
     return NGX_OK;
 }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.8.49';
+our $VERSION = '0.8.50';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -43,7 +43,8 @@ typedef struct {
     unsigned                         error:10;
     unsigned                         exists:1;
     unsigned                         updating:1;
-                                     /* 12 unused bits */
+    unsigned                         deleting:1;
+                                     /* 11 unused bits */
 
     ngx_file_uniq_t                  uniq;
     time_t                           expire;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -3111,9 +3111,9 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     clcf->gzip_http_version = NGX_CONF_UNSET_UINT;
 #if (NGX_PCRE)
     clcf->gzip_disable = NGX_CONF_UNSET_PTR;
+#endif
     clcf->gzip_disable_msie6 = 3;
 #endif
-#endif
 
     return clcf;
 }
@@ -3788,13 +3788,19 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
     n = ngx_http_script_variables_count(&clcf->root);
 
     ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+    sc.variables = n;
+
+#if (NGX_PCRE)
+    if (alias && clcf->regex) {
+        n = 1;
+    }
+#endif
 
     if (n) {
         sc.cf = cf;
         sc.source = &clcf->root;
         sc.lengths = &clcf->root_lengths;
         sc.values = &clcf->root_values;
-        sc.variables = n;
         sc.complete_lengths = 1;
         sc.complete_values = 1;
 
@@ -4384,7 +4390,7 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
 
     for (i = 1; i < cf->args->nelts; i++) {
 
-        if (ngx_strcmp(value[1].data, "msie6") == 0) {
+        if (ngx_strcmp(value[i].data, "msie6") == 0) {
             clcf->gzip_disable_msie6 = 1;
             continue;
         }
@@ -4394,7 +4400,7 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
             return NGX_CONF_ERROR;
         }
 
-        rc.pattern = value[1];
+        rc.pattern = value[i];
         rc.options = NGX_REGEX_CASELESS;
 
         if (ngx_regex_compile(&rc) != NGX_OK) {
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -582,6 +582,7 @@ ngx_http_file_cache_exists(ngx_http_file
     fcn->uses = 1;
     fcn->count = 1;
     fcn->updating = 0;
+    fcn->deleting = 0;
 
 renew:
 
@@ -1102,6 +1103,10 @@ ngx_http_file_cache_expire(ngx_http_file
             continue;
         }
 
+        if (fcn->deleting) {
+            continue;
+        }
+
         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
                          sizeof(ngx_rbtree_key_t));
         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
@@ -1153,6 +1158,7 @@ ngx_http_file_cache_delete(ngx_http_file
         *p = '\0';
 
         fcn->count++;
+        fcn->deleting = 1;
         ngx_shmtx_unlock(&cache->shpool->mutex);
 
         len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
@@ -1168,6 +1174,7 @@ ngx_http_file_cache_delete(ngx_http_file
 
         ngx_shmtx_lock(&cache->shpool->mutex);
         fcn->count--;
+        fcn->deleting = 0;
     }
 
     if (fcn->count == 0) {
@@ -1431,6 +1438,7 @@ ngx_http_file_cache_add(ngx_http_file_ca
         fcn->error = 0;
         fcn->exists = 1;
         fcn->updating = 0;
+        fcn->deleting = 0;
         fcn->uniq = c->uniq;
         fcn->valid_sec = c->valid_sec;
         fcn->body_start = c->body_start;
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -211,6 +211,42 @@ ngx_http_compile_complex_value(ngx_http_
 }
 
 
+char *
+ngx_http_set_comlex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char  *p = conf;
+
+    ngx_str_t                          *value;
+    ngx_http_complex_value_t          **cv;
+    ngx_http_compile_complex_value_t    ccv;
+
+    cv = (ngx_http_complex_value_t **) (p + cmd->offset);
+
+    if (*cv != NULL) {
+        return "duplicate";
+    }
+
+    *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+    if (*cv == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = *cv;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 ngx_int_t
 ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)
 {
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -207,6 +207,9 @@ void ngx_http_script_flush_complex_value
 ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
     ngx_http_complex_value_t *val, ngx_str_t *value);
 ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);
+char * ngx_http_set_comlex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 
 ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,
     ngx_array_t *predicates);