changeset 102:f63280c59dd5 NGINX_0_2_5

nginx 0.2.5 *) Change: the duplicate value of the ngx_http_geo_module variable now causes the warning and changes old value. *) Feature: the ngx_http_ssi_module supports the "set" command. *) Feature: the ngx_http_ssi_module supports the "file" parameter in the "include" command. *) Feature: the ngx_http_ssi_module supports the variable value substitutions in expressions of the "if" command.
author Igor Sysoev <http://sysoev.ru>
date Tue, 04 Oct 2005 00:00:00 +0400
parents 5bb09dde34e7
children acdd83ee07cb
files CHANGES CHANGES.ru src/core/nginx.h src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_chunked_filter_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_gzip_filter_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_not_modified_filter_module.c src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_postpone_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_script.c src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/http/ngx_http_variables.c
diffstat 21 files changed, 520 insertions(+), 326 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,17 @@
+
+Changes with nginx 0.2.5                                         04 Oct 2005
+
+    *) Change: the duplicate value of the ngx_http_geo_module variable now 
+       causes the warning and changes old value.
+
+    *) Feature: the ngx_http_ssi_module supports the "set" command.
+
+    *) Feature: the ngx_http_ssi_module supports the "file" parameter in 
+       the "include" command.
+
+    *) Feature: the ngx_http_ssi_module supports the variable value 
+       substitutions in epxiressions of the "if" command.
+
 
 Changes with nginx 0.2.4                                         03 Oct 2005
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,3 +1,18 @@
+
+Изменения в nginx 0.2.5                                           04.10.2005
+
+    *) Изменение: дублирующее значение переменной модуля 
+       ngx_http_geo_module теперь выдаёт предупреждение и изменяёт старое 
+       значение.
+
+    *) Добавление: модуль ngx_http_ssi_module поддерживает команду set.
+
+    *) Добавление: модуль ngx_http_ssi_module поддерживает параметр file в 
+       команде include.
+
+    *) Добавление: модуль ngx_http_ssi_module поддерживает подстановку 
+       значений переменных в выражениях команды if.
+
 
 Изменения в nginx 0.2.4                                           03.10.2005
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.2.4"
+#define NGINX_VER          "nginx/0.2.5"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -514,7 +514,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 
     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
 
-    if (r->main == NULL) {
+    if (r->main == r) {
         b->last_buf = 1;
     }
 
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -50,7 +50,7 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_chunked_header_filter(ngx_http_request_t *r)
 {
-    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->main) {
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->main != r) {
         return ngx_http_next_header_filter(r);
     }
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -182,11 +182,11 @@ static char *
 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
     ngx_int_t                   rc, n;
+    ngx_str_t                  *value, file;
     ngx_uint_t                  i;
-    ngx_str_t                  *value, file;
     ngx_inet_cidr_t             cidrin;
     ngx_http_geo_conf_t        *geo;
-    ngx_http_variable_value_t  *var, **v;
+    ngx_http_variable_value_t  *var, *old, **v;
 
     geo = cf->ctx;
 
@@ -274,17 +274,33 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         *v = var;
     }
 
-    rc = ngx_radix32tree_insert(geo->tree, cidrin.addr, cidrin.mask,
-                                (uintptr_t) var);
-    if (rc == NGX_ERROR) {
-        return NGX_CONF_ERROR;
+    for (i = 2; i; i--) {
+        rc = ngx_radix32tree_insert(geo->tree, cidrin.addr, cidrin.mask,
+                                    (uintptr_t) var);
+        if (rc == NGX_OK) {
+            return NGX_CONF_OK;
+        }
+
+        if (rc == NGX_ERROR) {
+            return NGX_CONF_ERROR;
+        }
+
+        /* rc == NGX_BUSY */
+
+        old  = (ngx_http_variable_value_t *)
+                    ngx_radix32tree_find(geo->tree, cidrin.addr & cidrin.mask);
+
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "duplicate parameter \"%V\", value: \"%V\", "
+                           "old value: \"%V\"",
+                           &value[0], &var->text, &old->text);
+
+        rc = ngx_radix32tree_delete(geo->tree, cidrin.addr, cidrin.mask);
+
+        if (rc == NGX_ERROR) {
+            return NGX_CONF_ERROR;
+        }
     }
 
-    if (rc == NGX_BUSY) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate parameter \"%V\"",
-                           &value[0]);
-        return NGX_CONF_ERROR;
-    }
-
-    return NGX_CONF_OK;
+    return NGX_CONF_ERROR;
 }
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -280,7 +280,7 @@ ngx_http_gzip_header_filter(ngx_http_req
             && r->headers_out.status != NGX_HTTP_FORBIDDEN
             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
         || r->header_only
-        || r->main
+        || r->main != r
         || r->http_version < conf->http_version
         || r->headers_out.content_type.len == 0
         || (r->headers_out.content_encoding
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -84,7 +84,7 @@ ngx_http_headers_filter(ngx_http_request
 
     if ((r->headers_out.status != NGX_HTTP_OK
          && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
-        || r->main)
+        || r->main != r)
     {
         return ngx_http_next_header_filter(r);
     }
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -52,7 +52,7 @@ static ngx_int_t ngx_http_not_modified_h
     time_t  ims;
 
     if (r->headers_out.status != NGX_HTTP_OK
-        || r->main
+        || r->main != r
         || r->headers_in.if_modified_since == NULL
         || r->headers_out.last_modified_time == -1)
     {
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -134,7 +134,7 @@ ngx_http_range_header_filter(ngx_http_re
 
     if (r->http_version < NGX_HTTP_VERSION_10
         || r->headers_out.status != NGX_HTTP_OK
-        || r->main
+        || r->main != r
         || r->headers_out.content_length_n == -1
         || !r->filter_allow_ranges)
     {
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -8,15 +8,18 @@
 #include <ngx_core.h>
 #include <ngx_http.h>
 
-#define NGX_HTTP_SSI_MAX_PARAMS   16
-
-#define NGX_HTTP_SSI_COMMAND_LEN  31
-#define NGX_HTTP_SSI_PARAM_LEN    31
-#define NGX_HTTP_SSI_PARAMS_N     4
-
-#define NGX_HTTP_SSI_ERROR        1
-
-#define NGX_HTTP_SSI_DATE_LEN     2048
+#define NGX_HTTP_SSI_MAX_PARAMS     16
+
+#define NGX_HTTP_SSI_COMMAND_LEN    31
+#define NGX_HTTP_SSI_PARAM_LEN      31
+#define NGX_HTTP_SSI_PARAMS_N       4
+
+#define NGX_HTTP_SSI_ERROR          1
+
+#define NGX_HTTP_SSI_DATE_LEN       2048
+
+
+#define NGX_HTTP_SSI_ADD_PREFIX     1
 
 
 typedef struct {
@@ -32,6 +35,12 @@ typedef struct {
 
 
 typedef struct {
+    ngx_str_t          name;
+    ngx_str_t          value;
+} ngx_http_ssi_var_t;
+
+
+typedef struct {
     ngx_buf_t         *buf;
 
     u_char            *pos;
@@ -56,6 +65,8 @@ typedef struct {
 
     size_t             value_len;
 
+    ngx_array_t        variables;
+
     ngx_uint_t         output;        /* unsigned  output:1; */
 
     ngx_str_t          timefmt;
@@ -113,12 +124,18 @@ static ngx_int_t ngx_http_ssi_output(ngx
     ngx_http_ssi_ctx_t *ctx);
 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx);
-
+static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
+    ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
+
+static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
+    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
 static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
-static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
+static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
 static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
@@ -221,6 +238,8 @@ static u_char ngx_http_ssi_string[] = "<
 
 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
 
+#define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
+#define  NGX_HTTP_SSI_INCLUDE_FILE     1
 
 #define  NGX_HTTP_SSI_ECHO_VAR         0
 #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
@@ -228,26 +247,25 @@ static ngx_str_t ngx_http_ssi_none = ngx
 #define  NGX_HTTP_SSI_CONFIG_ERRMSG    0
 #define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1
 
-#define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
-#define  NGX_HTTP_SSI_INCLUDE_FILE     1
+#define  NGX_HTTP_SSI_SET_VAR          0
+#define  NGX_HTTP_SSI_SET_VALUE        1
 
 #define  NGX_HTTP_SSI_IF_EXPR          0
 
 
+static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
+    { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0 },
+    { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0 },
+    { ngx_null_string, 0, 0 }
+};
+
+
 static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
     { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1 },
     { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0 },
     { ngx_null_string, 0, 0 }
 };
 
-static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
-    { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0 },
-#if 0
-    { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0 },
-#endif
-    { ngx_null_string, 0, 0 }
-};
-
 
 static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {
     { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0 },
@@ -256,8 +274,15 @@ static ngx_http_ssi_param_t  ngx_http_ss
 };
 
 
+static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {
+    { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1 },
+    { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1 },
+    { ngx_null_string, 0, 0 }
+};
+
+
 static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {
-    { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 0 },
+    { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1 },
     { ngx_null_string, 0, 0 }
 };
 
@@ -268,11 +293,12 @@ static ngx_http_ssi_param_t  ngx_http_ss
 
 
 static ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {
+    { ngx_string("include"), ngx_http_ssi_include,
+                       ngx_http_ssi_include_params, 0, 1 },
     { ngx_string("echo"), ngx_http_ssi_echo, ngx_http_ssi_echo_params, 0, 0 },
     { ngx_string("config"), ngx_http_ssi_config,
                        ngx_http_ssi_config_params, 0, 0 },
-    { ngx_string("include"), ngx_http_ssi_include,
-                       ngx_http_ssi_include_params, 0, 1 },
+    { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0 },
 
     { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0 },
     { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params, 1, 0 },
@@ -355,7 +381,7 @@ found:
 
     r->filter_need_in_memory = 1;
 
-    if (r->main == NULL) {
+    if (r->main == r) {
         r->headers_out.content_length_n = -1;
         if (r->headers_out.content_length) {
             r->headers_out.content_length->hash = 0;
@@ -1301,6 +1327,296 @@ ngx_http_ssi_parse(ngx_http_request_t *r
 }
 
 
+static ngx_str_t *
+ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name)
+{
+    ngx_uint_t           i;
+    ngx_http_ssi_var_t  *var;
+    ngx_http_ssi_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+    var = ctx->variables.elts;
+    for (i = 0; i < ctx->variables.nelts; i++) {
+        if (name->len != var[i].name.len) {
+            continue;
+        }
+
+        if (ngx_strncasecmp(name->data, var[i].name.data, name->len) == 0) {
+            return &var[i].value;
+        }
+    }
+
+    return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+    ngx_str_t *text, ngx_uint_t flags)
+{
+    u_char                      ch, *p, **value, *data;
+    size_t                     *size, len, prefix;
+    ngx_str_t                   var, part, *val;
+    ngx_uint_t                  i, j, n, bracket;
+    ngx_array_t                 lengths, values;
+    ngx_http_variable_value_t  *vv;
+
+    n = ngx_http_script_variables_count(text);
+
+    if (n == 0) {
+
+        if (!(flags & NGX_HTTP_SSI_ADD_PREFIX)) {
+            return NGX_OK;
+        }
+
+        if (text->data[0] != '/') {
+            for (prefix = r->uri.len; prefix; prefix--) {
+                if (r->uri.data[prefix - 1] == '/') {
+                    break;
+                }
+            }
+
+            if (prefix) {
+                len = prefix + text->len;
+
+                data = ngx_palloc(r->pool, len);
+                if (data == NULL) {
+                    return NGX_ERROR;
+                }
+
+                p = ngx_cpymem(data, r->uri.data, prefix);
+                ngx_memcpy(p, text->data, text->len);
+
+                text->len = len;
+                text->data = data;
+            }
+        }
+
+        return NGX_OK;
+    }
+
+    if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    len = 0;
+    i = 0;
+
+    while (i < text->len) {
+
+        if (text->data[i] == '$') {
+
+            var.len = 0;
+
+            if (++i == text->len) {
+                goto invalid_variable;
+            }
+
+            if (text->data[i] == '{') {
+                bracket = 1;
+
+                if (++i == text->len) {
+                    goto invalid_variable;
+                }
+
+                var.data = &text->data[i];
+
+            } else {
+                bracket = 0;
+                var.data = &text->data[i];
+            }
+
+            for ( /* void */ ; i < text->len; i++, var.len++) {
+                ch = text->data[i];
+
+                if (ch == '}' && bracket) {
+                    i++;
+                    bracket = 0;
+                    break;
+                }
+
+                if ((ch >= 'A' && ch <= 'Z')
+                    || (ch >= 'a' && ch <= 'z')
+                    || (ch >= '0' && ch <= '9')
+                    || ch == '_')
+                {
+                    continue;
+                }
+
+                break;
+            }
+
+            if (bracket) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "the closing bracket in \"%V\" "
+                              "variable is missing", &var);
+                return NGX_ERROR;
+            }
+
+            if (var.len == 0) { 
+                goto invalid_variable;
+            }
+
+            for (j = 0; j < var.len; j++) {
+                var.data[j] = ngx_tolower(var.data[j]);
+            }
+
+            val = ngx_http_ssi_get_variable(r, &var);
+
+            if (val == NULL) {
+                vv = ngx_http_get_variable(r, &var);
+
+                if (vv == NULL) {
+                    return NGX_ERROR;
+                }
+
+                if (vv == NGX_HTTP_VAR_NOT_FOUND) {
+                    continue;
+                }
+
+                part = vv->text;
+
+            } else {
+                part = *val;
+            }
+
+        } else {
+            part.len = 0;
+            part.data = &text->data[i];
+
+            while (i < text->len && text->data[i] != '$') {
+                i++;
+                part.len++;
+            }
+        }
+
+        len += part.len;
+
+        size = ngx_array_push(&lengths);
+        if (size == NULL) {
+            return NGX_ERROR;
+        }
+
+        *size = part.len;
+
+        value = ngx_array_push(&values);
+        if (value == NULL) {
+            return NGX_ERROR;
+        }
+
+        *value = part.data;
+    }
+
+    prefix = 0;
+
+    size = lengths.elts;
+    value = values.elts;
+
+    if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
+        for (i = 0; i < values.nelts; i++) {
+            if (size[i] != 0) {
+                if (*value[i] != '/') {
+                    for (prefix = r->uri.len; prefix; prefix--) {
+                        if (r->uri.data[prefix - 1] == '/') {
+                            len += prefix;
+                            break;
+                        }
+                    }
+                }
+
+                break;
+            }
+        }
+    }
+
+    p = ngx_palloc(r->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    text->len = len;
+    text->data = p;
+
+    if (prefix) {
+        p = ngx_cpymem(p, r->uri.data, prefix);
+    }
+
+    for (i = 0; i < values.nelts; i++) {
+        p = ngx_cpymem(p, value[i], size[i]);
+    }
+
+    return NGX_OK;
+
+invalid_variable:
+
+    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                  "invalid variable name in \"%V\"", text);
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+    ngx_str_t **params)
+{
+    ngx_str_t   *uri, *file, args;
+    ngx_uint_t   i;
+
+    uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
+    file = params[NGX_HTTP_SSI_INCLUDE_FILE];
+
+    if (uri && file) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
+                      uri, file);
+        return NGX_HTTP_SSI_ERROR;
+    }
+
+    if (uri == NULL && file == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "no parameter in \"include\" SSI command");
+        return NGX_HTTP_SSI_ERROR;
+    }
+
+    if (uri == NULL) {
+        uri = file;
+    }
+
+    if (ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX)
+        != NGX_OK)
+    {
+        return NGX_HTTP_SSI_ERROR;
+    }
+
+    args.len = 0;
+    args.data = NULL;
+
+    if (params[NGX_HTTP_SSI_INCLUDE_VIRTUAL]) {
+        for (i = 0; i < uri->len; i++) {
+            if (uri->data[i] == '?') {
+                args.len = uri->len - i - 1;
+                args.data = &uri->data[i + 1];
+                uri->len -= args.len + 1;
+
+                break;
+            }
+        }
+    }
+
+    if (ngx_http_subrequest(r, uri, &args) != NGX_OK) {
+        return NGX_HTTP_SSI_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
 static ngx_int_t
 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
@@ -1313,17 +1629,25 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
 
     var = params[NGX_HTTP_SSI_ECHO_VAR];
 
-    for (i = 0; i < var->len; i++) {
-        var->data[i] = ngx_tolower(var->data[i]);
+    value = ngx_http_ssi_get_variable(r, var);
+
+    if (value == NULL) {
+        for (i = 0; i < var->len; i++) {
+            var->data[i] = ngx_tolower(var->data[i]);
+        }
+
+        vv = ngx_http_get_variable(r, var);
+
+        if (vv == NULL) {
+            return NGX_HTTP_SSI_ERROR;
+        }
+
+        if (vv != NGX_HTTP_VAR_NOT_FOUND) {
+            value = &vv->text;
+        }
     }
 
-    vv = ngx_http_get_variable(r, var);
-
-    if (vv == NULL) {
-        return NGX_HTTP_SSI_ERROR;
-    }
-
-    if (vv == NGX_HTTP_VAR_NOT_FOUND) {
+    if (value == NULL) {
         value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
 
         if (value == NULL) {
@@ -1334,8 +1658,6 @@ ngx_http_ssi_echo(ngx_http_request_t *r,
         }
 
     } else {
-        value = &vv->text;
-
         if (value->len == 0) {
             return NGX_OK;
         }
@@ -1387,215 +1709,49 @@ ngx_http_ssi_config(ngx_http_request_t *
 
 
 static ngx_int_t
-ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
-    u_char                      ch, *p, **value, *data;
-    size_t                     *size, len, prefix;
-    ngx_uint_t                  i, j, n, bracket;
-    ngx_str_t                   uri, args, name;
-    ngx_array_t                 lengths, values;
-    ngx_http_variable_value_t  *vv;
-
-    /* TODO: file, virtual vs file */
-
-    uri = *params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
-    args.len = 0;
-    args.data = NULL;
-    prefix = 0;
-
-    n = ngx_http_script_variables_count(&uri);
-
-    if (n > 0) {
-
-        if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
-            return NGX_HTTP_SSI_ERROR;
-        }
-
-        if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
+    ngx_str_t           *name, *value, *vv;
+    ngx_http_ssi_var_t  *var;
+    ngx_http_ssi_ctx_t  *mctx;
+
+    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+    if (mctx->variables.elts == NULL) {
+        if (ngx_array_init(&mctx->variables, r->pool, 4,
+                           sizeof(ngx_http_ssi_var_t)) != NGX_OK)
+        {
             return NGX_HTTP_SSI_ERROR;
         }
-
-        len = 0;
-
-        for (i = 0; i < uri.len; /* void */ ) {
-
-            name.len = 0;
-
-            if (uri.data[i] == '$') {
-
-                if (++i == uri.len) {
-                    goto invalid_variable;
-                }
-
-                if (uri.data[i] == '{') {
-                    bracket = 1;
-
-                    if (++i == uri.len) {
-                        goto invalid_variable;
-                    }
-
-                    name.data = &uri.data[i];
-
-                } else {
-                    bracket = 0;
-                    name.data = &uri.data[i];
-                }
-
-                for ( /* void */ ; i < uri.len; i++, name.len++) {
-                    ch = uri.data[i];
-
-                    if (ch == '}' && bracket) {
-                        i++;
-                        bracket = 0;
-                        break;
-                    }
-
-                    if ((ch >= 'A' && ch <= 'Z')
-                        || (ch >= 'a' && ch <= 'z')
-                        || (ch >= '0' && ch <= '9')
-                        || ch == '_')
-                    {
-                        continue;
-                    }
-
-                    break;
-                }
-
-                if (bracket) {
-                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                                  "the closing bracket in \"%V\" "
-                                  "variable is missing", &name);
-                    return NGX_HTTP_SSI_ERROR;
-                }
-
-                if (name.len == 0) {
-                    goto invalid_variable;
-                }
-
-                for (j = 0; j < name.len; j++) {
-                    name.data[j] = ngx_tolower(name.data[j]);
-                }
-
-                vv = ngx_http_get_variable(r, &name);
-
-                if (vv == NULL) {
-                    return NGX_HTTP_SSI_ERROR;
-                }
-
-                if (vv == NGX_HTTP_VAR_NOT_FOUND) {
-                    continue;
-                }
-
-                name = vv->text;
-
-            } else {
-                name.data = &uri.data[i];
-
-                while (i < uri.len && uri.data[i] != '$') {
-                    i++;
-                    name.len++;
-                }
-            }
-
-            len += name.len;
-
-            size = ngx_array_push(&lengths);
-            if (size == NULL) {
-                return NGX_HTTP_SSI_ERROR;
-            }
-
-            *size = name.len;
-
-            value = ngx_array_push(&values);
-            if (value == NULL) {
-                return NGX_HTTP_SSI_ERROR;
-            }
-
-            *value = name.data;
-        }
-
-        size = lengths.elts;
-        value = values.elts;
-
-        for (i = 0; i < values.nelts; i++) {
-            if (size[i] != 0) {
-                if (*value[i] != '/') {
-                    for (prefix = r->uri.len; prefix; prefix--) {
-                        if (r->uri.data[prefix - 1] == '/') {
-                            len += prefix;
-                            break;
-                        }
-                    }
-                }
-
-                break;
-            }
-        }
-
-        p = ngx_palloc(r->pool, len);
-        if (p == NULL) {
-            return NGX_HTTP_SSI_ERROR;
-        }
-
-        uri.len = len;
-        uri.data = p;
-
-        if (prefix) {
-            p = ngx_cpymem(p, r->uri.data, prefix);
-        }
-
-        for (i = 0; i < values.nelts; i++) {
-            p = ngx_cpymem(p, value[i], size[i]);
-        }
-
-    } else {
-        if (uri.data[0] != '/') {
-            for (prefix = r->uri.len; prefix; prefix--) {
-                if (r->uri.data[prefix - 1] == '/') {
-                    break;
-                }
-            }
-
-            if (prefix) {
-                len = prefix + uri.len;
-
-                data = ngx_palloc(r->pool, len);
-                if (data == NULL) {
-                    return NGX_HTTP_SSI_ERROR;
-                }
-
-                p = ngx_cpymem(data, r->uri.data, prefix);
-                ngx_memcpy(p, uri.data, uri.len);
-
-                uri.len = len;
-                uri.data = data;
-            }
-        }
     }
 
-    for (i = 0; i < uri.len; i++) {
-        if (uri.data[i] == '?') {
-            args.len = uri.len - i - 1;
-            args.data = &uri.data[i + 1];
-            uri.len -= args.len + 1;
-
-            break;
-        }
-    }
-
-    if (ngx_http_subrequest(r, &uri, &args) != NGX_OK) {
+    name = params[NGX_HTTP_SSI_SET_VAR];
+    value = params[NGX_HTTP_SSI_SET_VALUE];
+
+    if (ngx_http_ssi_evaluate_string(r, ctx, value, 0) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
+    vv = ngx_http_ssi_get_variable(r, name);
+
+    if (vv) {
+        *vv = *value;
+        return NGX_OK;
+    }
+
+    var = ngx_array_push(&mctx->variables);
+    if (var == NULL) {
+        return NGX_HTTP_SSI_ERROR;
+    }
+
+    var->name = *name;
+    var->value = *value;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "set: \"%V\"=\"%V\"", name, value);
+
     return NGX_OK;
-
-invalid_variable:
-
-    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                  "invalid variable name in \"%V\"", &uri);
-
-    return NGX_ERROR;
 }
 
 
@@ -1603,55 +1759,55 @@ static ngx_int_t
 ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
-    u_char                     *p, *last;
-    ngx_str_t                  *expr, var, left, right;
-    ngx_int_t                   rc;
-    ngx_uint_t                  negative, noregex;
-    ngx_http_variable_value_t  *vv;
+    u_char       *p, *last;
+    ngx_str_t    *expr, left, right;
+    ngx_int_t     rc;
+    ngx_uint_t    negative, noregex;
 #if (NGX_PCRE)
-    ngx_str_t                   err;
-    ngx_regex_t                *regex;
-    u_char                      errstr[NGX_MAX_CONF_ERRSTR];
+    ngx_str_t     err;
+    ngx_regex_t  *regex;
+    u_char        errstr[NGX_MAX_CONF_ERRSTR];
 #endif
 
     expr = params[NGX_HTTP_SSI_IF_EXPR];
 
-    if (expr->data[0] != '$') {
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "invalid variable name in \"%V\"", expr);
-        return NGX_HTTP_SSI_ERROR;
-    }
-
-    var.data = expr->data + 1;
+    left.data = expr->data;
     last = expr->data + expr->len;
 
-    for (p = var.data; p < last; p++) {
+    for (p = left.data; p < last; p++) {
         if (*p >= 'A' && *p <= 'Z') {
             *p |= 0x20;
             continue;
         }
 
-        if ((*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+        if ((*p >= 'a' && *p <= 'z')
+             || (*p >= '0' && *p <= '9')
+             || *p == '$' || *p == '{' || *p == '}' || *p == '_')
+        {
             continue;
         }
 
         break;
     }
 
-    var.len = p - var.data;
+    left.len = p - left.data;
 
     while (p < last && *p == ' ') {
         p++;
     }
 
-    vv = ngx_http_get_variable(r, &var);
-
-    if (vv == NULL) {
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "left: \"%V\"", &left);
+
+    if (ngx_http_ssi_evaluate_string(r, ctx, &left, 0) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "evaluted left: \"%V\"", &left);
+
     if (p == last) {
-        if (vv != NGX_HTTP_VAR_NOT_FOUND && vv->text.len != 0) {
+        if (left.len) {
             ctx->output = 1;
 
         } else {
@@ -1693,16 +1849,15 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     right.len = last - p;
     right.data = p;
 
-    if (vv == NGX_HTTP_VAR_NOT_FOUND) {
-        left.len = 0;
-        left.data = (u_char *) "";
-
-    } else {
-        left = vv->text;
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "right: \"%V\"", &right);
+
+    if (ngx_http_ssi_evaluate_string(r, ctx, &right, 0) != NGX_OK) {
+        return NGX_HTTP_SSI_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "left: \"%V\" right: \"%V\"", &left, &right);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "evaluted right: \"%V\"", &right);
 
     if (noregex) {
         if (left.len != right.len) {
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -339,7 +339,7 @@ ngx_http_static_handler(ngx_http_request
 
     b->in_file = 1;
 
-    if (r->main == NULL) {
+    if (r->main == r) {
         b->last_buf = 1;
     }
 
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -206,7 +206,7 @@ ngx_http_userid_filter(ngx_http_request_
     ngx_http_userid_ctx_t   *ctx;
     ngx_http_userid_conf_t  *conf;
 
-    if (r->main) {
+    if (r->main != r) {
         return ngx_http_next_header_filter(r);
     }
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -498,7 +498,7 @@ ngx_http_core_run_phases(ngx_http_reques
             r->phase = NGX_HTTP_FIND_CONFIG_PHASE;
         }
 
-        if (r->phase == NGX_HTTP_ACCESS_PHASE && r->main) {
+        if (r->phase == NGX_HTTP_ACCESS_PHASE && r->main != r) {
             continue;
         }
 
@@ -1099,7 +1099,7 @@ ngx_http_subrequest(ngx_http_request_t *
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    sr->main = r->main ? r->main : r;
+    sr->main = r->main;
     sr->parent = r;
     sr->read_event_handler = ngx_http_request_empty_handler;
     sr->write_event_handler = ngx_http_request_empty_handler;
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -159,7 +159,7 @@ ngx_http_header_filter(ngx_http_request_
     ngx_table_elt_t           *header;
     ngx_http_core_loc_conf_t  *clcf;
 
-    if (r->main) {
+    if (r->main != r) {
         return NGX_OK;
     }
 
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -51,7 +51,6 @@ ngx_http_postpone_filter(ngx_http_reques
 {
     ngx_int_t                      rc;
     ngx_chain_t                   *out;
-    ngx_http_request_t            *mr;
     ngx_http_postponed_request_t  *pr, **ppr;
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -110,16 +109,14 @@ ngx_http_postpone_filter(ngx_http_reques
         out = in;
     }
 
-    mr = r->main ? r->main : r;
-
-    if (out == NULL && mr->out == NULL && !mr->connection->buffered) {
+    if (out == NULL && r->main->out == NULL && !r->main->connection->buffered) {
         return NGX_OK;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http postpone filter out \"%V\"", &r->uri);
 
-    rc = ngx_http_next_filter(mr, out);
+    rc = ngx_http_next_filter(r->main, out);
 
     if (rc == NGX_ERROR) {
         /* NGX_ERROR may be returned by any filter */
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -423,6 +423,8 @@ void ngx_http_init_request(ngx_event_t *
     c->single_connection = 1;
     r->connection = c;
 
+    r->main = r;
+
     r->start_time = ngx_time();
 
     r->headers_in.content_length_n = -1;
@@ -1666,8 +1668,7 @@ ngx_http_writer(ngx_http_request_t *r)
         wev->delayed = 0;
 
         if (!wev->ready) {
-            clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
-                                                ngx_http_core_module);
+            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
             ngx_add_timer(wev, clcf->send_timeout);
 
             if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
@@ -1683,8 +1684,7 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                            "http writer delayed");
 
-            clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
-                                                ngx_http_core_module);
+            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
 
             if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
                 ngx_http_close_request(r, 0);
@@ -1712,8 +1712,7 @@ ngx_http_writer(ngx_http_request_t *r)
                    "http writer output filter: %d, \"%V\"", rc, &r->uri);
 
     if (rc == NGX_AGAIN) {
-        clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
-                                            ngx_http_core_module);
+        clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
         if (!wev->ready && !wev->delayed) {
             ngx_add_timer(wev, clcf->send_timeout);
         }
@@ -1809,7 +1808,7 @@ ngx_http_discard_body(ngx_http_request_t
     ssize_t       size;
     ngx_event_t  *rev;
 
-    if (r->main) {
+    if (r->main != r) {
         return NGX_OK;
     }
 
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -361,11 +361,11 @@ ngx_http_script_copy_var_len_code(ngx_ht
 
     value = ngx_http_get_indexed_variable(e->request, code->index);
 
-    if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
-        return 0;
+    if (value && value != NGX_HTTP_VAR_NOT_FOUND) {
+        return value->text.len;
     }
 
-    return value->text.len;
+    return 0;
 }
 
 
@@ -382,15 +382,14 @@ ngx_http_script_copy_var_code(ngx_http_s
     if (!e->skip) {
         value = ngx_http_get_indexed_variable(e->request, code->index);
 
-        if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
-            return;
-        }
+        if (value && value != NGX_HTTP_VAR_NOT_FOUND) {
+            e->pos = ngx_cpymem(e->pos, value->text.data, value->text.len);
 
-        e->pos = ngx_cpymem(e->pos, value->text.data, value->text.len);
-
-        if (e->log) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                       "http script var: \"%V\"", &e->buf);
+            if (e->log) {
+                ngx_log_debug1(NGX_LOG_DEBUG_HTTP,
+                               e->request->connection->log, 0,
+                               "http script var: \"%V\"", &e->buf);
+            }
         }
     }
 }
@@ -879,19 +878,18 @@ ngx_http_script_var_code(ngx_http_script
 
     value = ngx_http_get_indexed_variable(e->request, code->index);
 
-    if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
-        e->sp->value = 0;
-        e->sp->text.len = 0;
-        e->sp->text.data = (u_char *) "";
+    if (value && value != NGX_HTTP_VAR_NOT_FOUND) {
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script var: %ui, \"%V\"", value->value, &value->text);
+        *e->sp = *value;
         e->sp++;
 
         return;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script var: %ui, \"%V\"", value->value, &value->text);
-
-    *e->sp = *value;
+    e->sp->value = 0;
+    e->sp->text.len = 0;
+    e->sp->text.data = (u_char *) "";
     e->sp++;
 }
 
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -423,7 +423,7 @@ ngx_http_special_response_handler(ngx_ht
         cl->buf = b;
     }
 
-    if (r->main == NULL) {
+    if (r->main == r) {
         b->last_buf = 1;
     }
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -495,7 +495,7 @@ ngx_http_upstream_connect(ngx_http_reque
     }
 
     if (r->request_body) {
-        if (r->request_body->temp_file && r->main == NULL) {
+        if (r->request_body->temp_file && r->main == r) {
 
             /*
              * the r->request_body->buf can be reused for one request only,
@@ -560,7 +560,7 @@ ngx_http_upstream_reinit(ngx_http_reques
     /* reinit the subrequest's ngx_output_chain() context */
 
     if (r->request_body) {
-        if (r->request_body->temp_file && r->main && u->output.buf) {
+        if (r->request_body->temp_file && r->main != r && u->output.buf) {
 
             u->output.free = ngx_alloc_chain_link(r->pool);
             if (u->output.free == NULL) {
@@ -1454,7 +1454,7 @@ ngx_http_upstream_finalize_request(ngx_h
 
     r->connection->log->action = "sending to client";
 
-    if (rc == 0 && r->main == NULL) {
+    if (rc == 0 && r->main == r) {
         rc = ngx_http_send_last(r);
     }
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -229,7 +229,7 @@ ngx_http_get_indexed_variable(ngx_http_r
 
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 
-    if (cmcf->variables.elts == NULL || cmcf->variables.nelts <= index) {
+    if (cmcf->variables.nelts <= index) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                       "unknown variable index: %d", index);
         return NULL;