diff src/http/modules/ngx_http_rewrite_module.c @ 50:72eb30262aac NGINX_0_1_25

nginx 0.1.25 *) Bugfix: nginx did run on Linux parisc. *) Feature: nginx now does not start under FreeBSD if the sysctl kern.ipc.somaxconn value is too big. *) Bugfix: if a request was internally redirected by the ngx_http_index_module module to the ngx_http_proxy_module or ngx_http_fastcgi_module modules, then the index file was not closed after request completion. *) Feature: the "proxy_pass" can be used in location with regular expression. *) Feature: the ngx_http_rewrite_filter_module module supports the condition like "if ($HTTP_USER_AGENT ~ MSIE)". *) Bugfix: nginx started too slow if the large number of addresses and text values were used in the "geo" directive. *) Change: a variable name must be declared as "$name" in the "geo" directive. The previous variant without "$" is still supported, but will be removed soon. *) Feature: the "%{VARIABLE}v" logging parameter. *) Feature: the "set $name value" directive. *) Bugfix: gcc 4.0 compatibility. *) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author Igor Sysoev <http://sysoev.ru>
date Sat, 19 Mar 2005 00:00:00 +0300
parents
children bcb5fce0b038
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -0,0 +1,1689 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct ngx_http_rewrite_engine_s  ngx_http_rewrite_engine_t;
+
+typedef void (*ngx_http_rewrite_code_pt) (ngx_http_rewrite_engine_t *e);
+
+
+typedef struct {
+    ngx_str_t                     name;
+    ngx_uint_t                    wildcard;
+} ngx_http_rewrite_referer_t;
+
+
+typedef struct {
+    ngx_str_t                    *name;
+    ngx_http_variable_value_t    *value;
+} ngx_http_rewrite_variable_t;
+
+
+typedef struct {
+    ngx_array_t                  *codes;        /* uintptr_t */
+    ngx_array_t                  *referers;     /* ngx_http_rewrite_referer_t */
+
+    ngx_uint_t                    max_captures;
+    ngx_uint_t                    stack_size;
+
+    ngx_flag_t                    log;
+
+    ngx_flag_t                    no_referer;
+} ngx_http_rewrite_loc_conf_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    ngx_regex_t                  *regex;
+    uintptr_t                     size;
+    uintptr_t                     ncaptures;
+    uintptr_t                     status;
+    uintptr_t                     next;
+
+    uintptr_t                     test:1;
+    uintptr_t                     uri:1;
+
+    /* add the r->args to the new arguments */
+    uintptr_t                     args:1;
+
+    uintptr_t                     redirect:1;
+    uintptr_t                     break_cycle:1;
+
+    ngx_str_t                     name;
+} ngx_http_rewrite_regex_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+
+    uintptr_t                     uri:1;
+
+    /* add the r->args to the new arguments */
+    uintptr_t                     args:1;
+
+    uintptr_t                     redirect:1;
+} ngx_http_rewrite_regex_end_code_t;
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     n;
+} ngx_http_rewrite_copy_capture_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     len;
+} ngx_http_rewrite_copy_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     status;
+    uintptr_t                     null;
+} ngx_http_rewrite_return_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     next;
+    void                        **loc_conf;
+} ngx_http_rewrite_if_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     value;
+    uintptr_t                     text_len;
+    uintptr_t                     text_data;
+} ngx_http_rewrite_value_code_t;
+
+
+typedef struct {
+    ngx_http_rewrite_code_pt      code;
+    uintptr_t                     index;
+} ngx_http_rewrite_var_code_t;
+
+
+struct ngx_http_rewrite_engine_s {
+    u_char                       *ip;
+    ngx_http_variable_value_t    *sp;
+
+    ngx_str_t                     buf;
+    ngx_str_t                    *line;
+
+    u_char                       *pos;
+
+    /* the start of the rewritten arguments */
+    u_char                       *args;
+
+    unsigned                      quote:1;
+
+    ngx_int_t                     status;
+
+    int                          *captures;
+
+    ngx_http_request_t           *request;
+    ngx_http_rewrite_loc_conf_t  *conf;
+};
+
+
+static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle);
+static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,
+    void *parent, void *child);
+static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,
+    ngx_http_rewrite_loc_conf_t *lcf);
+static char *ngx_http_rewrite_variable(ngx_conf_t *cf,
+    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
+static char *ngx_http_rewrite_valid_referers(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+static char * ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static void *ngx_http_rewrite_start_code(ngx_pool_t *pool,
+    ngx_array_t **codes, size_t size);
+static void *ngx_http_rewrite_add_code(ngx_array_t *codes, size_t size,
+    void *code);
+
+
+static ngx_command_t  ngx_http_rewrite_commands[] = {
+
+    { ngx_string("rewrite"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                       |NGX_CONF_TAKE23,
+      ngx_http_rewrite,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("return"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                       |NGX_CONF_TAKE1,
+      ngx_http_rewrite_return,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("if"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
+      ngx_http_rewrite_if,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("valid_referers"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_http_rewrite_valid_referers,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("set"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                       |NGX_CONF_TAKE2,
+      ngx_http_rewrite_set,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("rewrite_log"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                       |NGX_CONF_TAKE1,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_rewrite_loc_conf_t, log),
+      NULL },
+
+      ngx_null_command
+};
+
+
+ngx_http_module_t  ngx_http_rewrite_module_ctx = {
+    NULL,                                  /* pre conf */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_rewrite_create_loc_conf,      /* create location configration */
+    ngx_http_rewrite_merge_loc_conf        /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_rewrite_module = {
+    NGX_MODULE,
+    &ngx_http_rewrite_module_ctx,          /* module context */ 
+    ngx_http_rewrite_commands,             /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    ngx_http_rewrite_init,                 /* init module */
+    NULL                                   /* init process */
+};
+
+
+#define ngx_http_rewrite_exit  (u_char *) &ngx_http_rewrite_exit_code
+
+uintptr_t ngx_http_rewrite_exit_code = (uintptr_t) NULL;
+
+static ngx_http_variable_value_t  ngx_http_rewrite_null_value =
+                                                        { 0, ngx_string("") };
+
+
+static ngx_int_t
+ngx_http_rewrite_handler(ngx_http_request_t *r)
+{
+    ngx_http_rewrite_code_pt      code;
+    ngx_http_rewrite_engine_t    *e;
+    ngx_http_rewrite_loc_conf_t  *cf;
+
+    cf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
+
+    if (cf->codes == NULL) {
+        return NGX_DECLINED;
+    }
+
+    e = ngx_palloc(r->pool, sizeof(ngx_http_rewrite_engine_t));
+    if (e == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    e->sp = ngx_palloc(r->pool,
+                       cf->stack_size * sizeof(ngx_http_variable_value_t));
+    if (e->sp == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (cf->max_captures) {
+        e->captures = ngx_palloc(r->pool, cf->max_captures * sizeof(int));
+        if (e->captures == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+    } else {
+        e->captures = NULL;
+    }
+
+    e->ip = cf->codes->elts;
+    e->buf.len = 0;
+    e->buf.data = NULL;
+    e->line = NULL;
+    e->pos = NULL;
+    e->args = NULL;
+    e->quote = 1;
+    e->status = NGX_DECLINED;
+    e->request = r;
+    e->conf = cf;
+
+    while (*(uintptr_t *) e->ip) {
+        code = *(ngx_http_rewrite_code_pt *) e->ip;
+        code(e);
+    }
+
+    return e->status;
+}
+
+
+static void
+ngx_http_rewrite_regex_start_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_int_t                       rc;
+    ngx_uint_t                      n;
+    ngx_http_request_t             *r;
+    ngx_http_rewrite_regex_code_t  *code;
+
+    code = (ngx_http_rewrite_regex_code_t *) e->ip;
+
+    r = e->request;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http rewrite regex: \"%V\"", &code->name);
+
+    if (code->uri) {
+        e->line = &r->uri;
+    } else {
+        e->sp--;
+        e->line = &e->sp->text;
+    }
+
+    rc = ngx_regex_exec(code->regex, e->line, e->captures, code->ncaptures);
+
+    if (rc == NGX_REGEX_NO_MATCHED) {
+        if (e->conf->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_rewrite_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_rewrite_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    if (e->conf->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_rewrite_regex_code_t);
+        return;
+    }
+
+    if (code->status) {
+        e->status = code->status;
+
+        if (!code->redirect) {
+            e->ip = ngx_http_rewrite_exit;
+            return;
+        }
+    }
+
+    e->buf.len = code->size;
+
+    if (code->uri) {
+        if (!code->break_cycle) {
+            r->uri_changed = 1;
+        }
+
+        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];
+    }
+
+    if (code->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_rewrite_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    e->quote = code->redirect;
+
+    e->pos = e->buf.data;
+
+    e->ip += sizeof(ngx_http_rewrite_regex_code_t);
+}
+
+
+static void
+ngx_http_rewrite_regex_end_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_request_t                 *r;
+    ngx_http_rewrite_regex_end_code_t  *code;
+
+    code = (ngx_http_rewrite_regex_end_code_t *) e->ip;
+
+    r = e->request;
+
+    e->quote = 0;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http rewrite regex end");
+
+    if (e->args) {
+        e->buf.len = e->args - e->buf.data;
+
+        if (code->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 {
+        if (code->args && r->args.len) {
+            *e->pos++ = '?';
+            e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len);
+        }
+
+        e->buf.len = e->pos - e->buf.data;
+    }
+
+    if (!code->redirect) {
+        if (e->conf->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_rewrite_exit;
+                e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+                return;
+            }
+        }
+
+        e->ip += sizeof(ngx_http_rewrite_regex_end_code_t);
+        return;
+    }
+
+    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_rewrite_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    if (e->buf.data[0] != '/') {
+        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_rewrite_regex_end_code_t);
+}
+
+
+static void
+ngx_http_rewrite_copy_capture_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_rewrite_copy_capture_code_t  *code;
+
+    code = (ngx_http_rewrite_copy_capture_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_rewrite_copy_capture_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 rewrite capture: \"%V\"", &e->buf);
+}
+
+
+static void
+ngx_http_rewrite_copy_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_rewrite_copy_code_t  *code;
+
+    code = (ngx_http_rewrite_copy_code_t *) e->ip;
+
+    e->pos = ngx_cpymem(e->pos, e->ip + sizeof(ngx_http_rewrite_copy_code_t),
+                        code->len);
+
+    e->ip += sizeof(ngx_http_rewrite_copy_code_t)
+          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite copy: \"%V\"", &e->buf);
+}
+
+
+static void
+ngx_http_rewrite_start_args_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite args");
+
+    e->args = e->pos;
+    e->ip += sizeof(uintptr_t);
+}
+
+
+static void
+ngx_http_rewrite_return_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_rewrite_return_code_t  *code;
+
+    code = (ngx_http_rewrite_return_code_t *) e->ip;
+
+    e->status = code->status;
+
+    e->ip += sizeof(ngx_http_rewrite_return_code_t) - sizeof(uintptr_t);
+}
+
+
+static void
+ngx_http_rewrite_if_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_rewrite_if_code_t  *code;
+
+    code = (ngx_http_rewrite_if_code_t *) e->ip;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite if");
+
+    e->sp--;
+
+    if (e->sp->value) {
+        if (code->loc_conf) {
+            e->request->loc_conf = code->loc_conf;
+        }
+
+        e->ip += sizeof(ngx_http_rewrite_if_code_t);
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite if false");
+
+    e->ip += code->next;
+}
+
+
+static void
+ngx_http_rewrite_value_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_rewrite_value_code_t  *code;
+
+    code = (ngx_http_rewrite_value_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_rewrite_value_code_t);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite 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++;
+}
+
+
+static void
+ngx_http_rewrite_set_var_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_request_t           *r;
+    ngx_http_variable_value_t    *value;
+    ngx_http_core_main_conf_t    *cmcf;
+    ngx_http_rewrite_var_code_t  *code;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite set var");
+
+    code = (ngx_http_rewrite_var_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_rewrite_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_rewrite_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_rewrite_exit;
+        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        return;
+    }
+
+    e->sp--;
+
+    *value = *e->sp;
+
+    r->variables[code->index] = value;
+}
+
+
+static void
+ngx_http_rewrite_var_code(ngx_http_rewrite_engine_t *e)
+{
+    ngx_http_variable_value_t    *value;
+    ngx_http_rewrite_var_code_t  *code;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http rewrite var");
+
+    code = (ngx_http_rewrite_var_code_t *) e->ip;
+
+    e->ip += sizeof(ngx_http_rewrite_var_code_t);
+
+    value = ngx_http_get_indexed_variable(e->request, code->index);
+
+    if (value == NULL || value == NGX_HTTP_VARIABLE_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 rewrite var: %ui, \"%V\"", value->value, &value->text);
+
+    *e->sp = *value;
+    e->sp++;
+}
+
+
+static void
+ngx_http_rewrite_invalid_referer_code(ngx_http_rewrite_engine_t *e)
+{
+    u_char                       *ref;
+    size_t                        len;
+    ngx_uint_t                    i, n;
+    ngx_http_request_t           *r;
+    ngx_http_rewrite_referer_t   *refs;
+    ngx_http_rewrite_loc_conf_t  *cf;
+
+    r = e->request;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http rewrite invalid referer");
+
+    cf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
+
+    e->ip += sizeof(uintptr_t);
+
+    if (cf->referers == NULL) {
+        e->sp->value = 0;
+        e->sp->text.len = 0;
+        e->sp->text.data = (u_char *) "";
+        e->sp++;
+
+        return;
+    }
+
+    if (r->headers_in.referer == NULL) {
+        if (cf->no_referer) {
+            e->sp->value = 0;
+            e->sp->text.len = 0;
+            e->sp->text.data = (u_char *) "";
+            e->sp++;
+
+            return;
+        } else {
+            e->sp->value = 1;
+            e->sp->text.len = 1;
+            e->sp->text.data = (u_char *) "1";
+            e->sp++;
+
+            return;
+        }
+    }
+
+    len = r->headers_in.referer->value.len;
+    ref = r->headers_in.referer->value.data;
+
+    if (len < sizeof("http://i.ru") - 1
+        || (ngx_strncasecmp(ref, "http://", 7) != 0))
+    {
+        e->sp->value = 1;
+        e->sp->text.len = 1;
+        e->sp->text.data = (u_char *) "1";
+        e->sp++;
+
+        return;
+    }
+
+    len -= 7;
+    ref += 7;
+
+    refs = cf->referers->elts;
+    for (i = 0; i < cf->referers->nelts; i++ ){
+
+        if (refs[i].name.len > len) {
+            continue;
+        }
+
+        if (refs[i].wildcard) {
+            for (n = 0; n < len; n++) {
+                if (ref[n] == '/' || ref[n] == ':') {
+                    break;
+                }
+
+                if (ref[n] != '.') {
+                    continue;
+                }
+
+                if (ngx_strncmp(&ref[n], refs[i].name.data,
+                                refs[i].name.len) == 0)
+                {
+                    e->sp->value = 0;
+                    e->sp->text.len = 0;
+                    e->sp->text.data = (u_char *) "";
+                    e->sp++;
+
+                    return;
+                }
+            }
+
+        } else {
+            if (ngx_strncasecmp(refs[i].name.data, ref, refs[i].name.len) == 0)
+            {
+                e->sp->value = 0;
+                e->sp->text.len = 0;
+                e->sp->text.data = (u_char *) "";
+                e->sp++;
+
+                return;
+            }
+        }
+    }
+
+    e->sp->value = 1;
+    e->sp->text.len = 1;
+    e->sp->text.data = (u_char *) "1";
+    e->sp++;
+}
+
+
+static void
+ngx_http_rewrite_nop_code(ngx_http_rewrite_engine_t *e)
+{
+    e->ip += sizeof(uintptr_t);
+}
+
+
+static ngx_http_variable_value_t *
+ngx_http_rewrite_var(ngx_http_request_t *r, uintptr_t data)
+{
+    ngx_http_variable_t        *var;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+    var = cmcf->variables.elts;
+
+    /*
+     * the ngx_http_rewrite_module sets variables directly in r->variables,
+     * and they should be handle by ngx_http_get_indexed_variable(),
+     * so the handler is called only if the variable is not initialized
+     */
+
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  "using uninitialized \"%V\" variable", &var[data].name);
+
+    return &ngx_http_rewrite_null_value;
+}
+
+
+static ngx_int_t
+ngx_http_rewrite_init(ngx_cycle_t *cycle)
+{   
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+    
+    cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+    
+    *h = ngx_http_rewrite_handler;
+    
+    return NGX_OK;
+}   
+
+
+static void *
+ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)
+{
+    ngx_http_rewrite_loc_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->stack_size = NGX_CONF_UNSET_UINT;
+    conf->log = NGX_CONF_UNSET;
+    conf->no_referer = NGX_CONF_UNSET;
+
+    return conf;
+}
+
+
+static char *
+ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_rewrite_loc_conf_t *prev = parent;
+    ngx_http_rewrite_loc_conf_t *conf = child;
+
+    uintptr_t                      *code, *last;
+    ngx_http_rewrite_regex_code_t  *regex;
+
+    ngx_conf_merge_value(conf->log, prev->log, 0);
+    ngx_conf_merge_unsigned_value(conf->stack_size, prev->stack_size, 10);
+
+    if (conf->referers == NULL) {
+        conf->referers = prev->referers;
+        ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
+    }
+
+    if (conf->no_referer == NGX_CONF_UNSET) {
+        conf->no_referer = 0;
+    }
+
+    if (conf->codes == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    if (conf->codes == prev->codes) {
+        return NGX_CONF_OK;
+    }
+
+    code = conf->codes->elts;
+    last = (uintptr_t *) ((u_char *) code + conf->codes->nelts);
+
+    while (code < last) {
+        if (*code == (uintptr_t) NULL) {
+            return NGX_CONF_OK;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_regex_start_code) {
+            regex = (ngx_http_rewrite_regex_code_t *) code;
+            if (conf->max_captures < regex->ncaptures) {
+                conf->max_captures = regex->ncaptures;
+            }
+            code = (uintptr_t *) ((u_char *) code + regex->next);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_if_code) {
+            code += sizeof(ngx_http_rewrite_if_code_t) / sizeof(uintptr_t);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_return_code) {
+            code += sizeof(ngx_http_rewrite_return_code_t) / sizeof(uintptr_t);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_set_var_code) {
+            code += sizeof(ngx_http_rewrite_var_code_t) / sizeof(uintptr_t);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_var_code) {
+            code += sizeof(ngx_http_rewrite_var_code_t) / sizeof(uintptr_t);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_value_code) {
+            code += sizeof(ngx_http_rewrite_value_code_t) / sizeof(uintptr_t);
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_invalid_referer_code) {
+            code++;
+            continue;
+        }
+
+        if (*code == (uintptr_t) &ngx_http_rewrite_nop_code) {
+            code++;
+            continue;
+        }
+
+#if (NGX_DEBUG)
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "unknown rewrite code: %p", *code);
+        return NGX_CONF_ERROR;
+#endif
+    }
+
+    code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));
+    if (code == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    *code = (uintptr_t) NULL;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_rewrite_loc_conf_t *lcf = conf;
+    
+    u_char                                *data;
+    size_t                                 len, size;
+    ngx_str_t                             *value, err;
+    ngx_int_t                              n;
+    ngx_uint_t                             i, last;
+    ngx_http_rewrite_code_pt              *code;
+    ngx_http_rewrite_copy_code_t          *copy;
+    ngx_http_rewrite_regex_code_t         *regex;
+    ngx_http_rewrite_regex_end_code_t     *regex_end;
+    ngx_http_rewrite_copy_capture_code_t  *copy_capture;
+    u_char                                 errstr[NGX_MAX_CONF_ERRSTR];
+
+    regex = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                        sizeof(ngx_http_rewrite_regex_code_t));
+    if (regex == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    /* TODO: NGX_REGEX_CASELESS */
+
+    regex->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
+
+    if (regex->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_CONF_ERROR;
+    }
+
+    regex->code = ngx_http_rewrite_regex_start_code;
+    regex->size = 0;
+    regex->ncaptures = 0;
+    regex->status = 0;
+    regex->test = 0;
+    regex->uri = 1;
+    regex->args = 1;
+    regex->redirect = 0;
+    regex->name = value[1];
+
+    last = 0;
+
+    if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0) {
+        regex->status = NGX_HTTP_MOVED_TEMPORARILY;
+        regex->redirect = 1;
+        last = 1;
+    }
+
+    if (cf->args->nelts == 4) {
+        if (ngx_strcmp(value[3].data, "last") == 0) {
+            last = 1;
+
+        } else if (ngx_strcmp(value[3].data, "break") == 0) {
+            regex->break_cycle = 1;
+            last = 1;
+
+        } else if (ngx_strcmp(value[3].data, "redirect") == 0) {
+            regex->status = NGX_HTTP_MOVED_TEMPORARILY;
+            regex->redirect = 1;
+            last = 1;
+
+        } else if (ngx_strcmp(value[3].data, "permanent") == 0) {
+            regex->status = NGX_HTTP_MOVED_PERMANENTLY;
+            regex->redirect = 1;
+            last = 1;
+
+        } else {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[3]);
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    i = 0;
+
+    while (i < value[2].len) {
+
+        data = &value[2].data[i];
+
+        if (value[2].data[i] == '$' && i < value[2].len
+            && value[2].data[i + 1] >= '1' && value[2].data[i + 1] <= '9')
+        {
+
+            /* the "$1" - "$9" captures */
+
+            copy_capture = ngx_http_rewrite_add_code(lcf->codes,
+                                  sizeof(ngx_http_rewrite_copy_capture_code_t),
+                                  &regex);
+            if (copy_capture == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            i++;
+
+            copy_capture->code = ngx_http_rewrite_copy_capture_code;
+            copy_capture->n = value[2].data[i] - '0';
+
+            if (regex->ncaptures < copy_capture->n) {
+                regex->ncaptures = copy_capture->n;
+            }
+
+            copy_capture->n *= 2;
+
+            i++;
+
+            continue;
+        }
+
+        if (value[2].data[i] == '?') {
+
+            /* the arguments */
+
+            if (i == value[2].len - 1) {
+                /* the last "?" drops the original arguments */
+                regex->args = 0;
+                break;
+            }
+
+            if (!regex->redirect) {
+                code = ngx_http_rewrite_add_code(lcf->codes, sizeof(uintptr_t),
+                                                 &regex);
+                if (code == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                *code = ngx_http_rewrite_start_args_code;
+
+                i++;
+
+                continue;
+            }
+        }
+
+        i++;
+
+        /* the substituion strings */
+
+        while (i < value[2].len && value[2].data[i] != '$') {
+
+            if (value[2].data[i] == '?') {
+
+                if (i == value[2].len - 1) {
+                    /*
+                     * the last "?" drops the original arguments,
+                     * and it should not be copied to a substituion
+                     */
+                    regex->args = 0;
+                    break;
+                }
+
+                if (!regex->redirect) {
+                    break;
+                }
+            }
+
+            i++;
+        }
+
+        len = &value[2].data[i] - data;
+
+        if (len == 0) {
+            continue;
+        }
+
+        regex->size += len;
+
+        size = (len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
+
+        copy = ngx_http_rewrite_add_code(lcf->codes,
+                                   sizeof(ngx_http_rewrite_copy_code_t) + size,
+                                   &regex);
+        if (copy == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        copy->code = ngx_http_rewrite_copy_code;
+        copy->len = len;
+
+        ngx_memcpy((u_char *) copy + sizeof(ngx_http_rewrite_copy_code_t),
+                   data, len);
+    }
+
+    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_rewrite_add_code(lcf->codes,
+                                     sizeof(ngx_http_rewrite_regex_end_code_t),
+                                     &regex);
+    if (regex_end == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    regex_end->code = ngx_http_rewrite_regex_end_code;
+    regex_end->uri = regex->uri;
+    regex_end->args = regex->args;
+    regex_end->redirect = regex->redirect;
+
+    if (last) {
+        code = ngx_http_rewrite_add_code(lcf->codes, sizeof(uintptr_t),
+                                         &regex);
+        if (code == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *code = (uintptr_t) NULL;
+    }
+
+    regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
+                                              - (u_char *) regex;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+    ngx_str_t                       *value;
+    ngx_http_rewrite_return_code_t  *ret;
+
+    ret = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                      sizeof(ngx_http_rewrite_return_code_t));
+    if (ret == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    ret->code = ngx_http_rewrite_return_code;
+    ret->null = (uintptr_t) NULL;
+
+    ret->status = ngx_atoi(value[1].data, value[1].len);
+
+    if (ret->status == (uintptr_t) NGX_ERROR) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+    void                         *mconf;
+    char                         *rv;
+    u_char                       *elts;
+    ngx_uint_t                    i;
+    ngx_conf_t                    save;
+    ngx_http_module_t            *module;
+    ngx_http_conf_ctx_t          *ctx, *pctx;
+    ngx_http_core_loc_conf_t     *clcf, *pclcf, **clcfp;
+    ngx_http_rewrite_if_code_t   *if_code;
+    ngx_http_rewrite_loc_conf_t  *nlcf;
+
+    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+    if (ctx == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    pctx = cf->ctx;
+    ctx->main_conf = pctx->main_conf;
+    ctx->srv_conf = pctx->srv_conf; 
+
+    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+    if (ctx->loc_conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
+            continue;
+        }
+
+        module = ngx_modules[i]->ctx;
+    
+        if (module->create_loc_conf) {
+
+            mconf = module->create_loc_conf(cf);
+            if (mconf == NULL) {
+                 return NGX_CONF_ERROR;
+            }
+
+            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
+        }
+    }
+
+    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
+
+    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
+    clcf->loc_conf = ctx->loc_conf;
+    clcf->name = pclcf->name;
+    clcf->noname = 1;
+
+    if (pclcf->locations.elts == NULL) {
+        if (ngx_array_init(&pclcf->locations, cf->pool, 4, sizeof(void *))
+                                                                  == NGX_ERROR)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    clcfp = ngx_array_push(&pclcf->locations);
+    if (clcfp == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    *clcfp = clcf;
+
+
+    if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_rewrite_if_code_t));
+    if (if_code == NULL) {
+        return NULL;
+    }
+
+    if_code->code = ngx_http_rewrite_if_code;
+
+    elts = lcf->codes->elts;
+
+
+    /* the inside directives must compile to the same code array */
+
+    nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];
+    nlcf->codes = lcf->codes;
+
+
+    save = *cf;
+    cf->ctx = ctx;
+
+    if (pclcf->name.len == 0) {
+        if_code->loc_conf = NULL;
+        cf->cmd_type = NGX_HTTP_SIF_CONF;
+
+    } else {
+        if_code->loc_conf = ctx->loc_conf;
+        cf->cmd_type = NGX_HTTP_LIF_CONF;
+    }
+
+    rv = ngx_conf_parse(cf, NULL);
+
+    *cf = save;
+
+    if (rv != NGX_CONF_OK) {
+        return rv;
+    }
+
+
+    if (elts != lcf->codes->elts) {
+        if_code = (ngx_http_rewrite_if_code_t *)
+                   ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
+    }
+
+    if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
+                                                - (u_char *) if_code;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)
+{
+    ngx_str_t                      *value, err;
+    ngx_uint_t                      cur, last;
+    ngx_http_rewrite_regex_code_t  *regex;
+    u_char                          errstr[NGX_MAX_CONF_ERRSTR];
+
+    value = cf->args->elts;
+    last = cf->args->nelts - 1;
+
+    if (value[1].len < 1 || value[1].data[0] != '(') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid condition \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (value[1].len == 1) {
+        cur = 2;
+
+    } else {
+        cur = 1;
+        value[1].len--;
+        value[1].data++;
+    }
+
+    if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid condition \"%V\"", &value[last]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (value[last].len == 1) {
+        last--;
+
+    } else {
+        value[last].len--;
+        value[last].data[value[last].len] = '\0';
+    }
+
+    if (value[cur].len > 1 && value[cur].data[0] == '$') {
+
+        if (cur != last && cur + 2 != last) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid condition \"%V\"", &value[cur]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_http_rewrite_variable(cf, lcf, &value[cur])!= NGX_CONF_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (cur == last) {
+            return NGX_CONF_OK;
+        }
+
+        cur++;
+
+        if ((value[cur].len == 1 && value[cur].data[0] != '~')
+            || (value[cur].len == 2
+                && value[cur].data[0] != '~' && value[cur].data[1] != '*'))
+        {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unexpected \"%V\" in condition", &value[cur]);
+            return NGX_CONF_ERROR;
+        }
+
+        regex = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                        sizeof(ngx_http_rewrite_regex_code_t));
+        if (regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        err.len = NGX_MAX_CONF_ERRSTR;
+        err.data = errstr;
+
+        regex->regex = ngx_regex_compile(&value[last],
+                                (value[cur].len == 2) ? NGX_REGEX_CASELESS : 0,
+                                cf->pool, &err);
+
+        if (regex->regex == NULL) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+            return NGX_CONF_ERROR;
+        }
+
+        regex->code = ngx_http_rewrite_regex_start_code;
+        regex->size = 0;
+        regex->ncaptures = 0;
+        regex->status = 0;
+        regex->next = sizeof(ngx_http_rewrite_regex_code_t);
+        regex->test = 1;
+        regex->uri = 0;
+        regex->args = 0;
+        regex->redirect = 0;
+        regex->name = value[last];
+
+        return NGX_CONF_OK;
+    }
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid condition \"%V\"", &value[cur]);
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
+    ngx_str_t *value)
+{
+    ngx_http_variable_t          *var;
+    ngx_http_rewrite_code_pt     *code;
+    ngx_http_rewrite_var_code_t  *var_code;
+
+    value->len--;
+    value->data++;
+
+    if (value->len == sizeof("invalid_referer") - 1
+        && ngx_strncmp(value->data, "invalid_referer",
+                       sizeof("invalid_referer") - 1) == 0)
+    {
+        code = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                           sizeof(ngx_http_rewrite_code_pt));
+        if (code == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *code = ngx_http_rewrite_invalid_referer_code;
+
+    } else {
+        var = ngx_http_add_variable(cf, value, 0);
+
+        if (var == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        var_code = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                           sizeof(ngx_http_rewrite_var_code_t));
+        if (var_code == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        var_code->code = ngx_http_rewrite_var_code;
+        var_code->index = var->index;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+    ngx_uint_t                   i, server_names;
+    ngx_str_t                   *value;
+    ngx_http_server_name_t      *sn;
+    ngx_http_core_srv_conf_t    *cscf;
+    ngx_http_rewrite_referer_t  *ref;
+
+    cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
+
+    if (lcf->referers == NULL) {
+        lcf->referers = ngx_array_create(cf->pool,
+                                    cf->args->nelts + cscf->server_names.nelts,
+                                    sizeof(ngx_http_rewrite_referer_t));
+        if (lcf->referers == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    value = cf->args->elts;
+    server_names = 0;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+        if (value[i].len == 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid referer \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_strcmp(value[i].data, "none") == 0) {
+            lcf->no_referer = 1;
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "server_names") == 0) {
+            server_names = 1;
+            continue;
+        }
+
+        ref = ngx_array_push(lcf->referers);
+        if (ref == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (value[i].data[0] != '*') {
+            ref->name = value[i];
+            ref->wildcard = 0;
+            continue;
+        }
+
+        if (value[i].data[1] != '.') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid wildcard referer \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        ref->name.len = value[i].len - 1;
+        ref->name.data = value[i].data + 1;
+        ref->wildcard = 1;
+    }
+
+    if (!server_names) {
+        return NGX_CONF_OK;
+    }
+
+    sn = cscf->server_names.elts;
+    for (i = 0; i < cscf->server_names.nelts; i++) {
+        ref = ngx_array_push(lcf->referers);
+        if (ref == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        ref->name.len = sn[i].name.len + 1;
+        ref->name.data = ngx_palloc(cf->pool, ref->name.len);
+        if (ref->name.data == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_memcpy(ref->name.data, sn[i].name.data, sn[i].name.len);
+        ref->name.data[sn[i].name.len] = '/';
+        ref->wildcard = sn[i].wildcard;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+    ngx_int_t                       n;
+    ngx_str_t                      *value;
+    ngx_http_variable_t            *v;
+    ngx_http_rewrite_var_code_t    *var;
+    ngx_http_rewrite_value_code_t  *val;
+
+    value = cf->args->elts;
+
+    if (value[1].data[0] != '$') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid variable name \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    value[1].len--;
+    value[1].data++;
+
+    v = ngx_http_add_variable(cf, &value[1], 1);
+    if (v == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    v->handler = ngx_http_rewrite_var;
+    v->data = v->index;
+
+    val = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                      sizeof(ngx_http_rewrite_value_code_t));
+    if (val == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    n = ngx_atoi(value[2].data, value[2].len);
+
+    if (n == NGX_ERROR) {
+        n = 0;
+    }
+
+    val->code = ngx_http_rewrite_value_code;
+    val->value = (uintptr_t) n;
+    val->text_len = (uintptr_t) value[2].len;
+    val->text_data = (uintptr_t) value[2].data;
+
+    var = ngx_http_rewrite_start_code(cf->pool, &lcf->codes,
+                                      sizeof(ngx_http_rewrite_var_code_t));
+    if (var == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    var->code = ngx_http_rewrite_set_var_code;
+    var->index = (uintptr_t) v->index;
+
+    return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_rewrite_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
+{
+    if (*codes == NULL) {
+        *codes = ngx_array_create(pool, 256, 1);
+        if (*codes == NULL) {
+            return NULL;
+        }
+    }
+
+    return ngx_array_push_n(*codes, size);
+}
+
+
+static void *
+ngx_http_rewrite_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 (elts != codes->elts) {
+        p = code;
+        *p += (u_char *) codes->elts - elts;
+    }
+
+    return new;
+}