changeset 3325:42c16d8bddbe

regex named captures
author Igor Sysoev <igor@sysoev.ru>
date Mon, 16 Nov 2009 12:19:02 +0000
parents 616da2ea901f
children e6967a1dc8e9
files src/core/ngx_core.h src/core/ngx_regex.c src/core/ngx_regex.h src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_referer_module.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_script.c src/http/ngx_http_script.h src/http/ngx_http_variables.c src/http/ngx_http_variables.h
diffstat 15 files changed, 424 insertions(+), 288 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -83,7 +83,8 @@ typedef void (*ngx_connection_handler_pt
 #define CRLF   "\x0d\x0a"
 
 
-#define ngx_abs(value)   (((value) >= 0) ? (value) : - (value))
+#define ngx_abs(value)       (((value) >= 0) ? (value) : - (value))
+#define ngx_max(val1, val2)  ((val1 < val2) ? (val2) : (val1))
 
 void ngx_cpuinfo(void);
 
--- a/src/core/ngx_regex.c
+++ b/src/core/ngx_regex.c
@@ -23,25 +23,16 @@ ngx_regex_init(void)
 }
 
 
-ngx_regex_t *
-ngx_regex_compile(ngx_str_t *pattern, ngx_int_t options, ngx_pool_t *pool,
-    ngx_str_t *err)
+static ngx_inline void
+ngx_regex_malloc_init(ngx_pool_t *pool)
 {
-    int              erroff;
-    const char      *errstr;
-    ngx_regex_t     *re;
 #if (NGX_THREADS)
     ngx_core_tls_t  *tls;
 
-#if (NGX_SUPPRESS_WARN)
-    tls = NULL;
-#endif
-
     if (ngx_threaded) {
         tls = ngx_thread_get_tls(ngx_core_tls_key);
         tls->pool = pool;
-    } else {
-        ngx_pcre_pool = pool;
+        return;
     }
 
 #else
@@ -49,35 +40,103 @@ ngx_regex_compile(ngx_str_t *pattern, ng
     ngx_pcre_pool = pool;
 
 #endif
+}
 
-    re = pcre_compile((const char *) pattern->data, (int) options,
+
+static ngx_inline void
+ngx_regex_malloc_done(void)
+{
+#if (NGX_THREADS)
+    ngx_core_tls_t  *tls;
+
+    if (ngx_threaded) {
+        tls = ngx_thread_get_tls(ngx_core_tls_key);
+        tls->pool = NULL;
+        return;
+    }
+
+#else
+
+    ngx_pcre_pool = NULL;
+
+#endif
+}
+
+
+ngx_int_t
+ngx_regex_compile(ngx_regex_compile_t *rc)
+{
+    int           n, erroff;
+    char         *p;
+    const char   *errstr;
+    ngx_regex_t  *re;
+
+    ngx_regex_malloc_init(rc->pool);
+
+    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
                       &errstr, &erroff, NULL);
 
+    /* ensure that there is no current pool */
+    ngx_regex_malloc_done();
+
     if (re == NULL) {
-       if ((size_t) erroff == pattern->len) {
-           ngx_snprintf(err->data, err->len - 1,
-                        "pcre_compile() failed: %s in \"%s\"%Z",
-                        errstr, pattern->data);
+        if ((size_t) erroff == rc->pattern.len) {
+           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                              "pcre_compile() failed: %s in \"%V\"",
+                               errstr, &rc->pattern)
+                      - rc->err.data;
+
         } else {
-           ngx_snprintf(err->data, err->len - 1,
-                        "pcre_compile() failed: %s in \"%s\" at \"%s\"%Z",
-                        errstr, pattern->data, pattern->data + erroff);
+           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",
+                               errstr, &rc->pattern, rc->pattern.data + erroff)
+                      - rc->err.data;
         }
+
+        return NGX_ERROR;
+    }
+
+    rc->regex = re;
+
+    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
+    if (n < 0) {
+        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
+        goto failed;
+    }
+
+    if (rc->captures == 0) {
+        return NGX_OK;
     }
 
-    /* ensure that there is no current pool */
+    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);
+    if (n < 0) {
+        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";
+        goto failed;
+    }
+
+    if (rc->named_captures == 0) {
+        return NGX_OK;
+    }
 
-#if (NGX_THREADS)
-    if (ngx_threaded) {
-        tls->pool = NULL;
-    } else {
-        ngx_pcre_pool = NULL;
+    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);
+    if (n < 0) {
+        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";
+        goto failed;
     }
-#else
-    ngx_pcre_pool = NULL;
-#endif
+
+    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);
+    if (n < 0) {
+        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";
+        goto failed;
+    }
 
-    return re;
+    return NGX_OK;
+
+failed:
+
+    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
+                  - rc->err.data;
+    return NGX_OK;
 }
 
 
@@ -117,7 +176,7 @@ ngx_regex_exec_array(ngx_array_t *a, ngx
 
         if (n < 0) {
             ngx_log_error(NGX_LOG_ALERT, log, 0,
-                          ngx_regex_exec_n " failed: %d on \"%V\" using \"%s\"",
+                          ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"",
                           n, s, re[i].name);
             return NGX_ERROR;
         }
@@ -141,11 +200,15 @@ ngx_regex_malloc(size_t size)
     if (ngx_threaded) {
         tls = ngx_thread_get_tls(ngx_core_tls_key);
         pool = tls->pool;
+
     } else {
         pool = ngx_pcre_pool;
     }
+
 #else
+
     pool = ngx_pcre_pool;
+
 #endif
 
     if (pool) {
--- a/src/core/ngx_regex.h
+++ b/src/core/ngx_regex.h
@@ -20,26 +20,36 @@
 
 typedef pcre  ngx_regex_t;
 
+
 typedef struct {
-    ngx_regex_t   *regex;
-    u_char        *name;
+    ngx_str_t     pattern;
+    ngx_pool_t   *pool;
+    ngx_int_t     options;
+
+    ngx_regex_t  *regex;
+    int           captures;
+    int           named_captures;
+    int           name_size;
+    u_char       *names;
+    ngx_str_t     err;
+} ngx_regex_compile_t;
+
+
+typedef struct {
+    ngx_regex_t  *regex;
+    u_char       *name;
 } ngx_regex_elt_t;
 
 
 void ngx_regex_init(void);
-ngx_regex_t *ngx_regex_compile(ngx_str_t *pattern, ngx_int_t options,
-    ngx_pool_t *pool, ngx_str_t *err);
-ngx_int_t ngx_regex_capture_count(ngx_regex_t *re);
+ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc);
 
 #define ngx_regex_exec(re, s, captures, size)                                \
     pcre_exec(re, NULL, (const char *) (s)->data, (s)->len, 0, 0,            \
               captures, size)
+#define ngx_regex_exec_n      "pcre_exec()"
 
 ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);
 
 
-#define ngx_regex_exec_n           "pcre_exec()"
-#define ngx_regex_capture_count_n  "pcre_fullinfo()"
-
-
 #endif /* _NGX_REGEX_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2407,27 +2407,25 @@ ngx_http_fastcgi_split(ngx_http_request_
 
     n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
 
+    if (n >= 0) { /* match */
+        f->script_name.len = captures[3] - captures[2];
+        f->script_name.data = r->uri.data;
+
+        f->path_info.len = captures[5] - captures[4];
+        f->path_info.data = r->uri.data + f->script_name.len;
+
+        return f;
+    }
+
     if (n == NGX_REGEX_NO_MATCHED) {
         f->script_name = r->uri;
         return f;
     }
 
-    if (n < 0) {
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
-                      n, &r->uri, &flcf->split_name);
-        return NULL;
-    }
-
-    /* match */
-
-    f->script_name.len = captures[3] - captures[2];
-    f->script_name.data = r->uri.data;
-
-    f->path_info.len = captures[5] - captures[4];
-    f->path_info.data = r->uri.data + f->script_name.len;
-
-    return f;
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+                  n, &r->uri, &flcf->split_name);
+    return NULL;
 
 #else
 
@@ -2518,39 +2516,34 @@ ngx_http_fastcgi_split_path_info(ngx_con
 #if (NGX_PCRE)
     ngx_http_fastcgi_loc_conf_t *flcf = conf;
 
-    ngx_int_t   n;
-    ngx_str_t  *value, err;
-    u_char      errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_str_t            *value;
+    ngx_regex_compile_t   rc;
+    u_char                errstr[NGX_MAX_CONF_ERRSTR];
 
     value = cf->args->elts;
 
     flcf->split_name = value[1];
 
-    err.len = NGX_MAX_CONF_ERRSTR;
-    err.data = errstr;
-
-    flcf->split_regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
-
-    if (flcf->split_regex == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+    rc.pattern = value[1];
+    rc.pool = cf->pool;
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
+
+    if (ngx_regex_compile(&rc) != NGX_OK) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
         return NGX_CONF_ERROR;
     }
 
-    n = ngx_regex_capture_count(flcf->split_regex);
-
-    if (n < 0) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           ngx_regex_capture_count_n " failed for "
-                           "pattern \"%V\"", &value[1]);
-        return NGX_CONF_ERROR;
-    }
-
-    if (n != 2) {
+    if (rc.captures != 2) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "pattern \"%V\" must have 2 captures", &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    flcf->split_regex = rc.regex;
+
     return NGX_CONF_OK;
 
 #else
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -412,7 +412,7 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
                 if (sn[n].regex) {
 
                     if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
-                                                   sn[n].regex)
+                                                   sn[n].regex->regex)
                         != NGX_OK)
                     {
                         return NGX_CONF_ERROR;
@@ -502,9 +502,9 @@ ngx_http_add_regex_referer(ngx_conf_t *c
     ngx_str_t *name, ngx_regex_t *regex)
 {
 #if (NGX_PCRE)
-    ngx_str_t         err;
-    ngx_regex_elt_t  *re;
-    u_char            errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_regex_elt_t      *re;
+    ngx_regex_compile_t   rc;
+    u_char                errstr[NGX_MAX_CONF_ERRSTR];
 
     if (name->len == 1) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
@@ -530,19 +530,23 @@ ngx_http_add_regex_referer(ngx_conf_t *c
         return NGX_CONF_OK;
     }
 
-    err.len = NGX_MAX_CONF_ERRSTR;
-    err.data = errstr;
-
     name->len--;
     name->data++;
 
-    re->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
 
-    if (re->regex == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+    rc.pattern = *name;
+    rc.pool = cf->pool;
+    rc.options = NGX_REGEX_CASELESS;
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
+
+    if (ngx_regex_compile(&rc) != NGX_OK) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
         return NGX_CONF_ERROR;
     }
 
+    re->regex = rc.regex;
     re->name = name->data;
 
     return NGX_CONF_OK;
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -294,9 +294,9 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
 {
     ngx_http_rewrite_loc_conf_t  *lcf = conf;
 
-    ngx_str_t                         *value, err;
-    ngx_int_t                          n;
+    ngx_str_t                         *value;
     ngx_uint_t                         last;
+    ngx_regex_compile_t                rc;
     ngx_http_script_code_pt           *code;
     ngx_http_script_compile_t          sc;
     ngx_http_script_regex_code_t      *regex;
@@ -313,15 +313,16 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
 
     value = cf->args->elts;
 
-    err.len = NGX_MAX_CONF_ERRSTR;
-    err.data = errstr;
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+    rc.pattern = value[1];
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
 
     /* TODO: NGX_REGEX_CASELESS */
 
-    regex->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
-
+    regex->regex = ngx_http_regex_compile(cf, &rc);
     if (regex->regex == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
         return NGX_CONF_ERROR;
     }
 
@@ -394,7 +395,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
 
     regex = sc.main;
 
-    regex->ncaptures = sc.ncaptures;
     regex->size = sc.size;
     regex->args = sc.args;
 
@@ -402,31 +402,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
         regex->lengths = NULL;
     }
 
-    n = ngx_regex_capture_count(regex->regex);
-
-    if (n < 0) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           ngx_regex_capture_count_n " failed for "
-                           "pattern \"%V\"", &value[1]);
-        return NGX_CONF_ERROR;
-    }
-
-    if (regex->ncaptures > (ngx_uint_t) n) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "pattern \"%V\" has less captures "
-                           "than referrenced in substitution \"%V\"",
-                           &value[1], &value[2]);
-        return NGX_CONF_ERROR;
-    }
-
-    if (regex->ncaptures < (ngx_uint_t) n) {
-        regex->ncaptures = (ngx_uint_t) n;
-    }
-
-    if (regex->ncaptures) {
-        regex->ncaptures = (regex->ncaptures + 1) * 3;
-    }
-
     regex_end = ngx_http_script_add_code(lcf->codes,
                                       sizeof(ngx_http_script_regex_end_code_t),
                                       &regex);
@@ -624,8 +599,9 @@ ngx_http_rewrite_if_condition(ngx_conf_t
 {
     u_char                        *p;
     size_t                         len;
-    ngx_str_t                     *value, err;
-    ngx_uint_t                     cur, last, n;
+    ngx_str_t                     *value;
+    ngx_uint_t                     cur, last;
+    ngx_regex_compile_t            rc;
     ngx_http_script_code_pt       *code;
     ngx_http_script_file_code_t   *fop;
     ngx_http_script_regex_code_t  *regex;
@@ -733,15 +709,15 @@ ngx_http_rewrite_if_condition(ngx_conf_t
 
             ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
 
-            err.len = NGX_MAX_CONF_ERRSTR;
-            err.data = errstr;
+            ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
 
-            regex->regex = ngx_regex_compile(&value[last],
-                                  (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0,
-                                   cf->pool, &err);
+            rc.pattern = value[last];
+            rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;
+            rc.err.len = NGX_MAX_CONF_ERRSTR;
+            rc.err.data = errstr;
 
+            regex->regex = ngx_http_regex_compile(cf, &rc);
             if (regex->regex == NULL) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
                 return NGX_CONF_ERROR;
             }
 
@@ -753,12 +729,6 @@ ngx_http_rewrite_if_condition(ngx_conf_t
             }
             regex->name = value[last];
 
-            n = ngx_regex_capture_count(regex->regex);
-
-            if (n) {
-                regex->ncaptures = (n + 1) * 3;
-            }
-
             return NGX_CONF_OK;
         }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -2450,27 +2450,28 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
 
     } else {
 #if (NGX_PCRE)
-        ngx_str_t     err;
-        ngx_regex_t  *regex;
-        u_char        errstr[NGX_MAX_CONF_ERRSTR];
-
-        err.len = NGX_MAX_CONF_ERRSTR;
-        err.data = errstr;
+        ngx_regex_compile_t  rgc;
+        u_char               errstr[NGX_MAX_CONF_ERRSTR];
 
         right.data[right.len] = '\0';
 
-        regex = ngx_regex_compile(&right, 0, r->pool, &err);
-
-        if (regex == NULL) {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s", err.data);
+        ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));
+
+        rgc.pattern = right;
+        rgc.pool = r->pool;
+        rgc.err.len = NGX_MAX_CONF_ERRSTR;
+        rgc.err.data = errstr;
+
+        if (ngx_regex_compile(&rgc) != NGX_OK) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);
             return NGX_HTTP_SSI_ERROR;
         }
 
-        rc = ngx_regex_exec(regex, &left, NULL, 0);
+        rc = ngx_regex_exec(rgc.regex, &left, NULL, 0);
 
         if (rc < NGX_REGEX_NO_MATCHED) {
             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                          ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
+                          ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
                           rc, &left, &right);
             return NGX_HTTP_SSI_ERROR;
         }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1420,7 +1420,7 @@ ngx_http_core_find_location(ngx_http_req
     ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *pclcf;
 #if (NGX_PCRE)
-    ngx_int_t                  n, len;
+    ngx_int_t                  n;
     ngx_uint_t                 noregex;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
 
@@ -1454,51 +1454,28 @@ ngx_http_core_find_location(ngx_http_req
 
     if (noregex == 0 && pclcf->regex_locations) {
 
-        len = 0;
-
         for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "test location: ~ \"%V\"", &(*clcfp)->name);
 
-            if ((*clcfp)->captures) {
-
-                len = (NGX_HTTP_MAX_CAPTURES + 1) * 3;
-
-                if (r->captures == NULL) {
-                    r->captures = ngx_palloc(r->pool, len * sizeof(int));
-                    if (r->captures == NULL) {
-                        return NGX_ERROR;
-                    }
-                }
+            n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);
+
+            if (n == NGX_OK) {
+                r->loc_conf = (*clcfp)->loc_conf;
+
+                /* look up nested locations */
+
+                rc = ngx_http_core_find_location(r);
+
+                return (rc == NGX_ERROR) ? rc : NGX_OK;
             }
 
-            n = ngx_regex_exec((*clcfp)->regex, &r->uri, r->captures, len);
-
-            if (n == NGX_REGEX_NO_MATCHED) {
+            if (n == NGX_DECLINED) {
                 continue;
             }
 
-            if (n < 0) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              ngx_regex_exec_n
-                              " failed: %d on \"%V\" using \"%V\"",
-                              n, &r->uri, &(*clcfp)->name);
-                return NGX_ERROR;
-            }
-
-            /* match */
-
-            r->loc_conf = (*clcfp)->loc_conf;
-
-            r->ncaptures = len;
-            r->captures_data = r->uri.data;
-
-            /* look up nested locations */
-
-            rc = ngx_http_core_find_location(r);
-
-            return (rc == NGX_ERROR) ? rc : NGX_OK;
+            return NGX_ERROR;
         }
     }
 #endif
@@ -1778,7 +1755,7 @@ ngx_http_map_uri_to_path(ngx_http_reques
 #if (NGX_PCRE)
         ngx_uint_t  captures;
 
-        captures = alias && clcf->captures;
+        captures = alias && clcf->regex;
         reserved += captures ? 1 : r->uri.len - alias + 1;
 #else
         reserved += r->uri.len - alias + 1;
@@ -2596,26 +2573,25 @@ ngx_http_core_regex_location(ngx_conf_t 
     ngx_str_t *regex, ngx_uint_t caseless)
 {
 #if (NGX_PCRE)
-    ngx_str_t  err;
-    u_char     errstr[NGX_MAX_CONF_ERRSTR];
-
-    err.len = NGX_MAX_CONF_ERRSTR;
-    err.data = errstr;
+    ngx_regex_compile_t  rc;
+    u_char               errstr[NGX_MAX_CONF_ERRSTR];
+
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+    rc.pattern = *regex;
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
 
 #if (NGX_HAVE_CASELESS_FILESYSTEM)
-    caseless = 1;
+    rc.options = NGX_REGEX_CASELESS;
 #endif
 
-    clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0,
-                                    cf->pool, &err);
-
+    clcf->regex = ngx_http_regex_compile(cf, &rc);
     if (clcf->regex == NULL) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
         return NGX_ERROR;
     }
 
     clcf->name = *regex;
-    clcf->captures = (ngx_regex_capture_count(clcf->regex) > 0);
 
     return NGX_OK;
 
@@ -3535,8 +3511,8 @@ ngx_http_core_server_name(ngx_conf_t *cf
 
 #if (NGX_PCRE)
         {
-        ngx_str_t  err;
-        u_char     errstr[NGX_MAX_CONF_ERRSTR];
+        ngx_regex_compile_t  rc;
+        u_char               errstr[NGX_MAX_CONF_ERRSTR];
 
         if (value[i].len == 1) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -3544,21 +3520,22 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
-        err.len = NGX_MAX_CONF_ERRSTR;
-        err.data = errstr;
-
         value[i].len--;
         value[i].data++;
 
-        sn->regex = ngx_regex_compile(&value[i], 0, cf->pool, &err);
-
+        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+        rc.pattern = value[i];
+        rc.err.len = NGX_MAX_CONF_ERRSTR;
+        rc.err.data = errstr;
+
+        sn->regex = ngx_http_regex_compile(cf, &rc);
         if (sn->regex == NULL) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
             return NGX_CONF_ERROR;
         }
 
         sn->name = value[i];
-        cscf->captures = (ngx_regex_capture_count(sn->regex) > 0);
+        cscf->captures = (rc.captures > 0);
         }
 #else
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -3665,20 +3642,6 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_c
         }
     }
 
-#if (NGX_PCRE)
-
-    if (alias && clcf->regex
-        && (ngx_regex_capture_count(clcf->regex) <= 0 || sc.ncaptures == 0))
-    {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "the \"alias\" directive must use captures "
-                           "inside location given by regular expression");
-
-        return NGX_CONF_ERROR;
-    }
-
-#endif
-
     return NGX_CONF_OK;
 }
 
@@ -4235,10 +4198,11 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
 
 #if (NGX_PCRE)
 
-    ngx_str_t         err, *value;
-    ngx_uint_t        i;
-    ngx_regex_elt_t  *re;
-    u_char            errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_str_t            *value;
+    ngx_uint_t            i;
+    ngx_regex_elt_t      *re;
+    ngx_regex_compile_t   rc;
+    u_char                errstr[NGX_MAX_CONF_ERRSTR];
 
     if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) {
         clcf->gzip_disable = ngx_array_create(cf->pool, 2,
@@ -4250,8 +4214,11 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
 
     value = cf->args->elts;
 
-    err.len = NGX_MAX_CONF_ERRSTR;
-    err.data = errstr;
+    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+    rc.pool = cf->pool;
+    rc.err.len = NGX_MAX_CONF_ERRSTR;
+    rc.err.data = errstr;
 
     for (i = 1; i < cf->args->nelts; i++) {
 
@@ -4265,14 +4232,15 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
             return NGX_CONF_ERROR;
         }
 
-        re->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
-                                      &err);
-
-        if (re->regex == NULL) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        rc.pattern = value[1];
+        rc.options = NGX_REGEX_CASELESS;
+
+        if (ngx_regex_compile(&rc) != NGX_OK) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
             return NGX_CONF_ERROR;
         }
 
+        re->regex = rc.regex;
         re->name = value[i].data;
     }
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -126,6 +126,7 @@ typedef struct {
     ngx_hash_t                 variables_hash;
 
     ngx_array_t                variables;       /* ngx_http_variable_t */
+    ngx_uint_t                 ncaptures;
 
     ngx_uint_t                 server_names_hash_max_size;
     ngx_uint_t                 server_names_hash_bucket_size;
@@ -238,7 +239,7 @@ typedef struct {
 
 struct ngx_http_server_name_s {
 #if (NGX_PCRE)
-    ngx_regex_t               *regex;
+    ngx_http_regex_t          *regex;
 #endif
     ngx_http_core_srv_conf_t  *server;   /* virtual name server conf */
     ngx_str_t                  name;
@@ -267,9 +268,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_str_t     name;          /* location name */
 
 #if (NGX_PCRE)
-    ngx_regex_t  *regex;
-
-    unsigned      captures:1;
+    ngx_http_regex_t  *regex;
 #endif
 
     unsigned      noname:1;   /* "if () {}" block or limit_except */
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1700,7 +1700,6 @@ ngx_http_find_virtual_server(ngx_http_re
 #if (NGX_PCRE)
 
     if (len && r->virtual_names->nregex) {
-        size_t                   ncaptures;
         ngx_int_t                n;
         ngx_uint_t               i;
         ngx_str_t                name;
@@ -1709,44 +1708,22 @@ ngx_http_find_virtual_server(ngx_http_re
         name.len = len;
         name.data = host;
 
-        ncaptures = 0;
-
         sn = r->virtual_names->regex;
 
         for (i = 0; i < r->virtual_names->nregex; i++) {
 
-            if (sn[i].server->captures && r->captures == NULL) {
-
-                ncaptures = (NGX_HTTP_MAX_CAPTURES + 1) * 3;
-
-                r->captures = ngx_palloc(r->pool, ncaptures * sizeof(int));
-                if (r->captures == NULL) {
-                    return NGX_ERROR;
-                }
+            n = ngx_http_regex_exec(r, sn[i].regex, &name);
+
+            if (n == NGX_OK) {
+                cscf = sn[i].server;
+                goto found;
             }
 
-            n = ngx_regex_exec(sn[i].regex, &name, r->captures, ncaptures);
-
-            if (n == NGX_REGEX_NO_MATCHED) {
+            if (n == NGX_DECLINED) {
                 continue;
             }
 
-            if (n < 0) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              ngx_regex_exec_n
-                              " failed: %d on \"%V\" using \"%V\"",
-                              n, &name, &sn[i].name);
-                return NGX_ERROR;
-            }
-
-            /* match */
-
-            cscf = sn[i].server;
-
-            r->ncaptures = ncaptures;
-            r->captures_data = host;
-
-            goto found;
+            return NGX_ERROR;
         }
     }
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,7 +10,6 @@
 
 #define NGX_HTTP_MAX_URI_CHANGES           10
 #define NGX_HTTP_MAX_SUBREQUESTS           50
-#define NGX_HTTP_MAX_CAPTURES              9
 
 /* must be 2^n */
 #define NGX_HTTP_LC_HEADER_LEN             32
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -251,8 +251,6 @@ ngx_http_script_compile(ngx_http_script_
             {
             ngx_uint_t  n;
 
-            /* NGX_HTTP_MAX_CAPTURES is 9 */
-
             if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
 
                 n = sc->source->data[i] - '0';
@@ -828,20 +826,9 @@ ngx_http_script_regex_start_code(ngx_htt
         e->line.data = e->sp->data;
     }
 
-    if (code->ncaptures && r->captures == NULL) {
+    rc = ngx_http_regex_exec(r, code->regex, &e->line);
 
-        r->captures = ngx_palloc(r->pool,
-                                 (NGX_HTTP_MAX_CAPTURES + 1) * 3 * sizeof(int));
-        if (r->captures == NULL) {
-            e->ip = ngx_http_script_exit;
-            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
-            return;
-        }
-    }
-
-    rc = ngx_regex_exec(code->regex, &e->line, r->captures, code->ncaptures);
-
-    if (rc == NGX_REGEX_NO_MATCHED) {
+    if (rc == NGX_DECLINED) {
         if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                           "\"%V\" does not match \"%V\"",
@@ -870,11 +857,7 @@ ngx_http_script_regex_start_code(ngx_htt
         return;
     }
 
-    if (rc < 0) {
-        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                      ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
-                      rc, &e->line, &code->name);
-
+    if (rc == NGX_ERROR) {
         e->ip = ngx_http_script_exit;
         e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
         return;
@@ -885,9 +868,6 @@ ngx_http_script_regex_start_code(ngx_htt
                       "\"%V\" matches \"%V\"", &code->name, &e->line);
     }
 
-    r->ncaptures = code->ncaptures;
-    r->captures_data = e->line.data;
-
     if (code->test) {
         if (code->negative_test) {
             e->sp->len = 0;
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -114,10 +114,9 @@ typedef struct {
 
 typedef struct {
     ngx_http_script_code_pt     code;
-    ngx_regex_t                *regex;
+    ngx_http_regex_t           *regex;
     ngx_array_t                *lengths;
     uintptr_t                   size;
-    uintptr_t                   ncaptures;
     uintptr_t                   status;
     uintptr_t                   next;
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -1666,6 +1666,157 @@ ngx_http_variable_pid(ngx_http_request_t
 }
 
 
+static ngx_int_t
+ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    v->not_found = 1;
+    return NGX_OK;
+}
+
+
+ngx_http_regex_t *
+ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
+{
+    u_char                     *p;
+    size_t                      size;
+    ngx_str_t                   name;
+    ngx_uint_t                  i, n;
+    ngx_http_variable_t        *v;
+    ngx_http_regex_t           *re;
+    ngx_http_regex_variable_t  *rv;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    rc->pool = cf->pool;
+
+    if (ngx_regex_compile(rc) != NGX_OK) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
+        return NULL;
+    }
+
+    re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));
+    if (re == NULL) {
+        return NULL;
+    }
+
+    re->regex = rc->regex;
+    re->ncaptures = rc->captures;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+    cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
+
+    n = (ngx_uint_t) rc->named_captures;
+
+    if (n == 0) {
+        return re;
+    }
+
+    rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));
+    if (rv == NULL) {
+        return NULL;
+    }
+
+    re->variables = rv;
+    re->nvariables = n;
+    re->name = rc->pattern;
+
+    size = rc->name_size;
+    p = rc->names;
+
+    for (i = 0; i < n; i++) {
+        rv[i].capture = 2 * ((p[0] << 8) + p[1]);
+
+        name.data = &p[2];
+        name.len = ngx_strlen(name.data);
+
+        v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+        if (v == NULL) {
+            return NULL;
+        }
+
+        rv[i].index = ngx_http_get_variable_index(cf, &name);
+        if (rv[i].index == NGX_ERROR) {
+            return NULL;
+        }
+
+        v->get_handler = ngx_http_variable_not_found;
+
+        p += i + size;
+    }
+
+    return re;
+}
+
+
+ngx_int_t
+ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)
+{
+    ngx_int_t                   rc, index;
+    ngx_uint_t                  i, n, len;
+    ngx_http_variable_value_t  *vv;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+    if (re->ncaptures) {
+        len = (cmcf->ncaptures + 1) * 3;
+
+        if (r->captures == NULL) {
+            r->captures = ngx_palloc(r->pool, len * sizeof(int));
+            if (r->captures == NULL) {
+                return NGX_ERROR;
+            }
+        }
+
+    } else {
+        len = 0;
+    }
+
+    rc = ngx_regex_exec(re->regex, s, r->captures, len);
+
+    if (rc == NGX_REGEX_NO_MATCHED) {
+        return NGX_DECLINED;
+    }
+
+    if (rc < 0) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+                      rc, s, &re->name);
+        return NGX_ERROR;
+    }
+
+    for (i = 0; i < re->nvariables; i++) {
+
+        n = re->variables[i].capture;
+        index = re->variables[i].index;
+        vv = &r->variables[index];
+
+        vv->len = r->captures[n + 1] - r->captures[n];
+        vv->valid = 1;
+        vv->no_cacheable = 0;
+        vv->not_found = 0;
+        vv->data = &s->data[r->captures[n]];
+
+#if (NGX_DEBUG)
+        {
+        ngx_http_variable_t  *v;
+
+        v = cmcf->variables.elts;
+
+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http regex set $%V to \"%*s\"",
+                       &v[index].name, vv->len, vv->data);
+        }
+#endif
+    }
+
+    r->ncaptures = len;
+    r->captures_data = s->data;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -41,6 +41,21 @@ struct ngx_http_variable_s {
 };
 
 
+typedef struct {
+    ngx_uint_t                    capture;
+    ngx_int_t                     index;
+} ngx_http_regex_variable_t;
+
+
+typedef struct {
+    ngx_regex_t                  *regex;
+    ngx_uint_t                    ncaptures;
+    ngx_http_regex_variable_t    *variables;
+    ngx_uint_t                    nvariables;
+    ngx_str_t                     name;
+} ngx_http_regex_t;
+
+
 ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,
     ngx_uint_t flags);
 ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
@@ -59,6 +74,12 @@ ngx_int_t ngx_http_variable_unknown_head
 #define ngx_http_clear_variable(r, index) r->variables0[index].text.data = NULL;
 
 
+ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,
+    ngx_regex_compile_t *rc);
+ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,
+    ngx_str_t *s);
+
+
 ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);
 ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);