diff src/http/ngx_http_script.c @ 58:b55cbf18157e NGINX_0_1_29

nginx 0.1.29 *) Feature: the ngx_http_ssi_module supports "include virtual" command. *) Feature: the ngx_http_ssi_module supports the condition command like 'if expr="$NAME"' and "else" and "endif" commands. Only one nested level is supported. *) Feature: the ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT variables and "config timefmt" command. *) Feature: the "ssi_ignore_recycled_buffers" directive. *) Bugfix: the "echo" command did not show the default value for the empty QUERY_STRING variable. *) Change: the ngx_http_proxy_module was rewritten. *) Feature: the "proxy_redirect", "proxy_pass_request_headers", "proxy_pass_request_body", and "proxy_method" directives. *) Feature: the "proxy_set_header" directive. The "proxy_x_var" was canceled and must be replaced with the proxy_set_header directive. *) Change: the "proxy_preserve_host" is canceled and must be replaced with the "proxy_set_header Host $host" and the "proxy_redirect off" directives, the "proxy_set_header Host $host:$proxy_port" directive and the appropriate proxy_redirect directives. *) Change: the "proxy_set_x_real_ip" is canceled and must be replaced with the "proxy_set_header X-Real-IP $remote_addr" directive. *) Change: the "proxy_add_x_forwarded_for" is canceled and must be replaced with the "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for" directive. *) Change: the "proxy_set_x_url" is canceled and must be replaced with the "proxy_set_header X-URL http://$host:$server_port$request_uri" directive. *) Feature: the "fastcgi_param" directive. *) Change: the "fastcgi_root", "fastcgi_set_var" and "fastcgi_params" directive are canceled and must be replaced with the fastcgi_param directives. *) Feature: the "index" directive can use the variables. *) Feature: the "index" directive can be used at http and server levels. *) Change: the last index only in the "index" directive can be absolute. *) Feature: the "rewrite" directive can use the variables. *) Feature: the "internal" directive. *) Feature: the CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR, SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME, REQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables. *) Change: nginx now passes the invalid lines in a client request headers or a backend response header. *) Bugfix: if the backend did not transfer response for a long time and the "send_timeout" was less than "proxy_read_timeout", then nginx returned the 408 response. *) Bugfix: the segmentation fault was occurred if the backend sent an invalid line in response header; bug appeared in 0.1.26. *) Bugfix: the segmentation fault may occurred in FastCGI fault tolerance configuration. *) Bugfix: the "expires" directive did not remove the previous "Expires" and "Cache-Control" headers. *) Bugfix: nginx did not take into account trailing dot in "Host" header line. *) Bugfix: the ngx_http_auth_module did not work under Linux. *) Bugfix: the rewrite directive worked incorrectly, if the arguments were in a request. *) Bugfix: nginx could not be built on MacOS X.
author Igor Sysoev <http://sysoev.ru>
date Thu, 12 May 2005 00:00:00 +0400
parents 3050baa54a26
children 5db440287648
line wrap: on
line diff
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -9,107 +9,274 @@
 #include <ngx_http.h>
 
 
-ngx_int_t
-ngx_http_script_compile_lite(ngx_conf_t *cf, ngx_array_t *sources,
-    ngx_array_t **lengths, ngx_array_t **values,
-    ngx_http_script_compile_lite_start_pt start,
-    ngx_http_script_compile_lite_end_pt end)
+#define ngx_http_script_exit  (u_char *) &ngx_http_script_exit_code
+
+static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
+
+
+ngx_uint_t
+ngx_http_script_variables_count(ngx_str_t *value)
 {
-    uintptr_t                   *code;
-    ngx_uint_t                   i;
-    ngx_table_elt_t             *src;
-    ngx_http_variable_t         *var;
-    ngx_http_script_var_code_t  *var_code;
+    ngx_uint_t  i, n;
 
-    if (sources->nelts == 0) {
-        return NGX_OK;
+    for (n = 0, i = 0; i < value->len; i++) {
+        if (value->data[i] == '$') {
+            n++;
+        }
     }
 
-    if (*lengths == NULL) {
-        *lengths = ngx_array_create(cf->pool, 64, 1);
-        if (*lengths == NULL) {
+    return n;
+}
+
+
+ngx_int_t
+ngx_http_script_compile(ngx_http_script_compile_t *sc)
+{
+    u_char                                ch;
+    size_t                                size;
+    ngx_int_t                             index;
+    ngx_str_t                             name;
+    uintptr_t                            *code;
+    ngx_uint_t                            i, n, bracket;
+    ngx_http_script_var_code_t           *var_code;
+    ngx_http_script_copy_code_t          *copy;
+    ngx_http_script_copy_capture_code_t  *copy_capture;
+
+    if (*sc->lengths == NULL) {
+        n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+                             + sizeof(ngx_http_script_var_code_t))
+            + sizeof(uintptr_t);
+
+        *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
+        if (*sc->lengths == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+
+    if (*sc->values == NULL) {
+        n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+                              + sizeof(ngx_http_script_var_code_t))
+                + sizeof(uintptr_t)
+                + sc->source->len
+                + sizeof(uintptr_t) - 1)
+            & ~(sizeof(uintptr_t) - 1);
+
+        *sc->values = ngx_array_create(sc->cf->pool, n, 1);
+        if (*sc->values == NULL) {
             return NGX_ERROR;
         }
     }
 
-    if (*values == NULL) {
-        *values = ngx_array_create(cf->pool, 256, 1);
-        if (*values == NULL) {
-            return NGX_ERROR;
-        }
-    }
+    sc->variables = 0;
+
+    for (i = 0; i < sc->source->len; /* void */ ) {
+
+        name.len = 0;
+
+        if (sc->source->data[i] == '$') {
+
+            if (++i == sc->source->len) {
+                goto invalid_variable;
+            }
+
+            if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
+
+                copy_capture = ngx_http_script_add_code(*sc->lengths,
+                                   sizeof(ngx_http_script_copy_capture_code_t),
+                                   NULL);
+                if (copy_capture == NULL) {
+                    return NGX_ERROR;
+                }
+
+                copy_capture->code = (ngx_http_script_code_pt)
+                                         ngx_http_script_copy_capture_len_code;
+                copy_capture->n = 2 * (sc->source->data[i] - '0');
+
+                copy_capture = ngx_http_script_add_code(*sc->values,
+                                   sizeof(ngx_http_script_copy_capture_code_t),
+                                   &sc->main);
+                if (copy_capture == NULL) {
+                    return NGX_ERROR;
+                }
+
+                copy_capture->code = ngx_http_script_copy_capture_code;
+                copy_capture->n = sc->source->data[i] - '0';
+
+                if (sc->ncaptures < copy_capture->n) {
+                    sc->ncaptures = copy_capture->n;
+                }
+
+                copy_capture->n *= 2;
 
-    src = sources->elts;
-    for (i = 0; i < sources->nelts; i++) {
+                i++;
+
+                continue;
+            }
+
+            if (sc->source->data[i] == '{') {
+                bracket = 1;
+
+                if (++i == sc->source->len) {
+                    goto invalid_variable;
+                }
+
+                name.data = &sc->source->data[i];
+
+            } else {
+                bracket = 0;
+                name.data = &sc->source->data[i];
+            }
+
+            for ( /* void */ ; i < sc->source->len; i++, name.len++) {
+                ch = sc->source->data[i];
 
-        if (src[i].value.data[0] == '$') {
-            if (start(&src[i], *lengths, *values, 0) != NGX_OK) {
+                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_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
+                                   "the closing bracket in \"%V\" "
+                                   "variable is missing", &name);
                 return NGX_ERROR;
             }
 
-            src[i].value.len--;
-            src[i].value.data++;
+            if (name.len == 0) {
+                goto invalid_variable;
+            }
 
-            var = ngx_http_add_variable(cf, &src[i].value, 0);
+            sc->variables++;
 
-            if (var == NULL) {
+            index = ngx_http_get_variable_index(sc->cf, &name);
+
+            if (index == NGX_ERROR) {
                 return NGX_ERROR;
             }
 
-            var_code = ngx_array_push_n(*lengths,
-                                        sizeof(ngx_http_script_var_code_t));
+            var_code = ngx_http_script_add_code(*sc->lengths,
+                                            sizeof(ngx_http_script_var_code_t),
+                                            NULL);
             if (var_code == NULL) {
                 return NGX_ERROR;
             }
 
             var_code->code = (ngx_http_script_code_pt)
-                                                  ngx_http_script_copy_var_len;
-            var_code->index = var->index;
+                                            ngx_http_script_copy_var_len_code;
+            var_code->index = (uintptr_t) index;
 
 
-            var_code = ngx_array_push_n(*values,
-                                        sizeof(ngx_http_script_var_code_t));
+            var_code = ngx_http_script_add_code(*sc->values,
+                                            sizeof(ngx_http_script_var_code_t),
+                                            &sc->main);
             if (var_code == NULL) {
                 return NGX_ERROR;
             }
 
-            var_code->code = ngx_http_script_copy_var;
-            var_code->index = var->index;
+            var_code->code = ngx_http_script_copy_var_code;
+            var_code->index = (uintptr_t) index;
+
+            continue;
+        }
 
+        if (sc->source->data[i] == '?' && sc->compile_args) {
+            sc->args = 1;
+            sc->compile_args = 0;
 
-            if (end(*lengths, *values) != NGX_OK) {
+            code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
+                                            &sc->main);
+            if (code == NULL) {
                 return NGX_ERROR;
             }
 
+            *code = (uintptr_t) ngx_http_script_start_args_code;
+
+            i++;
+
             continue;
         }
 
-        if (start(&src[i], *lengths, *values, 1) != NGX_OK) {
+        name.data = &sc->source->data[i];
+
+        while (i < sc->source->len
+               && sc->source->data[i] != '$'
+               && !(sc->source->data[i] == '?' && sc->compile_args))
+        {
+            i++;
+            name.len++;
+        }
+
+        sc->size += name.len;
+
+        copy = ngx_http_script_add_code(*sc->lengths,
+                                        sizeof(ngx_http_script_copy_code_t),
+                                        NULL);
+        if (copy == NULL) {
             return NGX_ERROR;
         }
-    }
+
+        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->len = name.len;
+
+        size = (sizeof(ngx_http_script_copy_code_t) + name.len
+                   + sizeof(uintptr_t) - 1)
+                & ~(sizeof(uintptr_t) - 1);
 
-    code = ngx_array_push_n(*lengths, sizeof(uintptr_t));
-    if (code == NULL) {
-        return NGX_ERROR;
+        copy = ngx_http_script_add_code(*sc->values, size, &sc->main);
+        if (copy == NULL) {
+            return NGX_ERROR;
+        }
+
+        copy->code = ngx_http_script_copy_code;
+        copy->len = name.len;
+
+        ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t),
+                   name.data, name.len);
     }
 
-    *code = (uintptr_t) NULL;
+    if (sc->complete_lengths) {
+        code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
+        if (code == NULL) {
+            return NGX_ERROR;
+        }
 
-    code = ngx_array_push_n(*values, sizeof(uintptr_t));
-    if (code == NULL) {
-        return NGX_ERROR;
+        *code = (uintptr_t) NULL;
     }
 
-    *code = (uintptr_t) NULL;
+    if (sc->complete_values) {
+        code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
+                                        &sc->main);
+        if (code == NULL) {
+            return NGX_ERROR;
+        }
+
+        *code = (uintptr_t) NULL;
+    }
 
     return NGX_OK;
+
+invalid_variable:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
+
+    return NGX_ERROR;
 }
 
 
-#if 0
-
-static void *
+void *
 ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
 {
     if (*codes == NULL) {
@@ -122,51 +289,79 @@ ngx_http_script_start_code(ngx_pool_t *p
     return ngx_array_push_n(*codes, size);
 }
 
-#endif
+
+void *
+ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)
+{
+    u_char  *elts, **p;
+    void    *new;
+
+    elts = codes->elts;
+
+    new = ngx_array_push_n(codes, size);
+    if (new == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (code) {
+        if (elts != codes->elts) {
+            p = code;
+            *p += (u_char *) codes->elts - elts;
+        }
+    }
+
+    return new;
+}
 
 
 size_t
-ngx_http_script_copy_len(ngx_http_script_engine_t *e)
+ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
 {
     ngx_http_script_copy_code_t  *code;
 
-    code = (ngx_http_script_copy_code_t *) e->lite.ip;
+    code = (ngx_http_script_copy_code_t *) e->ip;
 
-    e->lite.ip += sizeof(ngx_http_script_copy_code_t);
+    e->ip += sizeof(ngx_http_script_copy_code_t);
 
     return code->len;
 }
 
 
 void
-ngx_http_script_copy(ngx_http_script_engine_t *e)
+ngx_http_script_copy_code(ngx_http_script_engine_t *e)
 {
     ngx_http_script_copy_code_t  *code;
 
-    code = (ngx_http_script_copy_code_t *) e->lite.ip;
+    code = (ngx_http_script_copy_code_t *) e->ip;
+
+    if (!e->skip) {
+        e->pos = ngx_cpymem(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t),
+                            code->len);
+    }
 
-    e->lite.pos = ngx_cpymem(e->lite.pos,
-                             e->lite.ip + sizeof(ngx_http_script_copy_code_t),
-                             code->len);
+    e->ip += sizeof(ngx_http_script_copy_code_t)
+          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
 
-    e->lite.ip += sizeof(ngx_http_script_copy_code_t)
-            + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
+    if (e->log) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                       "http script copy: \"%V\"", &e->buf);
+    }
 }
 
 
 size_t
-ngx_http_script_copy_var_len(ngx_http_script_engine_t *e)
+ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
 {
     ngx_http_variable_value_t   *value;
     ngx_http_script_var_code_t  *code;
 
-    code = (ngx_http_script_var_code_t *) e->lite.ip;
+    code = (ngx_http_script_var_code_t *) e->ip;
 
-    e->lite.ip += sizeof(ngx_http_script_var_code_t);
+    e->ip += sizeof(ngx_http_script_var_code_t);
 
-    value = ngx_http_get_indexed_variable(e->lite.request, code->index);
+    value = ngx_http_get_indexed_variable(e->request, code->index);
 
-    if (value == NULL || value == NGX_HTTP_VARIABLE_NOT_FOUND) {
+    if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
         return 0;
     }
 
@@ -175,20 +370,456 @@ ngx_http_script_copy_var_len(ngx_http_sc
 
 
 void
-ngx_http_script_copy_var(ngx_http_script_engine_t *e)
+ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
 {
     ngx_http_variable_value_t   *value;
     ngx_http_script_var_code_t  *code;
 
-    code = (ngx_http_script_var_code_t *) e->lite.ip;
+    code = (ngx_http_script_var_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_var_code_t);
+
+    if (!e->skip) {
+        value = ngx_http_get_indexed_variable(e->request, code->index);
+
+        if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
+            return;
+        }
+
+        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);
+        }
+    }
+}
+
+
+size_t
+ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_copy_capture_code_t  *code;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+    if ((e->args || e->quote)
+        && (e->request->quoted_uri || e->request->plus_in_uri))
+    {
+        return e->captures[code->n + 1] - e->captures[code->n]
+               + ngx_escape_uri(NULL,
+                                &e->line->data[e->captures[code->n]],
+                                e->captures[code->n + 1] - e->captures[code->n],
+                                NGX_ESCAPE_ARGS);
+    } else {
+        return e->captures[code->n + 1] - e->captures[code->n];
+    }
+}
+
+
+void
+ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_copy_capture_code_t  *code;
+
+    code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_copy_capture_code_t);
 
-    e->lite.ip += sizeof(ngx_http_script_var_code_t);
+    if ((e->args || e->quote)
+        && (e->request->quoted_uri || e->request->plus_in_uri))
+    {
+        e->pos = (u_char *) ngx_escape_uri(e->pos,
+                                &e->line->data[e->captures[code->n]],
+                                e->captures[code->n + 1] - e->captures[code->n],
+                                NGX_ESCAPE_ARGS);
+    } else {
+        e->pos = ngx_cpymem(e->pos,
+                            &e->line->data[e->captures[code->n]],
+                            e->captures[code->n + 1] - e->captures[code->n]);
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script capture: \"%V\"", &e->buf);
+}
+
+
+void
+ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
+{
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script args");
+    
+    e->args = e->pos;
+    e->ip += sizeof(uintptr_t);
+}
+
+
+void
+ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
+{
+    size_t                         len;
+    ngx_int_t                      rc;
+    ngx_uint_t                     n;
+    ngx_http_request_t            *r;
+    ngx_http_script_engine_t       le;
+    ngx_http_script_len_code_pt    lcode;
+    ngx_http_script_regex_code_t  *code;
+
+    code = (ngx_http_script_regex_code_t *) e->ip;
+
+    r = e->request;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http script regex: \"%V\"", &code->name);
+
+    if (code->uri) {
+        e->line = &r->uri;
+    } else {
+        e->sp--;
+        e->line = &e->sp->text;
+    }
 
-    value = ngx_http_get_indexed_variable(e->lite.request, code->index);
+    rc = ngx_regex_exec(code->regex, e->line, e->captures, code->ncaptures);
+
+    if (rc == NGX_REGEX_NO_MATCHED) {
+        if (e->log) {
+            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+                          "\"%V\" does not match \"%V\"", &code->name, e->line);
+        }
+
+        if (code->test) {
+            e->sp->value = 0; 
+            e->sp->text.len = 0;
+            e->sp->text.data = (u_char *) "";
+            e->sp++;
+
+            e->ip += sizeof(ngx_http_script_regex_code_t);
+            return;
+        }
+
+        e->ip += code->next;
+        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);
+
+        e->ip = ngx_http_script_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    if (e->log) {
+        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+                      "\"%V\" matches \"%V\"", &code->name, e->line);
+    }
+
+    if (code->test) {
+        e->sp->value = 1; 
+        e->sp->text.len = 1;
+        e->sp->text.data = (u_char *) "1";
+        e->sp++;
+
+        e->ip += sizeof(ngx_http_script_regex_code_t);
+        return;
+    }
+
+    if (code->status) {
+        e->status = code->status;
 
-    if (value == NULL || value == NGX_HTTP_VARIABLE_NOT_FOUND) {
+        if (!code->redirect) {
+            e->ip = ngx_http_script_exit;
+            return;
+        }
+    }
+
+    if (code->uri) {
+        r->internal = 1;
+        r->valid_unparsed_uri = 0;
+
+        if (code->break_cycle) {
+            r->valid_location = 0;
+
+        } else {
+            r->uri_changed = 1;
+        }
+    }
+
+    if (code->lengths == NULL) {
+        e->buf.len = code->size;
+
+        if (code->uri) {
+            if (rc && (r->quoted_uri || r->plus_in_uri)) {
+                e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
+                                                 NGX_ESCAPE_ARGS);
+            }
+        }
+
+        for (n = 1; n < (ngx_uint_t) rc; n++) {
+            e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n];
+        }
+
+    } else {
+        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+        le.ip = code->lengths->elts;
+        le.request = r;
+        le.captures = e->captures;
+
+        len = 1;  /* reserve 1 byte for possible "?" */
+
+        while (*(uintptr_t *) le.ip) {
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            len += lcode(&le);
+        }
+
+        e->buf.len = len;
+    }
+
+    if (code->args && code->add_args && r->args.len) {
+        e->buf.len += r->args.len + 1;
+    }
+
+    e->buf.data = ngx_palloc(r->pool, e->buf.len);
+    if (e->buf.data == NULL) {
+        e->ip = ngx_http_script_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
         return;
     }
 
-    e->lite.pos = ngx_cpymem(e->lite.pos, value->text.data, value->text.len);
+    e->quote = code->redirect;
+
+    e->pos = e->buf.data;
+
+    e->ip += sizeof(ngx_http_script_regex_code_t);
+}
+
+
+void
+ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_request_t                *r;
+    ngx_http_script_regex_end_code_t  *code;
+
+    code = (ngx_http_script_regex_end_code_t *) e->ip;
+
+    r = e->request;
+
+    e->quote = 0;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http script regex end");
+
+    if (code->redirect) {
+
+        if (code->add_args && r->args.len) { 
+            *e->pos++ = (u_char) (code->args ? '&' : '?');
+            e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len);
+        }
+
+        e->buf.len = e->pos - e->buf.data;
+
+        if (e->log) {
+            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+                          "rewritten redirect: \"%V\"", &e->buf);
+        }
+
+        r->headers_out.location = ngx_list_push(&r->headers_out.headers);
+        if (r->headers_out.location == NULL) {
+            e->ip = ngx_http_script_exit;
+            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return;
+        }
+
+        r->headers_out.location->hash = 1; 
+        r->headers_out.location->key.len = sizeof("Location") - 1;
+        r->headers_out.location->key.data = (u_char *) "Location";
+        r->headers_out.location->value = e->buf;
+
+        e->ip += sizeof(ngx_http_script_regex_end_code_t);
+        return;
+    }
+
+    if (e->args) { 
+        e->buf.len = e->args - e->buf.data;
+
+        if (code->add_args && r->args.len) {
+            *e->pos++ = '&';
+            e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len);
+        }
+
+        r->args.len = e->pos - e->args;
+        r->args.data = e->args;
+
+        e->args = NULL;
+
+    } else {
+        e->buf.len = e->pos - e->buf.data;
+    }
+
+    if (e->log) {
+        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+                      "rewritten data: \"%V\", args: \"%V\"",
+                      &e->buf, &r->args);
+    }
+
+    if (code->uri) {
+        r->uri = e->buf;
+
+        if (ngx_http_set_exten(r) != NGX_OK) {
+            e->ip = ngx_http_script_exit;
+            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return;
+        }
+    }
+
+    e->ip += sizeof(ngx_http_script_regex_end_code_t);
+}
+
+
+void
+ngx_http_script_return_code(ngx_http_script_engine_t *e)
+{   
+    ngx_http_script_return_code_t  *code;
+    
+    code = (ngx_http_script_return_code_t *) e->ip;
+
+    e->status = code->status;
+
+    e->ip += sizeof(ngx_http_script_return_code_t) - sizeof(uintptr_t);
 }
+
+
+void
+ngx_http_script_if_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_if_code_t  *code;
+
+    code = (ngx_http_script_if_code_t *) e->ip;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script if");
+
+    e->sp--;
+
+    if (e->sp->value) {
+        if (code->loc_conf) {
+            e->request->loc_conf = code->loc_conf;
+        }
+
+        e->ip += sizeof(ngx_http_script_if_code_t);
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script if false");
+
+    e->ip += code->next;
+}
+
+
+void
+ngx_http_script_value_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_script_value_code_t  *code;
+
+    code = (ngx_http_script_value_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_value_code_t);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script value");
+
+    e->sp->value = (ngx_uint_t) code->value;
+    e->sp->text.len = (size_t) code->text_len;
+    e->sp->text.data = (u_char *) code->text_data;
+    e->sp++;
+}
+
+
+void
+ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_request_t          *r;
+    ngx_http_variable_value_t   *value;
+    ngx_http_core_main_conf_t   *cmcf;
+    ngx_http_script_var_code_t  *code;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script set var");
+
+    code = (ngx_http_script_var_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_var_code_t);
+
+    r = e->request;
+
+    if (r->variables == NULL) {
+        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+        r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
+                                        * sizeof(ngx_http_variable_value_t *));
+        if (r->variables == NULL) {
+            e->ip = ngx_http_script_exit;
+            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            return;
+        }
+    }
+
+    value = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+    if (value == NULL) {
+        e->ip = ngx_http_script_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    e->sp--;
+
+    *value = *e->sp;
+
+    r->variables[code->index] = value;
+}
+
+
+void
+ngx_http_script_var_code(ngx_http_script_engine_t *e)
+{
+    ngx_http_variable_value_t   *value;
+    ngx_http_script_var_code_t  *code;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script var");
+
+    code = (ngx_http_script_var_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_script_var_code_t);
+
+    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 *) "";
+        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++;
+}
+
+
+void
+ngx_http_script_nop_code(ngx_http_script_engine_t *e)
+{
+    e->ip += sizeof(uintptr_t);
+}