changeset 582:c456a023113c NGINX_0_8_43

nginx 0.8.43 *) Feature: large geo ranges base loading speed-up. *) Bugfix: an error_page redirection to "location /zero { return 204; }" without changing status code kept the error body; the bug had appeared in 0.8.42. *) Bugfix: nginx might close IPv6 listen socket during reconfiguration. Thanks to Maxim Dounin. *) Bugfix: the $uid_set variable may be used at any request processing stage.
author Igor Sysoev <http://sysoev.ru>
date Wed, 30 Jun 2010 00:00:00 +0400
parents 22b2345b75d9
children 39e50617266a
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_cycle.c src/core/ngx_resolver.c src/core/ngx_string.c src/core/ngx_string.h src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_sub_filter_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_core_module.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_parse.c src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/http/ngx_http_variables.c src/http/ngx_http_variables.h src/mail/ngx_mail_handler.c src/os/unix/ngx_files.c src/os/unix/ngx_files.h src/os/unix/ngx_readv_chain.c src/os/unix/ngx_recv.c src/os/unix/ngx_udp_recv.c
diffstat 28 files changed, 843 insertions(+), 340 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,20 @@
 
+Changes with nginx 0.8.43                                        30 Jun 2010
+
+    *) Feature: large geo ranges base loading speed-up.
+
+    *) Bugfix: an error_page redirection to "location /zero { return 204; 
+       }" without changing status code kept the error body; the bug had 
+       appeared in 0.8.42.
+
+    *) Bugfix: nginx might close IPv6 listen socket during 
+       reconfiguration.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: the $uid_set variable may be used at any request processing 
+       stage.
+
+
 Changes with nginx 0.8.42                                        21 Jun 2010
 
     *) Change: now nginx tests locations given by regular expressions, if 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,20 @@
 
+Изменения в nginx 0.8.43                                          30.06.2010
+
+    *) Добавление: ускорение загрузки больших баз geo-диапазонов.
+
+    *) Исправление: перенаправление ошибки в "location /zero { return 204; 
+       }" без изменения кода ответа оставляло тело ошибки; ошибка появилась 
+       в 0.8.42.
+
+    *) Исправление: nginx мог закрывать IPv6 listen сокет во время 
+       переконфигурации.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: переменную $uid_set можно использовать на любой стадии 
+       обработки запроса.
+
+
 Изменения в nginx 0.8.42                                          21.06.2010
 
     *) Изменение: теперь nginx проверяет location'ы, заданные регулярными 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8042
-#define NGINX_VERSION      "0.8.42"
+#define nginx_version         8043
+#define NGINX_VERSION      "0.8.43"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -671,7 +671,8 @@ ngx_conf_read_token(ngx_conf_t *cf)
                 }
 
             } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
-                       || ch == ';' || ch == '{') {
+                       || ch == ';' || ch == '{')
+            {
                 last_space = 1;
                 found = 1;
             }
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -868,7 +868,7 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, s
         sin61 = (struct sockaddr_in6 *) sa1;
         sin62 = (struct sockaddr_in6 *) sa2;
 
-        if (sin61->sin6_port != sin61->sin6_port) {
+        if (sin61->sin6_port != sin62->sin6_port) {
             return NGX_DECLINED;
         }
 
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -1836,7 +1836,7 @@ ngx_resolver_create_addr_query(ngx_resol
 
     p += sizeof(ngx_resolver_query_t);
 
-    for (n = 0; n < 32; n += 8){
+    for (n = 0; n < 32; n += 8) {
         d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff);
         *p = (u_char) (d - &p[1]);
         p = d;
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -1638,6 +1638,89 @@ ngx_escape_html(u_char *dst, u_char *src
 }
 
 
+void
+ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_str_node_t      *n, *t;
+    ngx_rbtree_node_t  **p;
+
+    for ( ;; ) {
+
+        n = (ngx_str_node_t *) node;
+        t = (ngx_str_node_t *) temp;
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (n->str.len != t->str.len) {
+
+            p = (n->str.len < t->str.len) ? &temp->left : &temp->right;
+
+        } else {
+            p = (ngx_memcmp(n->str.data, t->str.data, n->str.len) < 0)
+                 ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+ngx_str_node_t *
+ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash)
+{
+    ngx_int_t           rc;
+    ngx_str_node_t     *n;
+    ngx_rbtree_node_t  *node, *sentinel;
+
+    node = rbtree->root;
+    sentinel = rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        n = (ngx_str_node_t *) node;
+
+        if (hash != node->key) {
+            node = (hash < node->key) ? node->left : node->right;
+            continue;
+        }
+
+        if (val->len != n->str.len) {
+            node = (val->len < n->str.len) ? node->left : node->right;
+            continue;
+        }
+
+        rc = ngx_memcmp(val->data, n->str.data, val->len);
+
+        if (rc < 0) {
+            node = node->left;
+            continue;
+        }
+
+        if (rc > 0) {
+            node = node->right;
+            continue;
+        }
+
+        return n;
+    }
+
+    return NULL;
+}
+
+
 /* ngx_sort() is implemented as insertion sort because we need stable sort */
 
 void
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -89,7 +89,7 @@ ngx_strlchr(u_char *p, u_char *last, u_c
 #if (NGX_MEMCPY_LIMIT)
 
 void *ngx_memcpy(void *dst, void *src, size_t n);
-#define ngx_cpymem(dst, src, n)   ((u_char *) ngx_memcpy(dst, src, n)) + (n)
+#define ngx_cpymem(dst, src, n)   (((u_char *) ngx_memcpy(dst, src, n)) + (n))
 
 #else
 
@@ -99,7 +99,7 @@ void *ngx_memcpy(void *dst, void *src, s
  * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves.
  */
 #define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)
-#define ngx_cpymem(dst, src, n)   ((u_char *) memcpy(dst, src, n)) + (n)
+#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))
 
 #endif
 
@@ -200,6 +200,17 @@ void ngx_unescape_uri(u_char **dst, u_ch
 uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);
 
 
+typedef struct {
+    ngx_rbtree_node_t         node;
+    ngx_str_t                 str;
+} ngx_str_node_t;
+
+
+void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+ngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name,
+    uint32_t hash);
+
 
 void ngx_sort(void *base, size_t n, size_t size,
     ngx_int_t (*cmp)(const void *, const void *));
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -10,41 +10,53 @@
 
 
 typedef struct {
+    ngx_http_variable_value_t       *value;
     u_short                          start;
     u_short                          end;
-    ngx_http_variable_value_t       *value;
 } ngx_http_geo_range_t;
 
 
 typedef struct {
-    ngx_http_geo_range_t            *ranges;
-    ngx_uint_t                       n;
-} ngx_http_geo_low_ranges_t;
-
-
-typedef struct {
-    ngx_http_geo_low_ranges_t        low[0x10000];
+    ngx_http_geo_range_t           **low;
     ngx_http_variable_value_t       *default_value;
 } ngx_http_geo_high_ranges_t;
 
 
 typedef struct {
+    ngx_str_node_t                   sn;
+    ngx_http_variable_value_t       *value;
+    size_t                           offset;
+} ngx_http_geo_variable_value_node_t;
+
+
+typedef struct {
     ngx_http_variable_value_t       *value;
     ngx_str_t                       *net;
-    ngx_http_geo_high_ranges_t      *high;
+    ngx_http_geo_high_ranges_t       high;
     ngx_radix_tree_t                *tree;
     ngx_rbtree_t                     rbtree;
     ngx_rbtree_node_t                sentinel;
     ngx_array_t                     *proxies;
     ngx_pool_t                      *pool;
     ngx_pool_t                      *temp_pool;
+
+    size_t                           data_size;
+
+    ngx_str_t                        include_name;
+    ngx_uint_t                       includes;
+    ngx_uint_t                       entries;
+
+    unsigned                         ranges:1;
+    unsigned                         outside_entries:1;
+    unsigned                         allow_binary_include:1;
+    unsigned                         binary_include:1;
 } ngx_http_geo_conf_ctx_t;
 
 
 typedef struct {
     union {
         ngx_radix_tree_t            *tree;
-        ngx_http_geo_high_ranges_t  *high;
+        ngx_http_geo_high_ranges_t   high;
     } u;
 
     ngx_array_t                     *proxies;
@@ -73,6 +85,13 @@ static char *ngx_http_geo_add_proxy(ngx_
     ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
     ngx_cidr_t *cidr);
+static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *name);
+static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
+static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
+static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 
 
 static ngx_command_t  ngx_http_geo_commands[] = {
@@ -119,6 +138,20 @@ ngx_module_t  ngx_http_geo_module = {
 };
 
 
+typedef struct {
+    u_char    GEORNG[6];
+    u_char    version;
+    u_char    ptr_size;
+    uint32_t  endianess;
+    uint32_t  crc32;
+} ngx_http_geo_header_t;
+
+
+static ngx_http_geo_header_t  ngx_http_geo_header = {
+    { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
+};
+
+
 /* AF_INET only */
 
 static ngx_int_t
@@ -148,23 +181,24 @@ ngx_http_geo_range_variable(ngx_http_req
     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
 
     in_addr_t              addr;
-    ngx_uint_t             i, n;
+    ngx_uint_t             n;
     ngx_http_geo_range_t  *range;
 
-    *v = *ctx->u.high->default_value;
+    *v = *ctx->u.high.default_value;
 
     addr = ngx_http_geo_addr(r, ctx);
 
-    range = ctx->u.high->low[addr >> 16].ranges;
-
-    n = addr & 0xffff;
+    range = ctx->u.high.low[addr >> 16];
 
-    for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
-        if (n >= (ngx_uint_t) range[i].start
-            && n <= (ngx_uint_t) range[i].end)
-        {
-            *v = *range[i].value;
-        }
+    if (range) {
+        n = addr & 0xffff;
+        do {
+            if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end)
+            {
+                *v = *range->value;
+                break;
+            }
+        } while ((++range)->value);
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -256,6 +290,7 @@ static char *
 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                     *rv;
+    void                    **p;
     size_t                    len;
     ngx_str_t                *value, name;
     ngx_uint_t                i;
@@ -302,18 +337,20 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
+    ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
+
     ctx.temp_pool = ngx_create_pool(16384, cf->log);
     if (ctx.temp_pool == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
-                    ngx_http_variable_value_rbtree_insert);
+    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
 
-    ctx.high = NULL;
-    ctx.tree = NULL;
-    ctx.proxies = NULL;
     ctx.pool = cf->pool;
+    ctx.data_size = sizeof(ngx_http_geo_header_t)
+                  + sizeof(ngx_http_variable_value_t)
+                  + 0x10000 * sizeof(ngx_http_geo_range_t *);
+    ctx.allow_binary_include = 1;
 
     save = *cf;
     cf->pool = pool;
@@ -327,25 +364,35 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     geo->proxies = ctx.proxies;
 
-    if (ctx.high) {
+    if (ctx.high.low) {
+
+        if (!ctx.binary_include) {
+            for (i = 0; i < 0x10000; i++) {
+                a = (ngx_array_t *) ctx.high.low[i];
+
+                if (a == NULL || a->nelts == 0) {
+                    continue;
+                }
 
-        for (i = 0; i < 0x10000; i++) {
-            a = (ngx_array_t *) ctx.high->low[i].ranges;
+                len = a->nelts * sizeof(ngx_http_geo_range_t);
 
-            if (a == NULL || a->nelts == 0) {
-                continue;
+                ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
+                if (ctx.high.low[i] == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len);
+                *p = NULL;
+                ctx.data_size += len + sizeof(void *);
             }
 
-            ctx.high->low[i].n = a->nelts;
-
-            len = a->nelts * sizeof(ngx_http_geo_range_t);
-
-            ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
-            if (ctx.high->low[i].ranges == NULL ){
-                return NGX_CONF_ERROR;
+            if (ctx.allow_binary_include
+                && !ctx.outside_entries
+                && ctx.entries > 100000
+                && ctx.includes == 1)
+            {
+                ngx_http_geo_create_binary_base(&ctx);
             }
-
-            ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
         }
 
         geo->u.high = ctx.high;
@@ -353,13 +400,13 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
         var->get_handler = ngx_http_geo_range_variable;
         var->data = (uintptr_t) geo;
 
+        if (ctx.high.default_value == NULL) {
+            ctx.high.default_value = &ngx_http_variable_null_value;
+        }
+
         ngx_destroy_pool(ctx.temp_pool);
         ngx_destroy_pool(pool);
 
-        if (ctx.high->default_value == NULL) {
-            ctx.high->default_value = &ngx_http_variable_null_value;
-        }
-
     } else {
         if (ctx.tree == NULL) {
             ctx.tree = ngx_radix_tree_create(cf->pool, -1);
@@ -396,7 +443,7 @@ static char *
 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
     char                     *rv;
-    ngx_str_t                *value, file;
+    ngx_str_t                *value;
     ngx_cidr_t                cidr;
     ngx_http_geo_conf_ctx_t  *ctx;
 
@@ -415,11 +462,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
                 goto failed;
             }
 
-            ctx->high = ngx_pcalloc(ctx->pool,
-                                    sizeof(ngx_http_geo_high_ranges_t));
-            if (ctx->high == NULL) {
-                goto failed;
-            }
+            ctx->ranges = 1;
 
             rv = NGX_CONF_OK;
 
@@ -435,20 +478,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
 
     if (ngx_strcmp(value[0].data, "include") == 0) {
 
-        file.len = value[1].len++;
-
-        file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
-        if (file.data == NULL) {
-            goto failed;
-        }
-
-        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
-            goto failed;
-        }
-
-        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
-
-        rv = ngx_conf_parse(cf, &file);
+        rv = ngx_http_geo_include(cf, ctx, &value[1]);
 
         goto done;
 
@@ -463,7 +493,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         goto done;
     }
 
-    if (ctx->high) {
+    if (ctx->ranges) {
         rv = ngx_http_geo_range(cf, ctx, value);
 
     } else {
@@ -488,30 +518,45 @@ static char *
 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
     ngx_str_t *value)
 {
-    u_char                     *p, *last;
-    in_addr_t                   start, end;
-    ngx_str_t                  *net;
-    ngx_uint_t                  del;
-    ngx_http_variable_value_t  *old;
+    u_char      *p, *last;
+    in_addr_t    start, end;
+    ngx_str_t   *net;
+    ngx_uint_t   del;
 
     if (ngx_strcmp(value[0].data, "default") == 0) {
 
-        old = ctx->high->default_value;
-
-        ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
-        if (ctx->high->default_value == NULL) {
-            return NGX_CONF_ERROR;
+        if (ctx->high.default_value) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                "duplicate default geo range value: \"%V\", old value: \"%v\"",
+                &value[1], ctx->high.default_value);
         }
 
-        if (old) {
-            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
-                    &value[0], ctx->high->default_value, old);
+        ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
+        if (ctx->high.default_value == NULL) {
+            return NGX_CONF_ERROR;
         }
 
         return NGX_CONF_OK;
     }
 
+    if (ctx->binary_include) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+            "binary geo range base \"%s\" may not be mixed with usual entries",
+            ctx->include_name.data);
+        return NGX_CONF_ERROR;
+    }
+
+    if (ctx->high.low == NULL) {
+        ctx->high.low = ngx_pcalloc(ctx->pool,
+                                    0x10000 * sizeof(ngx_http_geo_range_t *));
+        if (ctx->high.low == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    ctx->entries++;
+    ctx->outside_entries = 1;
+
     if (ngx_strcmp(value[0].data, "delete") == 0) {
         net = &value[1];
         del = 1;
@@ -606,7 +651,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n
             e = 0xffff;
         }
 
-        a = (ngx_array_t *) ctx->high->low[h].ranges;
+        a = (ngx_array_t *) ctx->high.low[h];
 
         if (a == NULL) {
             a = ngx_array_create(ctx->temp_pool, 64,
@@ -615,7 +660,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n
                 return NGX_CONF_ERROR;
             }
 
-            ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
+            ctx->high.low[h] = (ngx_http_geo_range_t *) a;
         }
 
         i = a->nelts;
@@ -803,7 +848,7 @@ ngx_http_geo_delete_range(ngx_conf_t *cf
             e = 0xffff;
         }
 
-        a = (ngx_array_t *) ctx->high->low[h].ranges;
+        a = (ngx_array_t *) ctx->high.low[h];
 
         if (a == NULL) {
             warn = 1;
@@ -929,16 +974,17 @@ static ngx_http_variable_value_t *
 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
     ngx_str_t *value)
 {
-    uint32_t                         hash;
-    ngx_http_variable_value_t       *val;
-    ngx_http_variable_value_node_t  *vvn;
+    uint32_t                             hash;
+    ngx_http_variable_value_t           *val;
+    ngx_http_geo_variable_value_node_t  *gvvn;
 
     hash = ngx_crc32_long(value->data, value->len);
 
-    val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
+    gvvn = (ngx_http_geo_variable_value_node_t *)
+               ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
 
-    if (val) {
-        return val;
+    if (gvvn) {
+        return gvvn->value;
     }
 
     val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
@@ -956,16 +1002,22 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_h
     val->no_cacheable = 0;
     val->not_found = 0;
 
-    vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
-    if (vvn == NULL) {
+    gvvn = ngx_palloc(ctx->temp_pool,
+                      sizeof(ngx_http_geo_variable_value_node_t));
+    if (gvvn == NULL) {
         return NULL;
     }
 
-    vvn->node.key = hash;
-    vvn->len = val->len;
-    vvn->value = val;
+    gvvn->sn.node.key = hash;
+    gvvn->sn.str.len = val->len;
+    gvvn->sn.str.data = val->data;
+    gvvn->value = val;
+    gvvn->offset = 0;
 
-    ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
+    ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
+
+    ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
+                                sizeof(void *));
 
     return val;
 }
@@ -1030,3 +1082,335 @@ ngx_http_geo_cidr_value(ngx_conf_t *cf, 
 
     return NGX_OK;
 }
+
+
+static char *
+ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *name)
+{
+    char       *rv;
+    ngx_str_t   file;
+
+    file.len = name->len + 4;
+    file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
+    if (file.data == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_sprintf(file.data, "%V.bin%Z", name);
+
+    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (ctx->ranges) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+        switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
+        case NGX_OK:
+            return NGX_CONF_OK;
+        case NGX_ERROR:
+            return NGX_CONF_ERROR;
+        default:
+            break;
+        }
+    }
+
+    file.len -= 4;
+    file.data[file.len] = '\0';
+
+    ctx->include_name = file;
+
+    if (ctx->outside_entries) {
+        ctx->allow_binary_include = 0;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+    rv = ngx_conf_parse(cf, &file);
+
+    ctx->includes++;
+    ctx->outside_entries = 0;
+
+    return rv;
+}
+
+
+static ngx_int_t
+ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *name)
+{
+    u_char                     *base, ch;
+    time_t                      mtime;
+    size_t                      size, len;
+    ssize_t                     n;
+    uint32_t                    crc32;
+    ngx_err_t                   err;
+    ngx_int_t                   rc;
+    ngx_uint_t                  i;
+    ngx_file_t                  file;
+    ngx_file_info_t             fi;
+    ngx_http_geo_range_t       *range, **ranges;
+    ngx_http_geo_header_t      *header;
+    ngx_http_variable_value_t  *vv;
+
+    ngx_memzero(&file, sizeof(ngx_file_t));
+    file.name = *name;
+    file.log = cf->log;
+
+    file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
+    if (file.fd == NGX_INVALID_FILE) {
+        err = ngx_errno;
+        if (err != NGX_ENOENT) {
+            ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
+                               ngx_open_file_n " \"%s\" failed", name->data);
+        }
+        return NGX_DECLINED;
+    }
+
+    if (ctx->outside_entries) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+            "binary geo range base \"%s\" may not be mixed with usual entries",
+            name->data);
+        rc = NGX_ERROR;
+        goto done;
+    }
+
+    if (ctx->binary_include) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+            "second binary geo range base \"%s\" may not be mixed with \"%s\"",
+            name->data, ctx->include_name.data);
+        rc = NGX_ERROR;
+        goto done;
+    }
+
+    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+                           ngx_fd_info_n " \"%s\" failed", name->data);
+        goto failed;
+    }
+
+    size = (size_t) ngx_file_size(&fi);
+    mtime = ngx_file_mtime(&fi);
+
+    ch = name->data[name->len - 4];
+    name->data[name->len - 4] = '\0';
+
+    if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+                           ngx_file_info_n " \"%s\" failed", name->data);
+        goto failed;
+    }
+
+    name->data[name->len - 4] = ch;
+
+    if (mtime < ngx_file_mtime(&fi)) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "stale binary geo range base \"%s\"", name->data);
+        goto failed;
+    }
+
+    base = ngx_palloc(ctx->pool, size);
+    if (base == NULL) {
+        goto failed;
+    }
+
+    n = ngx_read_file(&file, base, size, 0);
+
+    if (n == NGX_ERROR) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+                           ngx_read_file_n " \"%s\" failed", name->data);
+        goto failed;
+    }
+
+    if ((size_t) n != size) {
+        ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
+            ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
+            name->data, n, size);
+        goto failed;
+    }
+
+    header = (ngx_http_geo_header_t *) base;
+
+    if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+             "incompatible binary geo range base \"%s\"", name->data);
+        goto failed;
+    }
+
+    ngx_crc32_init(crc32);
+
+    vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
+
+    while(vv->data) {
+        len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
+                        sizeof(void *));
+        ngx_crc32_update(&crc32, (u_char *) vv, len);
+        vv->data += (size_t) base;
+        vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
+    }
+    ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
+    vv++;
+
+    ranges = (ngx_http_geo_range_t **) vv;
+
+    for (i = 0; i < 0x10000; i++) {
+        ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
+        if (ranges[i]) {
+            ranges[i] = (ngx_http_geo_range_t *)
+                            ((u_char *) ranges[i] + (size_t) base);
+        }
+    }
+
+    range = (ngx_http_geo_range_t *) &ranges[0x10000];
+
+    while ((u_char *) range < base + size) {
+        while (range->value) {
+            ngx_crc32_update(&crc32, (u_char *) range,
+                             sizeof(ngx_http_geo_range_t));
+            range->value = (ngx_http_variable_value_t *)
+                               ((u_char *) range->value + (size_t) base);
+            range++;
+        }
+        ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
+        range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
+    }
+
+    ngx_crc32_final(crc32);
+
+    if (crc32 != header->crc32) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                  "CRC32 mismatch in binary geo range base \"%s\"", name->data);
+        goto failed;
+    }
+
+    ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
+                       "using binary geo range base \"%s\"", name->data);
+
+    ctx->include_name = *name;
+    ctx->binary_include = 1;
+    ctx->high.low = ranges;
+    rc = NGX_OK;
+
+    goto done;
+
+failed:
+
+    rc = NGX_DECLINED;
+
+done:
+
+    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", name->data);
+    }
+
+    return rc;
+}
+
+
+static void
+ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
+{
+    u_char                              *p;
+    uint32_t                             hash;
+    ngx_str_t                            s;
+    ngx_uint_t                           i;
+    ngx_file_mapping_t                   fm;
+    ngx_http_geo_range_t                *r, *range, **ranges;
+    ngx_http_geo_header_t               *header;
+    ngx_http_geo_variable_value_node_t  *gvvn;
+
+    fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
+    if (fm.name == NULL) {
+        return;
+    }
+
+    ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
+
+    fm.size = ctx->data_size;
+    fm.log = ctx->pool->log;
+
+    ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
+                  "creating binary geo range base \"%s\"", fm.name);
+
+    if (ngx_create_file_mapping(&fm) != NGX_OK) {
+        return;
+    }
+
+    p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
+                   sizeof(ngx_http_geo_header_t));
+
+    p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
+                                 ctx->rbtree.sentinel);
+
+    p += sizeof(ngx_http_variable_value_t);
+
+    ranges = (ngx_http_geo_range_t **) p;
+
+    p += 0x10000 * sizeof(ngx_http_geo_range_t *);
+
+    for (i = 0; i < 0x10000; i++) {
+        r = ctx->high.low[i];
+        if (r == NULL) {
+            continue;
+        }
+
+        range = (ngx_http_geo_range_t *) p;
+        ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
+
+        do {
+            s.len = r->value->len;
+            s.data = r->value->data;
+            hash = ngx_crc32_long(s.data, s.len);
+            gvvn = (ngx_http_geo_variable_value_node_t *)
+                        ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
+
+            range->value = (ngx_http_variable_value_t *) gvvn->offset;
+            range->start = r->start;
+            range->end = r->end;
+            range++;
+
+        } while ((++r)->value);
+
+        range->value = NULL;
+
+        p = (u_char *) range + sizeof(void *);
+    }
+
+    header = fm.addr;
+    header->crc32 = ngx_crc32_long((u_char *) fm.addr
+                                       + sizeof(ngx_http_geo_header_t),
+                                   fm.size - sizeof(ngx_http_geo_header_t));
+
+    ngx_close_file_mapping(&fm);
+}
+
+
+static u_char *
+ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
+    ngx_rbtree_node_t *sentinel)
+{
+    ngx_http_variable_value_t           *vv;
+    ngx_http_geo_variable_value_node_t  *gvvn;
+
+    if (node == sentinel) {
+        return p;
+    }
+
+    gvvn = (ngx_http_geo_variable_value_node_t *) node;
+    gvvn->offset = p - base;
+
+    vv = (ngx_http_variable_value_t *) p;
+    *vv = *gvvn->value;
+    p += sizeof(ngx_http_variable_value_t);
+    vv->data = (u_char *) (p - base);
+
+    p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
+
+    p = ngx_align_ptr(p, sizeof(void *));
+
+    p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
+
+    return ngx_http_geo_copy_values(base, p, node->right, sentinel);
+}
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -374,7 +374,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
     if (ngx_strcmp(value[0].data, "include") == 0) {
         file = value[1];
 
-        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
+        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -431,7 +431,7 @@ ngx_http_ssi_body_filter(ngx_http_reques
 
     while (ctx->in || ctx->buf) {
 
-        if (ctx->buf == NULL ){
+        if (ctx->buf == NULL) {
             ctx->buf = ctx->in->buf;
             ctx->in = ctx->in->next;
             ctx->pos = ctx->buf->pos;
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -210,7 +210,7 @@ ngx_http_sub_body_filter(ngx_http_reques
 
     while (ctx->in || ctx->buf) {
 
-        if (ctx->buf == NULL ){
+        if (ctx->buf == NULL) {
             ctx->buf = ctx->in->buf;
             ctx->in = ctx->in->next;
             ctx->pos = ctx->buf->pos;
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -47,6 +47,8 @@ static ngx_int_t ngx_http_userid_variabl
     ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);
 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
+static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,
+    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
 
 static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);
@@ -199,7 +201,7 @@ ngx_http_userid_filter(ngx_http_request_
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
 
-    if (conf->enable <= NGX_HTTP_USERID_LOG) {
+    if (conf->enable < NGX_HTTP_USERID_V1) {
         return ngx_http_next_header_filter(r);
     }
 
@@ -209,23 +211,6 @@ ngx_http_userid_filter(ngx_http_request_
         return NGX_ERROR;
     }
 
-    if (ctx->uid_got[3] != 0) {
-
-        if (conf->mark == '\0') {
-            return ngx_http_next_header_filter(r);
-
-        } else {
-            if (ctx->cookie.len > 23
-                && ctx->cookie.data[22] == conf->mark
-                && ctx->cookie.data[23] == '=')
-            {
-                return ngx_http_next_header_filter(r);
-            }
-        }
-    }
-
-    /* ctx->status == NGX_DECLINED */
-
     if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
         return ngx_http_next_header_filter(r);
     }
@@ -248,18 +233,16 @@ ngx_http_userid_got_variable(ngx_http_re
         return NGX_OK;
     }
 
-    ctx = ngx_http_userid_get_uid(r, conf);
+    ctx = ngx_http_userid_get_uid(r->main, conf);
 
     if (ctx == NULL) {
         return NGX_ERROR;
     }
 
     if (ctx->uid_got[3] != 0) {
-        return ngx_http_userid_variable(r, v, &conf->name, ctx->uid_got);
+        return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);
     }
 
-    /* ctx->status == NGX_DECLINED */
-
     v->not_found = 1;
 
     return NGX_OK;
@@ -273,16 +256,29 @@ ngx_http_userid_set_variable(ngx_http_re
     ngx_http_userid_ctx_t   *ctx;
     ngx_http_userid_conf_t  *conf;
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
+    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
 
-    if (ctx == NULL || ctx->uid_set[3] == 0) {
+    if (conf->enable < NGX_HTTP_USERID_V1) {
         v->not_found = 1;
         return NGX_OK;
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
+    ctx = ngx_http_userid_get_uid(r->main, conf);
+
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
 
-    return ngx_http_userid_variable(r, v, &conf->name, ctx->uid_set);
+    if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (ctx->uid_set[3] == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);
 }
 
 
@@ -360,82 +356,17 @@ static ngx_int_t
 ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
     ngx_http_userid_conf_t *conf)
 {
-    u_char               *cookie, *p;
-    size_t                len;
-    ngx_str_t             src, dst;
-    ngx_table_elt_t      *set_cookie, *p3p;
-    ngx_connection_t     *c;
-    struct sockaddr_in   *sin;
-#if (NGX_HAVE_INET6)
-    struct sockaddr_in6  *sin6;
-#endif
-
-    /*
-     * TODO: in the threaded mode the sequencers should be in TLS and their
-     * ranges should be divided between threads
-     */
-
-    if (ctx->uid_got[3] == 0) {
-
-        if (conf->enable == NGX_HTTP_USERID_V1) {
-            if (conf->service == NGX_CONF_UNSET) {
-                ctx->uid_set[0] = 0;
-            } else {
-                ctx->uid_set[0] = conf->service;
-            }
-            ctx->uid_set[1] = (uint32_t) ngx_time();
-            ctx->uid_set[2] = start_value;
-            ctx->uid_set[3] = sequencer_v1;
-            sequencer_v1 += 0x100;
-
-        } else {
-            if (conf->service == NGX_CONF_UNSET) {
-
-                c = r->connection;
-
-                if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
-                    return NGX_ERROR;
-                }
+    u_char           *cookie, *p;
+    size_t            len;
+    ngx_str_t         src, dst;
+    ngx_table_elt_t  *set_cookie, *p3p;
 
-                switch (c->local_sockaddr->sa_family) {
-
-#if (NGX_HAVE_INET6)
-                case AF_INET6:
-                    sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
-
-                    p = (u_char *) &ctx->uid_set[0];
-
-                    *p++ = sin6->sin6_addr.s6_addr[12];
-                    *p++ = sin6->sin6_addr.s6_addr[13];
-                    *p++ = sin6->sin6_addr.s6_addr[14];
-                    *p = sin6->sin6_addr.s6_addr[15];
+    if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {
+        return NGX_ERROR;
+    }
 
-                    break;
-#endif
-                default: /* AF_INET */
-                    sin = (struct sockaddr_in *) c->local_sockaddr;
-                    ctx->uid_set[0] = sin->sin_addr.s_addr;
-                    break;
-                }
-
-            } else {
-                ctx->uid_set[0] = htonl(conf->service);
-            }
-
-            ctx->uid_set[1] = htonl((uint32_t) ngx_time());
-            ctx->uid_set[2] = htonl(start_value);
-            ctx->uid_set[3] = htonl(sequencer_v2);
-            sequencer_v2 += 0x100;
-            if (sequencer_v2 < 0x03030302) {
-                sequencer_v2 = 0x03030302;
-            }
-        }
-
-    } else {
-        ctx->uid_set[0] = ctx->uid_got[0];
-        ctx->uid_set[1] = ctx->uid_got[1];
-        ctx->uid_set[2] = ctx->uid_got[2];
-        ctx->uid_set[3] = ctx->uid_got[3];
+    if (ctx->uid_set[3] == 0) {
+        return NGX_OK;
     }
 
     len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
@@ -518,6 +449,102 @@ ngx_http_userid_set_uid(ngx_http_request
 
 
 static ngx_int_t
+ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
+    ngx_http_userid_conf_t *conf)
+{
+    ngx_connection_t     *c;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    u_char               *p;
+    struct sockaddr_in6  *sin6;
+#endif
+
+    if (ctx->uid_set[3] != 0) {
+        return NGX_OK;
+    }
+
+    if (ctx->uid_got[3] != 0) {
+
+        if (conf->mark == '\0'
+            || (ctx->cookie.len > 23
+                && ctx->cookie.data[22] == conf->mark
+                && ctx->cookie.data[23] == '='))
+        {
+            return NGX_OK;
+        }
+
+        ctx->uid_set[0] = ctx->uid_got[0];
+        ctx->uid_set[1] = ctx->uid_got[1];
+        ctx->uid_set[2] = ctx->uid_got[2];
+        ctx->uid_set[3] = ctx->uid_got[3];
+
+        return NGX_OK;
+    }
+
+    /*
+     * TODO: in the threaded mode the sequencers should be in TLS and their
+     * ranges should be divided between threads
+     */
+
+    if (conf->enable == NGX_HTTP_USERID_V1) {
+        if (conf->service == NGX_CONF_UNSET) {
+            ctx->uid_set[0] = 0;
+        } else {
+            ctx->uid_set[0] = conf->service;
+        }
+        ctx->uid_set[1] = (uint32_t) ngx_time();
+        ctx->uid_set[2] = start_value;
+        ctx->uid_set[3] = sequencer_v1;
+        sequencer_v1 += 0x100;
+
+    } else {
+        if (conf->service == NGX_CONF_UNSET) {
+
+            c = r->connection;
+
+            if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+            case AF_INET6:
+                sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+                p = (u_char *) &ctx->uid_set[0];
+
+                *p++ = sin6->sin6_addr.s6_addr[12];
+                *p++ = sin6->sin6_addr.s6_addr[13];
+                *p++ = sin6->sin6_addr.s6_addr[14];
+                *p = sin6->sin6_addr.s6_addr[15];
+
+                break;
+#endif
+            default: /* AF_INET */
+                sin = (struct sockaddr_in *) c->local_sockaddr;
+                ctx->uid_set[0] = sin->sin_addr.s_addr;
+                break;
+            }
+
+        } else {
+            ctx->uid_set[0] = htonl(conf->service);
+        }
+
+        ctx->uid_set[1] = htonl((uint32_t) ngx_time());
+        ctx->uid_set[2] = htonl(start_value);
+        ctx->uid_set[3] = htonl(sequencer_v2);
+        sequencer_v2 += 0x100;
+        if (sequencer_v2 < 0x03030302) {
+            sequencer_v2 = 0x03030302;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     ngx_str_t *name, uint32_t *uid)
 {
@@ -550,7 +577,7 @@ ngx_http_userid_add_variables(ngx_conf_t
 
     var->get_handler = ngx_http_userid_got_variable;
 
-    var = ngx_http_add_variable(cf, &ngx_http_userid_set, NGX_HTTP_VAR_NOHASH);
+    var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);
     if (var == NULL) {
         return NGX_ERROR;
     }
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -268,7 +268,7 @@ ngx_http_xslt_body_filter(ngx_http_reque
 
         if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
 
-            if (ctx->ctxt->myDoc){
+            if (ctx->ctxt->myDoc) {
 
 #if (NGX_HTTP_XSLT_REUSE_DTD)
                 ctx->ctxt->myDoc->extSubset = NULL;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.8.42';
+our $VERSION = '0.8.43';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1748,6 +1748,7 @@ ngx_http_send_response(ngx_http_request_
     r->headers_out.status = status;
 
     if (status == NGX_HTTP_NO_CONTENT) {
+        r->header_only = 1;
         return ngx_http_send_header(r);
     }
 
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -170,6 +170,10 @@ ngx_http_header_filter(ngx_http_request_
 #endif
     u_char                     addr[NGX_SOCKADDR_STRLEN];
 
+    if (r->header_sent) {
+        return NGX_OK;
+    }
+
     r->header_sent = 1;
 
     if (r != r->main) {
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -272,7 +272,7 @@ ngx_http_parse_request_line(ngx_http_req
         /* space* before URI */
         case sw_spaces_before_uri:
 
-            if (ch == '/' ){
+            if (ch == '/') {
                 r->uri_start = p;
                 state = sw_after_slash_in_uri;
                 break;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -489,7 +489,6 @@ struct ngx_http_request_s {
     unsigned                          plain_http:1;
     unsigned                          chunked:1;
     unsigned                          header_only:1;
-    unsigned                          zero_body:1;
     unsigned                          keepalive:1;
     unsigned                          lingering_close:1;
     unsigned                          discard_body:1;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -598,31 +598,24 @@ ngx_http_send_special_response(ngx_http_
 
     msie_padding = 0;
 
-    if (!r->zero_body) {
-        if (ngx_http_error_pages[err].len) {
-            r->headers_out.content_length_n = ngx_http_error_pages[err].len
-                                              + len;
-            if (clcf->msie_padding
-                && (r->headers_in.msie || r->headers_in.chrome)
-                && r->http_version >= NGX_HTTP_VERSION_10
-                && err >= NGX_HTTP_LEVEL_300)
-            {
-                r->headers_out.content_length_n +=
-                                             sizeof(ngx_http_msie_padding) - 1;
-                msie_padding = 1;
-            }
-
-            r->headers_out.content_type_len = sizeof("text/html") - 1;
-            ngx_str_set(&r->headers_out.content_type, "text/html");
-            r->headers_out.content_type_lowcase = NULL;
-
-        } else {
-            r->headers_out.content_length_n = -1;
+    if (ngx_http_error_pages[err].len) {
+        r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
+        if (clcf->msie_padding
+            && (r->headers_in.msie || r->headers_in.chrome)
+            && r->http_version >= NGX_HTTP_VERSION_10
+            && err >= NGX_HTTP_LEVEL_300)
+        {
+            r->headers_out.content_length_n +=
+                                         sizeof(ngx_http_msie_padding) - 1;
+            msie_padding = 1;
         }
 
+        r->headers_out.content_type_len = sizeof("text/html") - 1;
+        ngx_str_set(&r->headers_out.content_type, "text/html");
+        r->headers_out.content_type_lowcase = NULL;
+
     } else {
-        r->headers_out.content_length_n = 0;
-        err = 0;
+        r->headers_out.content_length_n = -1;
     }
 
     if (r->headers_out.content_length) {
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -1973,87 +1973,3 @@ ngx_http_variables_init_vars(ngx_conf_t 
 
     return NGX_OK;
 }
-
-
-void
-ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
-    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
-{
-    ngx_rbtree_node_t               **p;
-    ngx_http_variable_value_node_t   *vvn, *vvt;
-
-    for ( ;; ) {
-
-        vvn = (ngx_http_variable_value_node_t *) node;
-        vvt = (ngx_http_variable_value_node_t *) temp;
-
-        if (node->key != temp->key) {
-
-            p = (node->key < temp->key) ? &temp->left : &temp->right;
-
-        } else if (vvn->len != vvt->len) {
-
-            p = (vvn->len < vvt->len) ? &temp->left : &temp->right;
-
-        } else {
-            p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0)
-                 ? &temp->left : &temp->right;
-        }
-
-        if (*p == sentinel) {
-            break;
-        }
-
-        temp = *p;
-    }
-
-    *p = node;
-    node->parent = temp;
-    node->left = sentinel;
-    node->right = sentinel;
-    ngx_rbt_red(node);
-}
-
-
-ngx_http_variable_value_t *
-ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val,
-    uint32_t hash)
-{
-    ngx_int_t                        rc;
-    ngx_rbtree_node_t               *node, *sentinel;
-    ngx_http_variable_value_node_t  *vvn;
-
-    node = rbtree->root;
-    sentinel = rbtree->sentinel;
-
-    while (node != sentinel) {
-
-        vvn = (ngx_http_variable_value_node_t *) node;
-
-        if (hash != node->key) {
-            node = (hash < node->key) ? node->left : node->right;
-            continue;
-        }
-
-        if (val->len != vvn->len) {
-            node = (val->len < vvn->len) ? node->left : node->right;
-            continue;
-        }
-
-        rc = ngx_memcmp(val->data, vvn->value->data, val->len);
-
-        if (rc < 0) {
-            node = node->left;
-            continue;
-        }
-
-        if (rc > 0) {
-            node = node->right;
-            continue;
-        }
-
-        return vvn->value;
-    }
-
-    return NULL;
-}
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -88,19 +88,6 @@ ngx_int_t ngx_http_variables_add_core_va
 ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
 
 
-typedef struct {
-    ngx_rbtree_node_t             node;
-    size_t                        len;
-    ngx_http_variable_value_t    *value;
-} ngx_http_variable_value_node_t;
-
-
-void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
-    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
-ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree,
-    ngx_str_t *name, uint32_t hash);
-
-
 extern ngx_http_variable_value_t  ngx_http_variable_null_value;
 extern ngx_http_variable_value_t  ngx_http_variable_true_value;
 
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -346,7 +346,7 @@ ngx_mail_auth_plain(ngx_mail_session_t *
 #endif
 
     plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
-    if (plain.data == NULL){
+    if (plain.data == NULL) {
         return NGX_ERROR;
     }
 
@@ -403,7 +403,7 @@ ngx_mail_auth_login_username(ngx_mail_se
                    "mail auth login username: \"%V\"", &arg[n]);
 
     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
-    if (s->login.data == NULL){
+    if (s->login.data == NULL) {
         return NGX_ERROR;
     }
 
@@ -434,7 +434,7 @@ ngx_mail_auth_login_password(ngx_mail_se
 
     s->passwd.data = ngx_pnalloc(c->pool,
                                  ngx_base64_decoded_length(arg[0].len));
-    if (s->passwd.data == NULL){
+    if (s->passwd.data == NULL) {
         return NGX_ERROR;
     }
 
@@ -494,7 +494,7 @@ ngx_mail_auth_cram_md5(ngx_mail_session_
                    "mail auth cram-md5: \"%V\"", &arg[0]);
 
     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
-    if (s->login.data == NULL){
+    if (s->login.data == NULL) {
         return NGX_ERROR;
     }
 
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -76,7 +76,7 @@ ngx_write_file(ngx_file_t *file, u_char 
 #if (NGX_HAVE_PWRITE)
 
     for ( ;; ) {
-        n = pwrite(file->fd, buf, size, offset);
+        n = pwrite(file->fd, buf + written, size, offset);
 
         if (n == -1) {
             ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
@@ -108,7 +108,7 @@ ngx_write_file(ngx_file_t *file, u_char 
     }
 
     for ( ;; ) {
-        n = write(file->fd, buf, size);
+        n = write(file->fd, buf + written, size);
 
         if (n == -1) {
             ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
@@ -259,6 +259,58 @@ ngx_set_file_time(u_char *name, ngx_fd_t
 
 
 ngx_int_t
+ngx_create_file_mapping(ngx_file_mapping_t *fm)
+{
+    fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE,
+                           NGX_FILE_DEFAULT_ACCESS);
+    if (fm->fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", fm->name);
+        return NGX_ERROR;
+    }
+
+    if (ftruncate(fm->fd, fm->size) == -1) {
+        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+                      "ftruncate() \"%s\" failed", fm->name);
+        goto failed;
+    }
+
+    fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED,
+                    fm->fd, 0);
+    if (fm->addr != MAP_FAILED) {
+        return NGX_OK;
+    }
+
+    ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+                  "mmap(%uz) \"%s\" failed", fm->size, fm->name);
+
+failed:
+
+    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", fm->name);
+    }
+
+    return NGX_ERROR;
+}
+
+
+void
+ngx_close_file_mapping(ngx_file_mapping_t *fm)
+{
+    if (munmap(fm->addr, fm->size) == -1) {
+        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+                      "munmap(%uz) \"%s\" failed", fm->size, fm->name);
+    }
+
+    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", fm->name);
+    }
+}
+
+
+ngx_int_t
 ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)
 {
     dir->dir = opendir((const char *) name->data);
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -18,6 +18,15 @@ typedef ino_t                    ngx_fil
 
 
 typedef struct {
+    u_char                      *name;
+    size_t                       size;
+    void                        *addr;
+    ngx_fd_t                     fd;
+    ngx_log_t                   *log;
+} ngx_file_mapping_t;
+
+
+typedef struct {
     DIR                         *dir;
     struct dirent               *de;
     struct stat                  info;
@@ -152,6 +161,10 @@ ngx_int_t ngx_set_file_time(u_char *name
 #define ngx_file_uniq(sb)        (sb)->st_ino
 
 
+ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm);
+void ngx_close_file_mapping(ngx_file_mapping_t *fm);
+
+
 #if (NGX_HAVE_CASELESS_FILESYSTEM)
 
 #define ngx_filename_cmp(s1, s2, n)  strncasecmp((char *) s1, (char *) s2, n)
--- a/src/os/unix/ngx_readv_chain.c
+++ b/src/os/unix/ngx_readv_chain.c
@@ -158,7 +158,7 @@ ngx_readv_chain(ngx_connection_t *c, ngx
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         c->read->error = 1;
     }
 
@@ -247,7 +247,7 @@ ngx_readv_chain(ngx_connection_t *c, ngx
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         c->read->error = 1;
     }
 
--- a/src/os/unix/ngx_recv.c
+++ b/src/os/unix/ngx_recv.c
@@ -113,7 +113,7 @@ ngx_unix_recv(ngx_connection_t *c, u_cha
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         rev->error = 1;
     }
 
@@ -169,7 +169,7 @@ ngx_unix_recv(ngx_connection_t *c, u_cha
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         rev->error = 1;
     }
 
--- a/src/os/unix/ngx_udp_recv.c
+++ b/src/os/unix/ngx_udp_recv.c
@@ -60,7 +60,7 @@ ngx_udp_unix_recv(ngx_connection_t *c, u
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         rev->error = 1;
     }
 
@@ -104,7 +104,7 @@ ngx_udp_unix_recv(ngx_connection_t *c, u
 
     rev->ready = 0;
 
-    if (n == NGX_ERROR){
+    if (n == NGX_ERROR) {
         rev->error = 1;
     }