changeset 272:29a6403156b0 NGINX_0_5_6

nginx 0.5.6 *) Change: now the ngx_http_index_module ignores all methods except the GET, HEAD, and POST methods. *) Feature: the ngx_http_limit_zone_module. *) Feature: the $binary_remote_addr variable. *) Feature: the "ssl_session_cache" directives of the ngx_http_ssl_module and ngx_imap_ssl_module. *) Feature: the DELETE method supports recursive removal. *) Bugfix: the byte-ranges were transferred incorrectly if the $r->sendfile() was used.
author Igor Sysoev <http://sysoev.ru>
date Tue, 09 Jan 2007 00:00:00 +0300
parents fcbee7dacf2b
children 60df8db42ffb
files CHANGES CHANGES.ru LICENSE auto/modules auto/options auto/sources src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_core.h src/core/ngx_cycle.c src/core/ngx_cycle.h src/core/ngx_file.c src/core/ngx_file.h src/core/ngx_rbtree.h src/core/ngx_slab.c src/core/ngx_slab.h src/event/ngx_event_openssl.c src/event/ngx_event_openssl.h src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_index_module.c src/http/modules/ngx_http_limit_zone_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_realip_module.c src/http/modules/ngx_http_ssl_module.c src/http/modules/ngx_http_ssl_module.h src/http/modules/perl/nginx.pm src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_upstream_round_robin.h src/http/ngx_http_variables.c src/imap/ngx_imap_ssl_module.c src/imap/ngx_imap_ssl_module.h src/os/unix/ngx_linux_init.c
diffstat 34 files changed, 2032 insertions(+), 265 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,22 @@
 
+Changes with nginx 0.5.6                                         09 Jan 2007
+
+    *) Change: now the ngx_http_index_module ignores all methods except the 
+       GET, HEAD, and POST methods.
+
+    *) Feature: the ngx_http_limit_zone_module.
+
+    *) Feature: the $binary_remote_addr variable.
+
+    *) Feature: the "ssl_session_cache" directives of the 
+       ngx_http_ssl_module and ngx_imap_ssl_module.
+
+    *) Feature: the DELETE method supports recursive removal.
+
+    *) Bugfix: the byte-ranges were transferred incorrectly if the 
+       $r->sendfile() was used.
+
+
 Changes with nginx 0.5.5                                         24 Dec 2006
 
     *) Change: the -v switch does not show compiler information any more.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,22 @@
 
+Изменения в nginx 0.5.6                                           09.01.2007
+
+    *) Изменение: теперь модуль ngx_http_index_module игнорирует все 
+       методы, кроме GET, HEAD и POST.
+
+    *) Добавление: модуль ngx_http_limit_zone_module.
+
+    *) Добавление: переменная $binary_remote_addr.
+
+    *) Добавление: директивы ssl_session_cache модулей ngx_http_ssl_module 
+       и ngx_imap_ssl_module.
+
+    *) Добавление: метод DELETE поддерживает рекурсивное удаление.
+
+    *) Исправление: при использовании $r->sendfile() byte-ranges 
+       передавались неверно.
+
+
 Изменения в nginx 0.5.5                                           24.12.2006
 
     *) Изменение: ключ -v больше не выводит информацию о компиляторе.
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2002-2006 Igor Sysoev
+ * Copyright (C) 2002-2007 Igor Sysoev
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
--- a/auto/modules
+++ b/auto/modules
@@ -169,6 +169,11 @@ if [ $HTTP_ACCESS = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS"
 fi
 
+if [ $HTTP_LIMIT_ZONE = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_ZONE_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS"
+fi
+
 if [ $HTTP_REALIP = YES ]; then
     have=NGX_HTTP_REALIP . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE"
--- a/auto/options
+++ b/auto/options
@@ -68,6 +68,7 @@ HTTP_PROXY=YES
 HTTP_FASTCGI=YES
 HTTP_PERL=NO
 HTTP_MEMCACHED=YES
+HTTP_LIMIT_ZONE=YES
 HTTP_EMPTY_GIF=YES
 HTTP_BROWSER=YES
 HTTP_FLV=NO
@@ -169,6 +170,7 @@ do
         --without-http_proxy_module)     HTTP_PROXY=NO              ;;
         --without-http_fastcgi_module)   HTTP_FASTCGI=NO            ;;
         --without-http_memcached_module) HTTP_MEMCACHED=NO          ;;
+        --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO        ;;
         --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;
         --without-http_browser_module)   HTTP_BROWSER=NO            ;;
         --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
@@ -271,6 +273,7 @@ cat << END
   --without-http_proxy_module        disable ngx_http_proxy_module
   --without-http_fastcgi_module      disable ngx_http_fastcgi_module
   --without-http_memcached_module    disable ngx_http_memcached_module
+  --without-http_limit_zone_module   disable ngx_http_limit_zone_module
   --without-http_empty_gif_module    disable ngx_http_empty_gif_module
   --without-http_browser_module      disable ngx_http_browser_module
   --without-http_upstream_ip_hash_module
--- a/auto/sources
+++ b/auto/sources
@@ -388,6 +388,10 @@ HTTP_MEMCACHED_MODULE=ngx_http_memcached
 HTTP_MEMCACHED_SRCS=src/http/modules/ngx_http_memcached_module.c
 
 
+HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zone_module
+HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c
+
+
 HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module
 HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.5.5"
+#define NGINX_VERSION      "0.5.6"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -150,9 +150,9 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
 
         if (rc == NGX_CONF_FILE_DONE && block) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                         "unexpected end of file in %s:%ui, expecting \"}\"",
-                         cf->conf_file->file.name.data,
-                         cf->conf_file->line);
+                          "unexpected end of file in %s:%ui, expecting \"}\"",
+                          cf->conf_file->file.name.data,
+                          cf->conf_file->line);
             rc = NGX_ERROR;
             break;
         }
@@ -179,9 +179,9 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
             }
 
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                         "%s in %s:%ui",
-                         rv, cf->conf_file->file.name.data,
-                         cf->conf_file->line);
+                          "%s in %s:%ui",
+                          rv, cf->conf_file->file.name.data,
+                          cf->conf_file->line);
             rc = NGX_ERROR;
             break;
         }
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -64,11 +64,11 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_times.h>
 #include <ngx_shmtx.h>
 #include <ngx_slab.h>
+#include <ngx_inet.h>
+#include <ngx_cycle.h>
 #if (NGX_OPENSSL)
 #include <ngx_event_openssl.h>
 #endif
-#include <ngx_inet.h>
-#include <ngx_cycle.h>
 #include <ngx_process_cycle.h>
 #include <ngx_conf_file.h>
 #include <ngx_os.h>
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -49,7 +49,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     ngx_conf_t          conf;
     ngx_pool_t         *pool;
     ngx_cycle_t        *cycle, **old;
-    ngx_shm_zone_t     *shm, *oshm;
+    ngx_shm_zone_t     *shm_zone, *oshm_zone;
     ngx_slab_pool_t    *shpool;
     ngx_list_part_t    *part, *opart;
     ngx_open_file_t    *file;
@@ -353,7 +353,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     /* create shared memory */
 
     part = &cycle->shared_memory.part;
-    shm = part->elts;
+    shm_zone = part->elts;
 
     for (i = 0; /* void */ ; i++) {
 
@@ -362,14 +362,21 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
                 break;
             }
             part = part->next;
-            shm = part->elts;
+            shm_zone = part->elts;
             i = 0;
         }
 
-        shm[i].shm.log = cycle->log;
+        if (shm_zone[i].shm.size == 0) {
+            ngx_log_error(NGX_LOG_EMERG, log, 0,
+                          "zero size shared memory zone \"%V\"",
+                          &shm_zone[i].name);
+            goto failed;
+        }
+
+        shm_zone[i].shm.log = cycle->log;
 
         opart = &old_cycle->shared_memory.part;
-        oshm = opart->elts;
+        oshm_zone = opart->elts;
 
         for (n = 0; /* void */ ; n++) {
 
@@ -378,31 +385,45 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
                     break;
                 }
                 opart = opart->next;
-                oshm = opart->elts;
+                oshm_zone = opart->elts;
                 n = 0;
             }
 
-            if (ngx_strcmp(shm[i].name.data, oshm[n].name.data) != 0) {
+            if (shm_zone[i].name.len != oshm_zone[n].name.len) {
+                continue;
+            }
+
+            if (ngx_strncmp(shm_zone[i].name.data, oshm_zone[n].name.data,
+                            shm_zone[i].name.len)
+                != 0)
+            {
                 continue;
             }
 
-            if (shm[i].shm.size == oshm[n].shm.size) {
-                shm[i].shm.addr = oshm[n].shm.addr;
-                goto found;
+            if (shm_zone[i].shm.size == oshm_zone[n].shm.size) {
+                shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
+
+                if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
+                    != NGX_OK)
+                {
+                    goto failed;
+                }
+
+                goto shm_zone_found;
             }
 
-            ngx_shm_free(&oshm[n].shm);
+            ngx_shm_free(&oshm_zone[n].shm);
 
             break;
         }
 
-        if (ngx_shm_alloc(&shm[i].shm) != NGX_OK) {
+        if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
             goto failed;
         }
 
-        shpool = (ngx_slab_pool_t *) shm[i].shm.addr;
+        shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr;
 
-        shpool->end = shm[i].shm.addr + shm[i].shm.size;
+        shpool->end = shm_zone[i].shm.addr + shm_zone[i].shm.size;
         shpool->min_shift = 3;
 
 #if (NGX_HAVE_ATOMIC_OPS)
@@ -412,7 +433,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 #else
 
         lock_file = ngx_palloc(cycle->pool,
-                               cycle->lock_file.len + shm[i].name.len);
+                               cycle->lock_file.len + shm_zone[i].name.len);
 
         if (lock_file == NULL) {
             goto failed;
@@ -420,7 +441,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
         (void) ngx_cpystrn(ngx_cpymem(lock_file, cycle->lock_file.data,
                                       cycle->lock_file.len),
-                           shm[i].name.data, shm[i].name.len + 1);
+                           shm_zone[i].name.data, shm_zone[i].name.len + 1);
 
 #endif
 
@@ -432,7 +453,11 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
         ngx_slab_init(shpool);
 
-    found:
+        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
+            goto failed;
+        }
+
+    shm_zone_found:
 
         continue;
     }
@@ -569,7 +594,57 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
 
     /* close and delete stuff that lefts from an old cycle */
 
-    /* close the unneeded listening sockets */
+    /* free the unnecessary shared memory */
+
+    opart = &old_cycle->shared_memory.part;
+    oshm_zone = opart->elts;
+
+    for (i = 0; /* void */ ; i++) {
+
+        if (i >= opart->nelts) {
+            if (opart->next == NULL) {
+                goto old_shm_zone_done;
+            }
+            opart = opart->next;
+            oshm_zone = opart->elts;
+            i = 0;
+        }
+
+        part = &cycle->shared_memory.part;
+        shm_zone = part->elts;
+
+        for (n = 0; /* void */ ; n++) {
+
+            if (n >= part->nelts) {
+                if (part->next == NULL) {
+                    break;
+                }
+                part = part->next;
+                shm_zone = part->elts;
+                n = 0;
+            }
+
+            if (oshm_zone[i].name.len == shm_zone[n].name.len
+                && ngx_strncmp(oshm_zone[i].name.data,
+                               shm_zone[n].name.data,
+                               oshm_zone[i].name.len)
+                == 0)
+            {
+                goto live_shm_zone;
+            }
+        }
+
+        ngx_shm_free(&oshm_zone[i].shm);
+
+    live_shm_zone:
+
+        continue;
+    }
+
+old_shm_zone_done:
+
+
+    /* close the unnecessary listening sockets */
 
     ls = old_cycle->listening.elts;
     for (i = 0; i < old_cycle->listening.nelts; i++) {
@@ -585,7 +660,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
     }
 
 
-    /* close the unneeded open files */
+    /* close the unnecessary open files */
 
     part = &old_cycle->open_files.part;
     file = part->elts;
@@ -988,6 +1063,71 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
 }
 
 
+ngx_shm_zone_t *
+ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
+{
+    ngx_uint_t        i;
+    ngx_shm_zone_t   *shm_zone;
+    ngx_list_part_t  *part;
+
+    part = &cf->cycle->shared_memory.part;
+    shm_zone = part->elts;
+
+    for (i = 0; /* void */ ; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+            part = part->next;
+            shm_zone = part->elts;
+            i = 0;
+        }
+
+        if (name->len != shm_zone[i].name.len) {
+            continue;
+        }
+
+        if (ngx_strncmp(name->data, shm_zone[i].name.data, name->len) != 0) {
+            continue;
+        }
+
+        if (size && size != shm_zone[i].shm.size) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "the size %uz of shared memory zone \"%V\" "
+                               "conflicts with already declared size %uz",
+                               size, &shm_zone[i].name, shm_zone[i].shm.size);
+            return NULL;
+        }
+
+        if (tag != shm_zone[i].tag) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "the shared memory zone \"%V\" is "
+                               "already declared for a different use",
+                               &shm_zone[i].name);
+            return NULL;
+        }
+
+        return &shm_zone[i];
+    }
+
+    shm_zone = ngx_list_push(&cf->cycle->shared_memory);
+
+    if (shm_zone == NULL) {
+        return NULL;
+    }
+
+    shm_zone->data = NULL;
+    shm_zone->shm.log = cf->cycle->log;
+    shm_zone->shm.size = size;
+    shm_zone->init = NULL;
+    shm_zone->name = *name;
+    shm_zone->tag = tag;
+
+    return shm_zone;
+}
+
+
 static void
 ngx_clean_old_cycles(ngx_event_t *ev)
 {
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -21,10 +21,17 @@
 #define NGX_DEBUG_POINTS_ABORT  2
 
 
-typedef struct {
+typedef struct ngx_shm_zone_s  ngx_shm_zone_t;
+
+typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);
+
+struct ngx_shm_zone_s {
+    void                     *data;
     ngx_shm_t                 shm;
+    ngx_shm_zone_init_pt      init;
     ngx_str_t                 name;
-} ngx_shm_zone_t;
+    void                     *tag;
+};
 
 
 struct ngx_cycle_s {
@@ -108,6 +115,8 @@ void ngx_delete_pidfile(ngx_cycle_t *cyc
 void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);
 ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
 u_long ngx_get_cpu_affinity(ngx_uint_t n);
+ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
+    size_t size, void *tag);
 
 
 extern volatile ngx_cycle_t  *ngx_cycle;
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -418,3 +418,168 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
 
     return NGX_OK;
 }
+
+
+ngx_int_t
+ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
+{
+    void       *data, *prev;
+    u_char     *p, *name;
+    size_t      len;
+    ngx_int_t   rc;
+    ngx_err_t   err;
+    ngx_str_t   file, buf;
+    ngx_dir_t   dir;
+
+    buf.len = 0;
+    buf.data = NULL;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                   "walk tree \"%V\"", tree);
+
+    if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_open_dir_n " \"%s\" failed", tree->data);
+        return NGX_ERROR;
+    }
+
+    prev = ctx->data;
+
+    if (ctx->size) {
+        data = ngx_alloc(ctx->size, ctx->log);
+        if (data == NULL) {
+            goto failed;
+        }
+
+        if (ctx->init_handler(data, prev) == NGX_ABORT) {
+            goto failed;
+        }
+
+        ctx->data = data;
+    }
+
+    for ( ;; ) {
+
+        ngx_set_errno(0);
+
+        if (ngx_read_dir(&dir) == NGX_ERROR) {
+            err = ngx_errno;
+
+            if (err == NGX_ENOMOREFILES) {
+                rc = NGX_OK;
+
+            } else {
+                ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
+                              ngx_read_dir_n " \"%s\" failed", tree->data);
+                rc = NGX_ERROR;
+            }
+
+            goto done;
+        }
+
+        len = ngx_de_namelen(&dir);
+        name = ngx_de_name(&dir);
+
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                      "tree name %uz:\"%s\"", len, name);
+
+        if (len == 1 && name[0] == '.') {
+            continue;
+        }
+
+        if (len == 2 && name[0] == '.' && name[1] == '.') {
+            continue;
+        }
+
+        file.len = tree->len + 1 + len;
+
+        if (file.len + NGX_DIR_MASK_LEN > buf.len) {
+
+            if (buf.len) {
+                ngx_free(buf.data);
+            }
+
+            buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;
+
+            buf.data = ngx_alloc(buf.len + 1, ctx->log);
+            if (buf.data == NULL) {
+                goto failed;
+            }
+        }
+
+        p = ngx_cpymem(buf.data, tree->data, tree->len);
+        *p++ = '/';
+        ngx_memcpy(p, name, len + 1);
+
+        file.data = buf.data;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                       "tree path \"%s\"", file.data);
+
+        if (!dir.valid_info) {
+            if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                              ngx_de_info_n " \"%s\" failed", file.data);
+                continue;
+            }
+        }
+
+        if (ngx_de_is_file(&dir)) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                           "tree file \"%s\"", file.data);
+
+            if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
+                goto failed;
+            }
+
+        } else if (ngx_de_is_dir(&dir)) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                           "tree enter dir \"%s\"", file.data);
+
+            if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {
+                goto failed;
+            }
+
+            if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
+                goto failed;
+            }
+
+            if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
+                goto failed;
+            }
+
+        } else {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+                           "tree special \"%s\"", file.data);
+
+            if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
+                goto failed;
+            }
+        }
+    }
+
+failed:
+
+    rc = NGX_ABORT;
+
+done:
+
+    if (buf.len) {
+        ngx_free(buf.data);
+    }
+
+    if (ctx->data) {
+        ngx_free(ctx->data);
+        ctx->data = prev;
+    }
+
+    if (ngx_close_dir(&dir) == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+                      ngx_close_dir_n " \"%s\" failed", tree->data);
+    }
+
+    return rc;
+}
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -56,6 +56,24 @@ typedef struct {
 } ngx_temp_file_t;
 
 
+typedef struct ngx_tree_ctx_s  ngx_tree_ctx_t;
+
+typedef ngx_int_t (*ngx_tree_init_handler_pt) (ngx_tree_ctx_t *ctx,
+    ngx_tree_ctx_t *prev);
+typedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name);
+
+struct ngx_tree_ctx_s {
+    ngx_tree_init_handler_pt   init_handler;
+    ngx_tree_handler_pt        file_handler;
+    ngx_tree_handler_pt        pre_tree_handler;
+    ngx_tree_handler_pt        post_tree_handler;
+    ngx_tree_handler_pt        spec_handler;
+    void                      *data;
+    size_t                     size;
+    ngx_log_t                 *log;
+};
+
+
 ssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain);
 ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,
     ngx_pool_t *pool, ngx_uint_t persistent,ngx_uint_t mode);
@@ -64,6 +82,7 @@ ngx_int_t ngx_create_path(ngx_file_t *fi
 ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);
 ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);
 ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
+ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);
 
 void ngx_init_temp_number(void);
 ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision);
--- a/src/core/ngx_rbtree.h
+++ b/src/core/ngx_rbtree.h
@@ -50,6 +50,11 @@ void ngx_rbtree_insert_timer_value(ngx_r
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 
 
+/* a sentinel must be black */
+
+#define ngx_rbtree_sentinel_init(node)   node->color = 0
+
+
 static ngx_inline ngx_rbtree_node_t *
 ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 {
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -148,14 +148,31 @@ ngx_slab_init(ngx_slab_pool_t *pool)
 void *
 ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)
 {
+    void  *p;
+
+    ngx_shmtx_lock(&pool->mutex);
+
+    p = ngx_slab_alloc_locked(pool, size);
+
+    ngx_shmtx_unlock(&pool->mutex);
+
+    return p;
+}
+
+
+void *
+ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)
+{
     size_t            s;
     uintptr_t         p, n, m, mask, *bitmap;
     ngx_uint_t        i, slot, shift, map;
     ngx_slab_page_t  *page, *prev, *slots;
 
-    ngx_shmtx_lock(&pool->mutex);
+    if (size >= ngx_slab_max_size) {
 
-    if (size >= ngx_slab_max_size) {
+        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
+                       "slab alloc: %uz", size);
+
         page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1)
                                           >> ngx_pagesize_shift);
         if (page) {
@@ -186,14 +203,9 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
     slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));
     page = slots[slot].next;
 
-#if 0
-    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
-                   "slab alloc: page %p next: %p", page, page->next);
-#endif
-
     if (page->next != page) {
 
-        if (size < ngx_slab_exact_size) {
+        if (shift < ngx_slab_exact_shift) {
 
             do {
                 p = (page - pool->pages) << ngx_pagesize_shift;
@@ -212,7 +224,9 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
                             }
 
                             bitmap[n] |= m;
-                            i <<= shift;
+
+                            i = ((n * sizeof(uintptr_t) * 8) << shift)
+                                + (i << shift);
 
                             if (bitmap[n] == NGX_SLAB_BUSY) {
                                 for (n = n + 1; n < map; n++) {
@@ -243,7 +257,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
             } while (page);
 
-        } else if (size == ngx_slab_exact_size) {
+        } else if (shift == ngx_slab_exact_shift) {
 
             do {
                 if (page->slab != NGX_SLAB_BUSY) {
@@ -277,7 +291,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
             } while (page);
 
-        } else { /* size < ngx_pagesize */
+        } else { /* shift > ngx_slab_exact_shift */
 
             n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);
             n = 1 << n;
@@ -324,7 +338,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
     page = ngx_slab_alloc_pages(pool, 1);
 
     if (page) {
-        if (size < ngx_slab_exact_size) {
+        if (shift < ngx_slab_exact_shift) {
             p = (page - pool->pages) << ngx_pagesize_shift;
             bitmap = (uintptr_t *) (pool->start + p);
 
@@ -354,7 +368,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
             goto done;
 
-        } else if (size == ngx_slab_exact_size) {
+        } else if (shift == ngx_slab_exact_shift) {
 
             page->slab = 1;
             page->next = &slots[slot];
@@ -367,7 +381,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
             goto done;
 
-        } else { /* size < ngx_pagesize */
+        } else { /* shift > ngx_slab_exact_shift */
 
             page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;
             page->next = &slots[slot];
@@ -386,8 +400,6 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
 done:
 
-    ngx_shmtx_unlock(&pool->mutex);
-
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p);
 
     return (void *) p;
@@ -397,6 +409,17 @@ done:
 void
 ngx_slab_free(ngx_slab_pool_t *pool, void *p)
 {
+    ngx_shmtx_lock(&pool->mutex);
+
+    ngx_slab_free_locked(pool, p);
+
+    ngx_shmtx_unlock(&pool->mutex);
+}
+
+
+void
+ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)
+{
     size_t            size;
     uintptr_t         slab, *bitmap;
     ngx_uint_t        n, m, type, slot, shift, map;
@@ -404,8 +427,6 @@ ngx_slab_free(ngx_slab_pool_t *pool, voi
 
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);
 
-    ngx_shmtx_lock(&pool->mutex);
-
     if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                       "ngx_slab_free(): outside of pool");
@@ -584,8 +605,6 @@ done:
 
     ngx_slab_junk(p, size);
 
-    ngx_shmtx_unlock(&pool->mutex);
-
     return;
 
 wrong_chunk:
@@ -602,8 +621,6 @@ chunk_already_free:
 
 fail:
 
-    ngx_shmtx_unlock(&pool->mutex);
-
     return;
 }
 
@@ -658,6 +675,7 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po
 
     ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM,
                       "ngx_slab_alloc(): failed");
+
     return NULL;
 }
 
@@ -674,12 +692,16 @@ ngx_slab_free_pages(ngx_slab_pool_t *poo
         ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));
     }
 
-    prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);
-    prev->next = page->next;
+    if (page->next) {
+        prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);
+        prev->next = page->next;
+        page->next->prev = page->prev;
+    }
 
+    page->prev = (uintptr_t) &pool->free;
     page->next = pool->free.next;
+
+    page->next->prev = (uintptr_t) page;
+
     pool->free.next = page;
-
-    page->prev = page->next->prev;
-    page->next->prev = (uintptr_t) page;
 }
--- a/src/core/ngx_slab.h
+++ b/src/core/ngx_slab.h
@@ -39,7 +39,9 @@ typedef struct {
 
 void ngx_slab_init(ngx_slab_pool_t *pool);
 void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);
+void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);
 void ngx_slab_free(ngx_slab_pool_t *pool, void *p);
+void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);
 
 
 #endif /* _NGX_SLAB_H_INCLUDED_ */
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -22,6 +22,17 @@ static void ngx_ssl_read_handler(ngx_eve
 static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
 static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
     ngx_err_t err, char *text);
+
+static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone,
+    void *data);
+static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
+    ngx_ssl_session_t *sess);
+static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
+    u_char *id, int len, int *copy);
+static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
+static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
+    ngx_slab_pool_t *shpool, ngx_uint_t n);
+
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
@@ -84,12 +95,18 @@ static long  ngx_ssl_protocols[] = {
 };
 
 
-int  ngx_connection_index;
+int  ngx_ssl_connection_index;
+int  ngx_ssl_server_conf_index;
+int  ngx_ssl_session_cache_index;
 
 
 ngx_int_t
 ngx_ssl_init(ngx_log_t *log)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x00907000
+    OPENSSL_config(NULL);
+#endif
+
     SSL_library_init();
     SSL_load_error_strings();
 
@@ -97,10 +114,26 @@ ngx_ssl_init(ngx_log_t *log)
     ENGINE_load_builtin_engines();
 #endif
 
-    ngx_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+    ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
+    if (ngx_ssl_connection_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
 
-    if (ngx_connection_index == -1) {
-        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
+    ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+                                                         NULL);
+    if (ngx_ssl_server_conf_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
+    ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+                                                           NULL);
+    if (ngx_ssl_session_cache_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
         return NGX_ERROR;
     }
 
@@ -109,7 +142,7 @@ ngx_ssl_init(ngx_log_t *log)
 
 
 ngx_int_t
-ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols)
+ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
 {
     ssl->ctx = SSL_CTX_new(SSLv23_method());
 
@@ -118,6 +151,12 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
         return NGX_ERROR;
     }
 
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        return NGX_ERROR;
+    }
+
     /* client side options */
 
     SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
@@ -260,7 +299,7 @@ ngx_http_ssl_verify_callback(int ok, X50
     name = X509_get_issuer_name(cert);
     issuer = name ? X509_NAME_oneline(name, NULL, 0) : "(none)";
 
-    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "verify:%d, error:%d, depth:%d, "
                    "subject:\"%s\",issuer: \"%s\"",
                    ok, err, depth, subject, issuer);
@@ -332,7 +371,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl
         SSL_set_accept_state(sc->connection);
     }
 
-    if (SSL_set_ex_data(sc->connection, ngx_connection_index, c) == 0) {
+    if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {
         ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
         return NGX_ERROR;
     }
@@ -1014,7 +1053,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev
         c->timedout = 1;
     }
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "SSL shutdown handler");
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler");
 
     if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
         return;
@@ -1100,6 +1139,433 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_
 }
 
 
+ngx_int_t
+ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
+    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
+{
+    long  cache_mode;
+
+    cache_mode = SSL_SESS_CACHE_SERVER;
+
+    if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {
+        cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;
+    }
+
+    SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);
+
+    SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len);
+
+    if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {
+
+        if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {
+            SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);
+        }
+
+        SSL_CTX_set_timeout(ssl->ctx, timeout);
+    }
+
+    if (shm_zone) {
+        shm_zone->init = ngx_ssl_session_cache_init;
+
+        SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
+        SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
+        SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
+
+        if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
+            == 0)
+        {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "SSL_CTX_set_ex_data() failed");
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+    ngx_slab_pool_t          *shpool;
+    ngx_rbtree_node_t        *sentinel;
+    ngx_ssl_session_cache_t  *cache;
+
+    if (data) {
+        shm_zone->data = data;
+        return NGX_OK;
+    }
+
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
+    if (cache == NULL) {
+        return NGX_ERROR;
+    }
+
+    cache->session_cache_head.prev = NULL;
+    cache->session_cache_head.next = &cache->session_cache_tail;
+
+    cache->session_cache_tail.prev = &cache->session_cache_head;
+    cache->session_cache_tail.next = NULL;
+
+    cache->session_rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
+    if (cache->session_rbtree == NULL) {
+        return NGX_ERROR;
+    }
+
+    sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_rbtree_sentinel_init(sentinel);
+
+    cache->session_rbtree->root = sentinel;
+    cache->session_rbtree->sentinel = sentinel;
+    cache->session_rbtree->insert = ngx_rbtree_insert_value;
+
+    shm_zone->data = cache;
+
+    return NGX_OK;
+}
+
+
+/*
+ * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
+ * so they are outside the code locked by shared pool mutex
+ */
+
+static int
+ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
+{
+    int                       len;
+    u_char                   *p, *id;
+    uint32_t                  hash;
+    SSL_CTX                  *ssl_ctx;
+    ngx_time_t               *tp;
+    ngx_shm_zone_t           *shm_zone;
+    ngx_connection_t         *c;
+    ngx_slab_pool_t          *shpool;
+    ngx_ssl_sess_id_t        *sess_id;
+    ngx_ssl_cached_sess_t    *cached_sess;
+    ngx_ssl_session_cache_t  *cache;
+    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];
+
+    len = i2d_SSL_SESSION(sess, NULL);
+
+    /* do not cache too big session */
+
+    if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
+        return 0;
+    }
+
+    p = buf;
+    i2d_SSL_SESSION(sess, &p);
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
+    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
+
+    cache = shm_zone->data;
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    /* drop one or two expired sessions */
+    ngx_ssl_expire_sessions(cache, shpool, 1);
+
+    cached_sess = ngx_slab_alloc_locked(shpool,
+                                  offsetof(ngx_ssl_cached_sess_t, asn1) + len);
+
+    if (cached_sess == NULL) {
+
+        /* drop the oldest non-expired session and try once more */
+
+        ngx_ssl_expire_sessions(cache, shpool, 0);
+
+        cached_sess = ngx_slab_alloc_locked(shpool,
+                                  offsetof(ngx_ssl_cached_sess_t, asn1) + len);
+
+        if (cached_sess == NULL) {
+            id = NULL;
+            goto failed;
+        }
+    }
+
+    id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
+    if (id == NULL) {
+        goto failed;
+    }
+
+    sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
+    if (sess_id == NULL) {
+        goto failed;
+    }
+
+    ngx_memcpy(&cached_sess->asn1[0], buf, len);
+
+    ngx_memcpy(id, sess->session_id, sess->session_id_length);
+
+    hash = ngx_crc32_short(sess->session_id, sess->session_id_length);
+
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "http ssl new session: %08XD:%d:%d",
+                   hash, sess->session_id_length, len);
+
+    sess_id->node.key = hash;
+    sess_id->node.data = (u_char) sess->session_id_length;
+    sess_id->id = id;
+    sess_id->len = len;
+    sess_id->session = cached_sess;
+
+    tp = ngx_timeofday();
+
+    cached_sess->expire = tp->sec + SSL_CTX_get_timeout(ssl_ctx);
+    cached_sess->sess_id = sess_id;
+
+    cached_sess->next = cache->session_cache_head.next;
+    cached_sess->next->prev = cached_sess;
+    cached_sess->prev = &cache->session_cache_head;
+    cache->session_cache_head.next = cached_sess;
+
+    ngx_rbtree_insert(cache->session_rbtree, &sess_id->node);
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    return 0;
+
+failed:
+
+    if (cached_sess) {
+        ngx_slab_free_locked(shpool, cached_sess);
+    }
+
+    if (id) {
+        ngx_slab_free_locked(shpool, id);
+    }
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                  "could not add new SSL session to the session cache");
+
+    return 0;
+}
+
+
+static ngx_ssl_session_t *
+ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,
+    int *copy)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+    const
+#endif
+    u_char                   *p;
+    uint32_t                  hash;
+    ngx_time_t               *tp;
+    ngx_shm_zone_t           *shm_zone;
+    ngx_slab_pool_t          *shpool;
+    ngx_connection_t         *c;
+    ngx_rbtree_node_t        *node, *sentinel;
+    ngx_ssl_session_t        *sess;
+    ngx_ssl_sess_id_t        *sess_id;
+    ngx_ssl_cached_sess_t    *cached_sess;
+    ngx_ssl_session_cache_t  *cache;
+    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    hash = ngx_crc32_short(id, len);
+    *copy = 0;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "http ssl get session: %08XD:%d", hash, len);
+
+    shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
+                                   ngx_ssl_session_cache_index);
+
+    cache = shm_zone->data;
+
+    if (cache->session_rbtree == NULL) {
+        return NULL;
+    }
+
+    sess = NULL;
+
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    node = cache->session_rbtree->root;
+    sentinel = cache->session_rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        if (hash == node->key && (u_char) len == node->data) {
+            sess_id = (ngx_ssl_sess_id_t *) node;
+
+            if (ngx_strncmp(id, sess_id->id, len) == 0) {
+
+                cached_sess = sess_id->session;
+
+                tp = ngx_timeofday();
+
+                if (cached_sess->expire > tp->sec) {
+                    ngx_memcpy(buf, &cached_sess->asn1[0], sess_id->len);
+
+                    ngx_shmtx_unlock(&shpool->mutex);
+
+                    p = buf;
+                    sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
+
+                    return sess;
+                }
+
+                cached_sess->next->prev = cached_sess->prev;
+                cached_sess->prev->next = cached_sess->next;
+
+                ngx_rbtree_delete(cache->session_rbtree, node);
+
+                ngx_slab_free_locked(shpool, cached_sess);
+                ngx_slab_free_locked(shpool, sess_id->id);
+                ngx_slab_free_locked(shpool, sess_id);
+
+                sess = NULL;
+
+                break;
+            }
+        }
+
+        node = node->right;
+    }
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    return sess;
+}
+
+
+static void
+ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
+{
+    u_char                   *id, len;
+    uint32_t                  hash;
+    ngx_shm_zone_t           *shm_zone;
+    ngx_slab_pool_t          *shpool;
+    ngx_rbtree_node_t        *node, *sentinel;
+    ngx_ssl_sess_id_t        *sess_id;
+    ngx_ssl_cached_sess_t    *cached_sess;
+    ngx_ssl_session_cache_t  *cache;
+
+    shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);
+
+    cache = shm_zone->data;
+
+    id = sess->session_id;
+    len = (u_char) sess->session_id_length;
+
+    hash = ngx_crc32_short(id, (size_t) len);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+                   "http ssl remove session: %08XD:%d", hash, len);
+
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    node = cache->session_rbtree->root;
+    sentinel = cache->session_rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        if (hash == node->key && len == node->data) {
+            sess_id = (ngx_ssl_sess_id_t *) node;
+
+            if (ngx_strncmp(id, sess_id->id, (size_t) len) == 0) {
+
+                cached_sess = sess_id->session;
+
+                cached_sess->next->prev = cached_sess->prev;
+                cached_sess->prev->next = cached_sess->next;
+
+                ngx_rbtree_delete(cache->session_rbtree, node);
+
+                ngx_slab_free_locked(shpool, cached_sess);
+                ngx_slab_free_locked(shpool, sess_id->id);
+                ngx_slab_free_locked(shpool, sess_id);
+
+                break;
+            }
+        }
+
+        node = node->right;
+    }
+
+    ngx_shmtx_unlock(&shpool->mutex);
+}
+
+
+static void
+ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
+    ngx_slab_pool_t *shpool, ngx_uint_t n)
+{
+    ngx_time_t                  *tp;
+    ngx_ssl_sess_id_t      *sess_id;
+    ngx_ssl_cached_sess_t  *sess;
+
+    tp = ngx_timeofday();
+
+    while (n < 3) {
+
+        sess = cache->session_cache_tail.prev;
+
+        if (sess == &cache->session_cache_head) {
+            return;
+        }
+
+        if (n++ != 0 && sess->expire > tp->sec) {
+            break;
+        }
+
+        sess->next->prev = sess->prev;
+        sess->prev->next = sess->next;
+
+        sess_id = sess->sess_id;
+
+        ngx_rbtree_delete(cache->session_rbtree, &sess_id->node);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+                       "expire session: %08Xi", sess_id->node.key);
+
+        ngx_slab_free_locked(shpool, sess);
+        ngx_slab_free_locked(shpool, sess_id->id);
+        ngx_slab_free_locked(shpool, sess_id);
+    }
+}
+
+
 void
 ngx_ssl_cleanup_ctx(void *data)
 {
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -15,6 +15,7 @@
 #include <openssl/err.h>
 
 #if OPENSSL_VERSION_NUMBER >= 0x00907000
+#include <openssl/conf.h>
 #include <openssl/engine.h>
 #define NGX_SSL_ENGINE   1
 #endif
@@ -50,6 +51,41 @@ typedef struct {
 } ngx_ssl_connection_t;
 
 
+#define NGX_SSL_DFLT_BUILTIN_SCACHE  -2
+#define NGX_SSL_NO_BUILTIN_SCACHE    -3
+
+
+typedef struct ngx_ssl_cached_sess_s  ngx_ssl_cached_sess_t;
+
+
+#define NGX_SSL_MAX_SESSION_SIZE (4096 - offsetof(ngx_ssl_cached_sess_t, asn1))
+
+
+typedef struct {
+    ngx_rbtree_node_t           node;
+    u_char                     *id;
+    size_t                      len;
+    ngx_ssl_cached_sess_t      *session;
+} ngx_ssl_sess_id_t;
+
+
+struct ngx_ssl_cached_sess_s {
+    ngx_ssl_cached_sess_t      *prev;
+    ngx_ssl_cached_sess_t      *next;
+    time_t                      expire;
+    ngx_ssl_sess_id_t          *sess_id;
+    u_char                      asn1[1];
+};
+
+
+typedef struct {
+    ngx_rbtree_t               *session_rbtree;
+    ngx_ssl_cached_sess_t       session_cache_head;
+    ngx_ssl_cached_sess_t       session_cache_tail;
+} ngx_ssl_session_cache_t;
+
+
+
 #define NGX_SSL_SSLv2    2
 #define NGX_SSL_SSLv3    4
 #define NGX_SSL_TLSv1    8
@@ -62,19 +98,24 @@ typedef struct {
 
 
 ngx_int_t ngx_ssl_init(ngx_log_t *log);
-ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols);
+ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_str_t *key);
 ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_int_t depth);
 ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl);
+ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
+    ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);
 
 ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);
 #define ngx_ssl_get_session(c)      SSL_get1_session(c->ssl->connection)
 #define ngx_ssl_free_session        SSL_SESSION_free
-#define ngx_ssl_get_connection(sc)  SSL_get_ex_data(sc, ngx_connection_index)
+#define ngx_ssl_get_connection(ssl_conn)                                      \
+    SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)
+#define ngx_ssl_get_server_conf(ssl_ctx)                                      \
+    SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)
 
 
 ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,
@@ -89,8 +130,6 @@ ngx_int_t ngx_ssl_get_serial_number(ngx_
     ngx_str_t *s);
 
 
-
-
 ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
 ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
 ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
@@ -103,7 +142,9 @@ void ngx_cdecl ngx_ssl_error(ngx_uint_t 
 void ngx_ssl_cleanup_ctx(void *data);
 
 
-extern int  ngx_connection_index;
+extern int  ngx_ssl_connection_index;
+extern int  ngx_ssl_server_conf_index;
+extern int  ngx_ssl_session_cache_index;
 
 
 #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -19,6 +19,11 @@ typedef struct {
 
 
 static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_dav_no_init(ngx_tree_ctx_t *ctx,
+    ngx_tree_ctx_t *prev);
+static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
 static void ngx_http_dav_put_handler(ngx_http_request_t *r);
 static ngx_int_t ngx_http_dav_error(ngx_http_request_t *, ngx_err_t err,
     ngx_int_t not_found, char *failed, u_char *path);
@@ -105,6 +110,7 @@ ngx_http_dav_handler(ngx_http_request_t 
     size_t                    root;
     ngx_int_t                 rc;
     ngx_str_t                 path;
+    ngx_tree_ctx_t            tree;
     ngx_file_info_t           fi;
     ngx_table_elt_t          *depth;
     ngx_http_dav_loc_conf_t  *dlcf;
@@ -176,8 +182,23 @@ ngx_http_dav_handler(ngx_http_request_t 
                     return NGX_HTTP_BAD_REQUEST;
                 }
 
-                if (ngx_delete_dir(path.data) != NGX_FILE_ERROR) {
-                    return NGX_HTTP_NO_CONTENT;
+                path.len -= 2;
+
+                tree.init_handler = ngx_http_dav_no_init;
+                tree.file_handler = ngx_http_dav_delete_file;
+                tree.pre_tree_handler = ngx_http_dav_noop;
+                tree.post_tree_handler = ngx_http_dav_delete_dir;
+                tree.spec_handler = ngx_http_dav_delete_file;
+                tree.data = NULL;
+                tree.size = 0;
+                tree.log = r->connection->log;
+
+                if (ngx_walk_tree(&tree, &path) == NGX_OK) {
+
+                    if (ngx_delete_dir(path.data) != NGX_FILE_ERROR) {
+                        return NGX_HTTP_NO_CONTENT;
+                    }
+
                 }
 
                 failed = ngx_delete_dir_n;
@@ -248,6 +269,44 @@ ngx_http_dav_handler(ngx_http_request_t 
 }
 
 
+static ngx_int_t
+ngx_http_dav_no_init(ngx_tree_ctx_t *ctx, ngx_tree_ctx_t *prev)
+{
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+                   "http delete dir: \"%s\"", path->data);
+
+    ngx_delete_dir(path->data);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+                   "http delete file: \"%s\"", path->data);
+
+    ngx_delete_file(path->data);
+
+    return NGX_OK;
+}
+
+
 static void
 ngx_http_dav_put_handler(ngx_http_request_t *r)
 {
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -140,6 +140,10 @@ ngx_http_index_handler(ngx_http_request_
         return NGX_DECLINED;
     }
 
+    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
+        return NGX_DECLINED;
+    }
+
     /* TODO: Win32 */
     if (r->zero_in_uri) {
         return NGX_DECLINED;
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -0,0 +1,444 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    u_char              color;
+    u_char              len;
+    u_short             conn;
+    u_char              data[1];
+} ngx_http_limit_zone_node_t;
+
+
+typedef struct {
+    ngx_shm_zone_t     *shm_zone;
+    ngx_rbtree_node_t  *node;
+} ngx_http_limit_zone_cleanup_t;
+
+
+typedef struct {
+    ngx_rbtree_t       *rbtree;
+    ngx_int_t           index;
+    ngx_str_t           var;
+} ngx_http_limit_zone_ctx_t;
+
+
+typedef struct {
+    ngx_shm_zone_t     *shm_zone;
+    ngx_uint_t          conn;
+} ngx_http_limit_zone_conf_t;
+
+
+static void ngx_http_limit_zone_cleanup(void *data);
+
+static void *ngx_http_limit_zone_create_conf(ngx_conf_t *cf);
+static char *ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static ngx_int_t ngx_http_limit_zone_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_limit_zone_commands[] = {
+
+    { ngx_string("limit_zone"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+      ngx_http_limit_zone,
+      0,
+      0,
+      NULL },
+
+    { ngx_string("limit_conn"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+      ngx_http_limit_conn,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_limit_zone_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_limit_zone_init,              /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_limit_zone_create_conf,       /* create location configration */
+    ngx_http_limit_zone_merge_conf         /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_limit_zone_module = {
+    NGX_MODULE_V1,
+    &ngx_http_limit_zone_module_ctx,       /* module context */
+    ngx_http_limit_zone_commands,          /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_limit_zone_handler(ngx_http_request_t *r)
+{
+    size_t                          len, n;
+    uint32_t                        hash;
+    ngx_slab_pool_t                *shpool;
+    ngx_rbtree_node_t              *node, *sentinel;
+    ngx_pool_cleanup_t             *cln;
+    ngx_http_variable_value_t      *vv;
+    ngx_http_limit_zone_ctx_t      *ctx;
+    ngx_http_limit_zone_node_t     *lz;
+    ngx_http_limit_zone_conf_t     *lzcf;
+    ngx_http_limit_zone_cleanup_t  *lzcln;
+
+    if (r->main->limit_zone_set) {
+        return NGX_DECLINED;
+    }
+
+    lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module);
+
+    if (lzcf->shm_zone == NULL) {
+        return NGX_DECLINED;
+    }
+
+    ctx = lzcf->shm_zone->data;
+
+    vv = ngx_http_get_indexed_variable(r, ctx->index);
+
+    if (vv == NULL || vv->not_found) {
+        return NGX_DECLINED;
+    }
+
+    r->limit_zone_set = 1;
+
+    len = vv->len;
+
+    hash = ngx_crc32_short(vv->data, len);
+
+    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t));
+    if (cln == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr;
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    node = ctx->rbtree->root;
+    sentinel = ctx->rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        if (hash == node->key ){
+            lz = (ngx_http_limit_zone_node_t *) &node->color;
+
+            if (len == (size_t) lz->len
+                && ngx_strncmp(lz->data, vv->data, len) == 0)
+            {
+                if ((ngx_uint_t) lz->conn < lzcf->conn) {
+                    lz->conn++;
+                    goto done;
+                }
+
+                ngx_shmtx_unlock(&shpool->mutex);
+
+                return NGX_HTTP_SERVICE_UNAVAILABLE;
+            }
+        }
+    }
+
+    n = offsetof(ngx_rbtree_node_t, color)
+        + offsetof(ngx_http_limit_zone_node_t, data)
+        + len;
+
+    node = ngx_slab_alloc_locked(shpool, n);
+    if (node == NULL) {
+        ngx_shmtx_unlock(&shpool->mutex);
+        return NGX_HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    lz = (ngx_http_limit_zone_node_t *) &node->color;
+
+    node->key = hash;
+    lz->len = (u_char) len;
+    lz->conn = 1;
+    ngx_memcpy(lz->data, vv->data, len);
+
+    ngx_rbtree_insert(ctx->rbtree, node);
+
+done:
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "limit zone: %08XD %d", node->key, lz->conn);
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    cln->handler = ngx_http_limit_zone_cleanup;
+    lzcln = cln->data;
+
+    lzcln->shm_zone = lzcf->shm_zone;
+    lzcln->node = node;
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_zone_cleanup(void *data)
+{
+    ngx_http_limit_zone_cleanup_t  *lzcln = data;
+
+    ngx_slab_pool_t             *shpool;
+    ngx_rbtree_node_t           *node;
+    ngx_http_limit_zone_ctx_t   *ctx;
+    ngx_http_limit_zone_node_t  *lz;
+
+    ctx = lzcln->shm_zone->data;
+    shpool = (ngx_slab_pool_t *) lzcln->shm_zone->shm.addr;
+    node = lzcln->node;
+    lz = (ngx_http_limit_zone_node_t *) &node->color;
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lzcln->shm_zone->shm.log, 0,
+                   "limit zone cleanup: %08XD %d", node->key, lz->conn);
+
+    lz->conn--;
+
+    if (lz->conn == 0) {
+        ngx_rbtree_delete(ctx->rbtree, node);
+        ngx_slab_free_locked(shpool, node);
+    }
+
+    ngx_shmtx_unlock(&shpool->mutex);
+}
+
+
+static ngx_int_t
+ngx_http_limit_zone_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+    ngx_http_limit_zone_ctx_t  *octx = data;
+
+    ngx_slab_pool_t            *shpool;
+    ngx_rbtree_node_t          *sentinel;
+    ngx_http_limit_zone_ctx_t  *ctx;
+
+    ctx = shm_zone->data;
+
+    if (octx) {
+        if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
+            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+                          "limit_zone \"%V\" use the \"%V\" variable "
+                          "while previously it used the \"%V\" variable",
+                          &shm_zone->name, &ctx->var, &octx->var);
+            return NGX_ERROR;
+        }
+
+        ctx->rbtree = octx->rbtree;
+
+        return NGX_OK;
+    }
+
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
+    if (ctx->rbtree == NULL) {
+        return NGX_ERROR;
+    }
+
+    sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_rbtree_sentinel_init(sentinel);
+
+    ctx->rbtree->root = sentinel;
+    ctx->rbtree->sentinel = sentinel;
+    ctx->rbtree->insert = ngx_rbtree_insert_value;
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_http_limit_zone_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_limit_zone_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->shm_zone = NULL;
+     *     conf->conn = 0;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_limit_zone_conf_t *prev = parent;
+    ngx_http_limit_zone_conf_t *conf = child;
+
+    if (conf->shm_zone == NULL) {
+        *conf = *prev;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ssize_t                     n;
+    ngx_str_t                  *value;
+    ngx_shm_zone_t             *shm_zone;
+    ngx_http_limit_zone_ctx_t  *ctx;
+
+    value = cf->args->elts;
+
+    if (value[2].data[0] != '$') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid variable name \"%V\"", &value[2]);
+        return NGX_CONF_ERROR;
+    }
+
+    value[2].len--;
+    value[2].data++;
+
+    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_ctx_t));
+    if (ctx == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->index = ngx_http_get_variable_index(cf, &value[2]);
+    if (ctx->index == NGX_ERROR) {
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->var = value[2];
+
+    n = ngx_parse_size(&value[3]);
+
+    if (n == NGX_ERROR) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid size of limit_zone \"%V\"", &value[3]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "limit_zone \"%V\" is too small", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+
+    shm_zone = ngx_shared_memory_add(cf, &value[1], n,
+                                     &ngx_http_limit_zone_module);
+    if (shm_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (shm_zone->data) {
+        ctx = shm_zone->data;
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                        "limit_zone \"%V\" is already bound to variable \"%V\"",
+                        &value[1], &ctx->var);
+        return NGX_CONF_ERROR;
+    }
+
+    shm_zone->init = ngx_http_limit_zone_init_zone;
+    shm_zone->data = ctx;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_limit_zone_conf_t  *lzcf = conf;
+
+    ngx_int_t   n;
+    ngx_str_t  *value;
+
+    value = cf->args->elts;
+
+    lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
+                                           &ngx_http_limit_zone_module);
+    if (lzcf->shm_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    n = ngx_atoi(value[2].data, value[2].len);
+    if (n <= 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid number of connections \"%V\"", &value[2]);
+        return NGX_CONF_ERROR;
+    }
+
+    lzcf->conn = n;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_limit_zone_init(ngx_conf_t *cf)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_limit_zone_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -2130,7 +2130,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         plcf->upstream.ssl->log = cf->log;
 
         if (ngx_ssl_create(plcf->upstream.ssl,
-                           NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)
+                           NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -45,7 +45,16 @@
 
 
 typedef struct {
-    ngx_str_t  boundary_header;
+    off_t        start;
+    off_t        end;
+    ngx_str_t    content_range;
+} ngx_http_range_t;
+
+
+typedef struct {
+    off_t        offset;
+    ngx_str_t    boundary_header;
+    ngx_array_t  ranges;
 } ngx_http_range_filter_ctx_t;
 
 
@@ -159,8 +168,13 @@ ngx_http_range_header_filter(ngx_http_re
         return ngx_http_next_header_filter(r);
     }
 
-    if (ngx_array_init(&r->headers_out.ranges, r->pool, 2,
-                                        sizeof(ngx_http_range_t)) == NGX_ERROR)
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
+        == NGX_ERROR)
     {
         return NGX_ERROR;
     }
@@ -201,7 +215,7 @@ ngx_http_range_header_filter(ngx_http_re
             while (*p == ' ') { p++; }
 
             if (*p == ',' || *p == '\0') {
-                range = ngx_array_push(&r->headers_out.ranges);
+                range = ngx_array_push(&ctx->ranges);
                 if (range == NULL) {
                     return NGX_ERROR;
                 }
@@ -247,7 +261,7 @@ ngx_http_range_header_filter(ngx_http_re
             break;
         }
 
-        range = ngx_array_push(&r->headers_out.ranges);
+        range = ngx_array_push(&ctx->ranges);
         if (range == NULL) {
             return NGX_ERROR;
         }
@@ -275,7 +289,6 @@ ngx_http_range_header_filter(ngx_http_re
         /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */
 
         r->headers_out.status = rc;
-        r->headers_out.ranges.nelts = 0;
 
         content_range = ngx_list_push(&r->headers_out.headers);
         if (content_range == NULL) {
@@ -304,9 +317,11 @@ ngx_http_range_header_filter(ngx_http_re
         return rc;
     }
 
+    ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
+
     r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
 
-    if (r->headers_out.ranges.nelts == 1) {
+    if (ctx->ranges.nelts == 1) {
 
         content_range = ngx_list_push(&r->headers_out.headers);
         if (content_range == NULL) {
@@ -335,20 +350,17 @@ ngx_http_range_header_filter(ngx_http_re
 
         r->headers_out.content_length_n = range->end - range->start;
 
+        if (r->headers_out.content_length) {
+            r->headers_out.content_length->hash = 0;
+            r->headers_out.content_length = NULL;
+        }
+
         return ngx_http_next_header_filter(r);
     }
 
 
     /* TODO: what if no content_type ?? */
 
-    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
-    if (ctx == NULL) {
-        return NGX_ERROR;
-    }
-
-    ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
-
-
     len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
           + sizeof(CRLF "Content-Type: ") - 1
           + r->headers_out.content_type.len
@@ -417,8 +429,8 @@ ngx_http_range_header_filter(ngx_http_re
 
     len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
 
-    range = r->headers_out.ranges.elts;
-    for (i = 0; i < r->headers_out.ranges.nelts; i++) {
+    range = ctx->ranges.elts;
+    for (i = 0; i < ctx->ranges.nelts; i++) {
 
         /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
 
@@ -440,7 +452,11 @@ ngx_http_range_header_filter(ngx_http_re
     }
 
     r->headers_out.content_length_n = len;
-    r->headers_out.content_length = NULL;
+
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+        r->headers_out.content_length = NULL;
+    }
 
     return ngx_http_next_header_filter(r);
 }
@@ -449,106 +465,105 @@ ngx_http_range_header_filter(ngx_http_re
 static ngx_int_t
 ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
+    off_t                         start, last;
+    ngx_buf_t                    *b, *buf;
     ngx_uint_t                    i;
-    ngx_buf_t                    *b;
     ngx_chain_t                  *out, *hcl, *rcl, *dcl, **ll;
     ngx_http_range_t             *range;
     ngx_http_range_filter_ctx_t  *ctx;
 
-    if (r->headers_out.ranges.nelts == 0) {
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
+
+    if (ctx == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    buf = in->buf;
+
+    if (ngx_buf_special(in->buf)) {
         return ngx_http_next_body_filter(r, in);
     }
 
+    if (ctx->offset) {
+        goto overlapped;
+    }
+
+    range = ctx->ranges.elts;
+
+    if (!buf->last_buf) {
+
+        if (buf->in_file) {
+            start = buf->file_pos + ctx->offset;
+            last = buf->file_last + ctx->offset;
+
+        } else {
+            start = buf->pos - buf->start + ctx->offset;
+            last = buf->last - buf->start + ctx->offset;
+        }
+
+        for (i = 0; i < ctx->ranges.nelts; i++) {
+            if (start > range[i].start || last < range[i].end) {
+                 goto overlapped;
+            }
+        }
+    }
+
     /*
-     * the optimized version for the static files only
-     * that are passed in the single file buf
+     * the optimized version for the responses
+     * that are passed in the single buffer
      */
 
-    if (in && in->buf->in_file && in->buf->last_buf) {
-        range = r->headers_out.ranges.elts;
+    ctx->offset = ngx_buf_size(buf);
+
+    if (ctx->ranges.nelts == 1) {
 
-        if (r->headers_out.ranges.nelts == 1) {
-            in->buf->file_pos = range->start;
-            in->buf->file_last = range->end;
+        if (buf->in_file) {
+            buf->file_pos = range->start;
+            buf->file_last = range->end;
+        }
 
-            return ngx_http_next_body_filter(r, in);
+        if (ngx_buf_in_memory(buf)) {
+            buf->pos = buf->start + (size_t) range->start;
+            buf->last = buf->start + (size_t) range->end;
         }
 
-        ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
-        ll = &out;
+        return ngx_http_next_body_filter(r, in);
+    }
 
-        for (i = 0; i < r->headers_out.ranges.nelts; i++) {
+    ll = &out;
+
+    for (i = 0; i < ctx->ranges.nelts; i++) {
 
-            /*
-             * The boundary header of the range:
-             * CRLF
-             * "--0123456789" CRLF
-             * "Content-Type: image/jpeg" CRLF
-             * "Content-Range: bytes "
-             */
+        /*
+         * The boundary header of the range:
+         * CRLF
+         * "--0123456789" CRLF
+         * "Content-Type: image/jpeg" CRLF
+         * "Content-Range: bytes "
+         */
 
-            b = ngx_calloc_buf(r->pool);
-            if (b == NULL) {
-                return NGX_ERROR;
-            }
+        b = ngx_calloc_buf(r->pool);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
 
-            b->memory = 1;
-            b->pos = ctx->boundary_header.data;
-            b->last = ctx->boundary_header.data + ctx->boundary_header.len;
+        b->memory = 1;
+        b->pos = ctx->boundary_header.data;
+        b->last = ctx->boundary_header.data + ctx->boundary_header.len;
 
-            hcl = ngx_alloc_chain_link(r->pool);
-            if (hcl == NULL) {
-                return NGX_ERROR;
-            }
+        hcl = ngx_alloc_chain_link(r->pool);
+        if (hcl == NULL) {
+            return NGX_ERROR;
+        }
 
-            hcl->buf = b;
+        hcl->buf = b;
 
 
-            /* "SSSS-EEEE/TTTT" CRLF CRLF */
-
-            b = ngx_calloc_buf(r->pool);
-            if (b == NULL) {
-                return NGX_ERROR;
-            }
-
-            b->temporary = 1;
-            b->pos = range[i].content_range.data;
-            b->last = range[i].content_range.data + range[i].content_range.len;
-
-            rcl = ngx_alloc_chain_link(r->pool);
-            if (rcl == NULL) {
-                return NGX_ERROR;
-            }
-
-            rcl->buf = b;
-
-
-            /* the range data */
-
-            b = ngx_calloc_buf(r->pool);
-            if (b == NULL) {
-                return NGX_ERROR;
-            }
-
-            b->in_file = 1;
-            b->file_pos = range[i].start;
-            b->file_last = range[i].end;
-            b->file = in->buf->file;
-
-            dcl = ngx_alloc_chain_link(r->pool);
-            if (dcl == NULL) {
-                return NGX_ERROR;
-            }
-
-            dcl->buf = b;
-
-            *ll = hcl;
-            hcl->next = rcl;
-            rcl->next = dcl;
-            ll = &dcl->next;
-        }
-
-        /* the last boundary CRLF "--0123456789--" CRLF  */
+        /* "SSSS-EEEE/TTTT" CRLF CRLF */
 
         b = ngx_calloc_buf(r->pool);
         if (b == NULL) {
@@ -556,34 +571,91 @@ ngx_http_range_body_filter(ngx_http_requ
         }
 
         b->temporary = 1;
-        b->last_buf = 1;
+        b->pos = range[i].content_range.data;
+        b->last = range[i].content_range.data + range[i].content_range.len;
+
+        rcl = ngx_alloc_chain_link(r->pool);
+        if (rcl == NULL) {
+            return NGX_ERROR;
+        }
+
+        rcl->buf = b;
+
+
+        /* the range data */
+
+        b = ngx_calloc_buf(r->pool);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
 
-        b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
-                                     + sizeof("--" CRLF) - 1);
-        if (b->pos == NULL) {
+        b->in_file = buf->in_file;
+        b->temporary = buf->temporary;
+        b->memory = buf->memory;
+        b->mmap = buf->mmap;
+        b->file = buf->file;
+
+        if (buf->in_file) {
+            buf->file_pos = range[i].start;
+            buf->file_last = range[i].end;
+        }
+
+        if (ngx_buf_in_memory(buf)) {
+            buf->pos = buf->start + (size_t) range[i].start;
+            buf->last = buf->start + (size_t) range[i].end;
+        }
+
+        dcl = ngx_alloc_chain_link(r->pool);
+        if (dcl == NULL) {
             return NGX_ERROR;
         }
 
-        b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10);
-        *b->last++ = '-'; *b->last++ = '-';
-        *b->last++ = CR; *b->last++ = LF;
-
-        hcl = ngx_alloc_chain_link(r->pool);
-        if (hcl == NULL) {
-            return NGX_ERROR;
-        }
-
-        hcl->buf = b;
-        hcl->next = NULL;
+        dcl->buf = b;
 
         *ll = hcl;
+        hcl->next = rcl;
+        rcl->next = dcl;
+        ll = &dcl->next;
+    }
 
-        return ngx_http_next_body_filter(r, out);
+    /* the last boundary CRLF "--0123456789--" CRLF  */
+
+    b = ngx_calloc_buf(r->pool);
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    b->temporary = 1;
+    b->last_buf = 1;
+
+    b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+                                 + sizeof("--" CRLF) - 1);
+    if (b->pos == NULL) {
+        return NGX_ERROR;
     }
 
-    /* TODO: alert */
+    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10);
+    *b->last++ = '-'; *b->last++ = '-';
+    *b->last++ = CR; *b->last++ = LF;
+
+    hcl = ngx_alloc_chain_link(r->pool);
+    if (hcl == NULL) {
+        return NGX_ERROR;
+    }
 
-    return ngx_http_next_body_filter(r, in);
+    hcl->buf = b;
+    hcl->next = NULL;
+
+    *ll = hcl;
+
+    return ngx_http_next_body_filter(r, out);
+
+overlapped:
+
+    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                  "range in overlapped buffers");
+
+    return NGX_ERROR;
 }
 
 
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -103,18 +103,18 @@ ngx_http_realip_handler(ngx_http_request
     ngx_http_realip_loc_conf_t  *rlcf;
 
     if (r->realip_set) {
-        return NGX_OK;
+        return NGX_DECLINED;
     }
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
 
     if (rlcf->from == NULL) {
-        return NGX_OK;
+        return NGX_DECLINED;
     }
 
     if (rlcf->xfwd == 0) {
         if (r->headers_in.x_real_ip == NULL) {
-            return NGX_OK;
+            return NGX_DECLINED;
         }
 
         len = r->headers_in.x_real_ip->value.len;
@@ -122,7 +122,7 @@ ngx_http_realip_handler(ngx_http_request
 
     } else {
         if (r->headers_in.x_forwarded_for == NULL) {
-            return NGX_OK;
+            return NGX_DECLINED;
         }
 
         len = r->headers_in.x_forwarded_for->value.len;
@@ -158,11 +158,11 @@ ngx_http_realip_handler(ngx_http_request
 
             r->realip_set = 1;
 
-            return NGX_OK;
+            return NGX_DECLINED;
         }
     }
 
-    return NGX_OK;
+    return NGX_DECLINED;
 }
 
 
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -28,6 +28,9 @@ static void *ngx_http_ssl_create_srv_con
 static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
     void *parent, void *child);
 
+static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
 
 static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -115,6 +118,13 @@ static ngx_command_t  ngx_http_ssl_comma
       ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 },
 #endif
 
+    { ngx_string("ssl_session_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
+      ngx_http_ssl_session_cache,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("ssl_session_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_sec_slot,
@@ -178,7 +188,7 @@ static ngx_http_variable_t  ngx_http_ssl
 };
 
 
-static u_char ngx_http_session_id_ctx[] = "HTTP";
+static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
 
 
 static ngx_int_t
@@ -257,35 +267,36 @@ ngx_http_ssl_add_variables(ngx_conf_t *c
 static void *
 ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
 {
-    ngx_http_ssl_srv_conf_t  *scf;
+    ngx_http_ssl_srv_conf_t  *sscf;
 
-    scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
-    if (scf == NULL) {
+    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
+    if (sscf == NULL) {
         return NGX_CONF_ERROR;
     }
 
     /*
      * set by ngx_pcalloc():
      *
-     *     scf->protocols = 0;
-
-     *     scf->certificate.len = 0;
-     *     scf->certificate.data = NULL;
-     *     scf->certificate_key.len = 0;
-     *     scf->certificate_key.data = NULL;
-     *     scf->client_certificate.len = 0;
-     *     scf->client_certificate.data = NULL;
-     *     scf->ciphers.len = 0;
-     *     scf->ciphers.data = NULL;
+     *     sscf->protocols = 0;
+     *     sscf->certificate.len = 0;
+     *     sscf->certificate.data = NULL;
+     *     sscf->certificate_key.len = 0;
+     *     sscf->certificate_key.data = NULL;
+     *     sscf->client_certificate.len = 0;
+     *     sscf->client_certificate.data = NULL;
+     *     sscf->ciphers.len = 0;
+     *     sscf->ciphers.data = NULL;
+     *     sscf->shm_zone = NULL;
      */
 
-    scf->enable = NGX_CONF_UNSET;
-    scf->session_timeout = NGX_CONF_UNSET;
-    scf->verify = NGX_CONF_UNSET;
-    scf->verify_depth = NGX_CONF_UNSET;
-    scf->prefer_server_ciphers = NGX_CONF_UNSET;
+    sscf->enable = NGX_CONF_UNSET;
+    sscf->verify = NGX_CONF_UNSET;
+    sscf->verify_depth = NGX_CONF_UNSET;
+    sscf->prefer_server_ciphers = NGX_CONF_UNSET;
+    sscf->builtin_session_cache = NGX_CONF_UNSET;
+    sscf->session_timeout = NGX_CONF_UNSET;
 
-    return scf;
+    return sscf;
 }
 
 
@@ -330,7 +341,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
 
     conf->ssl.log = cf->log;
 
-    if (ngx_ssl_create(&conf->ssl, conf->protocols) != NGX_OK) {
+    if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -343,7 +354,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     cln->data = &conf->ssl;
 
     if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
-                            &conf->certificate_key) != NGX_OK)
+                            &conf->certificate_key)
+        != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
@@ -359,7 +371,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
 
     if (conf->verify) {
         if (ngx_ssl_client_certificate(cf, &conf->ssl,
-                                 &conf->client_certificate, conf->verify_depth)
+                                       &conf->client_certificate,
+                                       conf->verify_depth)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -379,17 +392,126 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
         return NGX_CONF_ERROR;
     }
 
-    SSL_CTX_set_session_cache_mode(conf->ssl.ctx, SSL_SESS_CACHE_SERVER);
+    ngx_conf_merge_value(conf->builtin_session_cache,
+                         prev->builtin_session_cache,
+                         NGX_SSL_DFLT_BUILTIN_SCACHE);
+
+    if (conf->shm_zone == NULL) {
+        conf->shm_zone = prev->shm_zone;
+    }
 
-    SSL_CTX_set_session_id_context(conf->ssl.ctx, ngx_http_session_id_ctx,
-                                   sizeof(ngx_http_session_id_ctx) - 1);
-
-    SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout);
+    if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
+                              conf->builtin_session_cache,
+                              conf->shm_zone, conf->session_timeout)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
 
     return NGX_CONF_OK;
 }
 
 
+static char *
+ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_ssl_srv_conf_t *sscf = conf;
+
+    size_t       len;
+    ngx_str_t   *value, name, size;
+    ngx_int_t    n;
+    ngx_uint_t   i, j;
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strcmp(value[i].data, "builtin") == 0) {
+            sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+            continue;
+        }
+
+        if (value[i].len > sizeof("builtin:") - 1
+            && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+               == 0)
+        {
+            n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+                         value[i].len - (sizeof("builtin:") - 1));
+
+            if (n == NGX_ERROR) {
+                goto invalid;
+            }
+
+            sscf->builtin_session_cache = n;
+
+            continue;
+        }
+
+        if (value[i].len > sizeof("shared:") - 1
+            && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+               == 0)
+        {
+            len = 0;
+
+            for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+                if (value[i].data[j] == ':') {
+                    break;
+                }
+
+                len++;
+            }
+
+            if (len == 0) {
+                goto invalid;
+            }
+
+            name.len = len;
+            name.data = value[i].data + sizeof("shared:") - 1;
+
+            size.len = value[i].len - j - 1;
+            size.data = name.data + len + 1;
+
+            n = ngx_parse_size(&size);
+
+            if (n == NGX_ERROR) {
+                goto invalid;
+            }
+
+            if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "session cache \"%V\" is too small",
+                                   &value[i]);
+
+                return NGX_CONF_ERROR;
+            }
+
+            sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+                                                   &ngx_http_ssl_module);
+            if (sscf->shm_zone == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        goto invalid;
+    }
+
+    if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
+        sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+    }
+
+    return NGX_CONF_OK;
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid session cache \"%V\"", &value[i]);
+
+    return NGX_CONF_ERROR;
+}
+
+
 #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
 
 static char *
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -14,24 +14,28 @@
 
 
 typedef struct {
-    ngx_flag_t      enable;
+    ngx_flag_t                      enable;
 
-    ngx_ssl_t       ssl;
+    ngx_ssl_t                       ssl;
 
-    ngx_flag_t      prefer_server_ciphers;
+    ngx_flag_t                      prefer_server_ciphers;
 
-    ngx_uint_t      protocols;
+    ngx_uint_t                      protocols;
+
+    ngx_int_t                       verify;
+    ngx_int_t                       verify_depth;
 
-    ngx_int_t       verify;
-    ngx_int_t       verify_depth;
+    ssize_t                         builtin_session_cache;
 
-    time_t          session_timeout;
+    time_t                          session_timeout;
 
-    ngx_str_t       certificate;
-    ngx_str_t       certificate_key;
-    ngx_str_t       client_certificate;
+    ngx_str_t                       certificate;
+    ngx_str_t                       certificate_key;
+    ngx_str_t                       client_certificate;
 
-    ngx_str_t       ciphers;
+    ngx_str_t                       ciphers;
+
+    ngx_shm_zone_t                 *shm_zone;
 } ngx_http_ssl_srv_conf_t;
 
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.5.5';
+our $VERSION = '0.5.6';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -976,10 +976,10 @@ ngx_http_read_request_header(ngx_http_re
     }
 
     if (n == 0 || n == NGX_ERROR) {
-        c->error = rev->error;
-        c->log->action = "sending response to client";
-
-        ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+        c->error = 1;
+        c->log->action = "reading client request headers";
+
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         return NGX_ERROR;
     }
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -215,13 +215,6 @@ typedef struct {
 
 
 typedef struct {
-    off_t                             start;
-    off_t                             end;
-    ngx_str_t                         content_range;
-} ngx_http_range_t;
-
-
-typedef struct {
     ngx_list_t                        headers;
 
     ngx_uint_t                        status;
@@ -245,7 +238,6 @@ typedef struct {
     ngx_str_t                         content_type;
     ngx_str_t                         charset;
 
-    ngx_array_t                       ranges;
     ngx_array_t                       cache_control;
 
     off_t                             content_length_n;
@@ -438,6 +430,12 @@ struct ngx_http_request_s {
 
 #endif
 
+    /*
+     * instead of using the request context data in ngx_http_limit_zone_module
+     * we use the single bit in the request structure
+     */
+    unsigned                          limit_zone_set:1;
+
 #if 0
     unsigned                          cachable:1;
 #endif
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -53,7 +53,7 @@ typedef struct {
 typedef struct {
     ngx_http_upstream_rr_peers_t   *peers;
     ngx_uint_t                      current;
-    uintptr_t                      *tried; 
+    uintptr_t                      *tried;
     uintptr_t                       data;
 } ngx_http_upstream_rr_peer_data_t;
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -28,6 +28,8 @@ static ngx_int_t ngx_http_variable_unkno
 
 static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
@@ -115,6 +117,9 @@ static ngx_http_variable_t  ngx_http_cor
 
     { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
 
+    { ngx_string("binary_remote_addr"), NULL,
+      ngx_http_variable_binary_remote_addr, 0, 0, 0 },
+
     { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },
 
     { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
@@ -696,6 +701,26 @@ ngx_http_variable_host(ngx_http_request_
 
 
 static ngx_int_t
+ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    struct sockaddr_in  *sin;
+
+    /* AF_INET only */
+
+    sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+    v->len = sizeof(in_addr_t);
+    v->valid = 1;
+    v->no_cachable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) &sin->sin_addr.s_addr;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_remote_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
--- a/src/imap/ngx_imap_ssl_module.c
+++ b/src/imap/ngx_imap_ssl_module.c
@@ -16,6 +16,8 @@
 
 static void *ngx_imap_ssl_create_conf(ngx_conf_t *cf);
 static char *ngx_imap_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+static char *ngx_imap_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
 
@@ -99,6 +101,13 @@ static ngx_command_t  ngx_imap_ssl_comma
       ngx_imap_ssl_nosupported, 0, 0, ngx_imap_ssl_openssl097 },
 #endif
 
+    { ngx_string("ssl_session_cache"),
+      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE12,
+      ngx_imap_ssl_session_cache,
+      NGX_IMAP_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("ssl_session_timeout"),
       NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_sec_slot,
@@ -135,7 +144,7 @@ ngx_module_t  ngx_imap_ssl_module = {
 };
 
 
-static u_char ngx_imap_session_id_ctx[] = "IMAP";
+static ngx_str_t ngx_imap_ssl_sess_id_ctx = ngx_string("IMAP");
 
 
 static void *
@@ -152,19 +161,20 @@ ngx_imap_ssl_create_conf(ngx_conf_t *cf)
      * set by ngx_pcalloc():
      *
      *     scf->protocols = 0;
-     *
      *     scf->certificate.len = 0;
      *     scf->certificate.data = NULL;
      *     scf->certificate_key.len = 0;
      *     scf->certificate_key.data = NULL;
      *     scf->ciphers.len = 0;
      *     scf->ciphers.data = NULL;
+     *     scf->shm_zone = NULL;
      */
 
     scf->enable = NGX_CONF_UNSET;
     scf->starttls = NGX_CONF_UNSET;
+    scf->prefer_server_ciphers = NGX_CONF_UNSET;
+    scf->builtin_session_cache = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
-    scf->prefer_server_ciphers = NGX_CONF_UNSET;
 
     return scf;
 }
@@ -206,7 +216,7 @@ ngx_imap_ssl_merge_conf(ngx_conf_t *cf, 
 
     conf->ssl.log = cf->log;
 
-    if (ngx_ssl_create(&conf->ssl, conf->protocols) != NGX_OK) {
+    if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
@@ -248,17 +258,126 @@ ngx_imap_ssl_merge_conf(ngx_conf_t *cf, 
         return NGX_CONF_ERROR;
     }
 
-    SSL_CTX_set_session_cache_mode(conf->ssl.ctx, SSL_SESS_CACHE_SERVER);
+    ngx_conf_merge_value(conf->builtin_session_cache,
+                         prev->builtin_session_cache,
+                         NGX_SSL_DFLT_BUILTIN_SCACHE);
+
+    if (conf->shm_zone == NULL) {
+        conf->shm_zone = prev->shm_zone;
+    }
 
-    SSL_CTX_set_session_id_context(conf->ssl.ctx, ngx_imap_session_id_ctx,
-                                   sizeof(ngx_imap_session_id_ctx) - 1);
-
-    SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout);
+    if (ngx_ssl_session_cache(&conf->ssl, &ngx_imap_ssl_sess_id_ctx,
+                              conf->builtin_session_cache,
+                              conf->shm_zone, conf->session_timeout)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
 
     return NGX_CONF_OK;
 }
 
 
+static char *
+ngx_imap_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_imap_ssl_conf_t  *scf = conf;
+
+    size_t       len;
+    ngx_str_t   *value, name, size;
+    ngx_int_t    n;
+    ngx_uint_t   i, j;
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strcmp(value[i].data, "builtin") == 0) {
+            scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+            continue;
+        }
+
+        if (value[i].len > sizeof("builtin:") - 1
+            && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+               == 0)
+        {
+            n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+                         value[i].len - (sizeof("builtin:") - 1));
+
+            if (n == NGX_ERROR) {
+                goto invalid;
+            }
+
+            scf->builtin_session_cache = n;
+
+            continue;
+        }
+
+        if (value[i].len > sizeof("shared:") - 1
+            && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+               == 0)
+        {
+            len = 0;
+
+            for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+                if (value[i].data[j] == ':') {
+                    break;
+                }
+
+                len++;
+            }
+
+            if (len == 0) {
+                goto invalid;
+            }
+
+            name.len = len;
+            name.data = value[i].data + sizeof("shared:") - 1;
+
+            size.len = value[i].len - j - 1;
+            size.data = name.data + len + 1;
+
+            n = ngx_parse_size(&size);
+
+            if (n == NGX_ERROR) {
+                goto invalid;
+            }
+
+            if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "session cache \"%V\" is too small",
+                                   &value[i]);
+
+                return NGX_CONF_ERROR;
+            }
+
+            scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+                                                   &ngx_imap_ssl_module);
+            if (scf->shm_zone == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        goto invalid;
+    }
+
+    if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
+        scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+    }
+
+    return NGX_CONF_OK;
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid session cache \"%V\"", &value[i]);
+
+    return NGX_CONF_ERROR;
+}
+
+
 #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
 
 static char *
--- a/src/imap/ngx_imap_ssl_module.h
+++ b/src/imap/ngx_imap_ssl_module.h
@@ -19,22 +19,25 @@
 
 
 typedef struct {
-    ngx_flag_t      enable;
+    ngx_flag_t       enable;
+
+    ngx_ssl_t        ssl;
 
-    ngx_ssl_t       ssl;
+    ngx_flag_t       prefer_server_ciphers;
+    ngx_flag_t       starttls;
 
-    ngx_flag_t      prefer_server_ciphers;
-    ngx_flag_t      starttls;
+    ngx_uint_t       protocols;
 
-    ngx_uint_t      protocols;
+    ssize_t          builtin_session_cache;
 
-    time_t          session_timeout;
+    time_t           session_timeout;
 
-    ngx_str_t       certificate;
-    ngx_str_t       certificate_key;
+    ngx_str_t        certificate;
+    ngx_str_t        certificate_key;
 
-    ngx_str_t       ciphers;
+    ngx_str_t        ciphers;
 
+    ngx_shm_zone_t  *shm_zone;
 } ngx_imap_ssl_conf_t;
 
 
--- a/src/os/unix/ngx_linux_init.c
+++ b/src/os/unix/ngx_linux_init.c
@@ -13,7 +13,7 @@ static ngx_int_t ngx_linux_procfs(char *
 
 
 char  ngx_linux_kern_ostype[50];
-char  ngx_linux_kern_osrelease[20];
+char  ngx_linux_kern_osrelease[50];
 
 int   ngx_linux_rtsig_max;