Merge branch 'nginx' (nginx-1.11.7).

Change-Id: I85676807130d1ebe5fcbadd82e096b32401ec02f
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index d4acb78..ffd9d06 100644
--- a/.hgtags
+++ b/.hgtags
@@ -405,3 +405,4 @@
 953512ca02c6f63b4fcbbc3e10d0d9835896bf99 release-1.11.4
 5253015a339aaca0a3111473d3e931b6d4752393 release-1.11.5
 5e371426b3bcba4312ce08606194b89b758927d1 release-1.11.6
+5c8f60faf33ca8926473d2da27b4c3c417bd4630 release-1.11.7
diff --git a/BUILD b/BUILD
index efc249a..1d3b91a 100644
--- a/BUILD
+++ b/BUILD
@@ -1468,5 +1468,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.11.6",
+    version = "1.11.7",
 )
diff --git a/auto/lib/perl/conf b/auto/lib/perl/conf
index 959690a..8310936 100644
--- a/auto/lib/perl/conf
+++ b/auto/lib/perl/conf
@@ -12,9 +12,9 @@
 if test -n "$NGX_PERL_VER"; then
     echo " + perl version: $NGX_PERL_VER"
 
-    if [ "`$NGX_PERL -e 'use 5.006001; print "OK"'`" != "OK" ]; then
+    if [ "`$NGX_PERL -e 'use 5.008006; print "OK"'`" != "OK" ]; then
         echo
-        echo "$0: error: perl 5.6.1 or higher is required"
+        echo "$0: error: perl 5.8.6 or higher is required"
         echo
 
         exit 1;
@@ -76,7 +76,7 @@
 
 else
     echo
-    echo "$0: error: perl 5.6.1 or higher is required"
+    echo "$0: error: perl 5.8.6 or higher is required"
     echo
 
     exit 1;
diff --git a/auto/make b/auto/make
index 84d2668..7ddd100 100644
--- a/auto/make
+++ b/auto/make
@@ -156,7 +156,7 @@
 ngx_all_srcs="$ngx_all_srcs $MISC_SRCS"
 
 
-if test -n "$NGX_ADDON_SRCS"; then
+if test -n "$NGX_ADDON_SRCS$DYNAMIC_MODULES"; then
 
 cat << END                                                >> $NGX_MAKEFILE
 
@@ -499,17 +499,6 @@
     ngx_perl_cc="$ngx_perl_cc \$(ALL_INCS)"
 fi
 
-ngx_obj_deps="\$(CORE_DEPS)"
-if [ $HTTP != NO ]; then
-    ngx_obj_deps="$ngx_obj_deps \$(HTTP_DEPS)"
-fi
-if [ $MAIL != NO ]; then
-    ngx_obj_deps="$ngx_obj_deps \$(MAIL_DEPS)"
-fi
-if [ $STREAM != NO ]; then
-    ngx_obj_deps="$ngx_obj_deps \$(STREAM_DEPS)"
-fi
-
 for ngx_module in $DYNAMIC_MODULES
 do
     eval ngx_module_srcs="\$${ngx_module}_SRCS"
@@ -665,7 +654,7 @@
 
             cat << END                                        >> $NGX_MAKEFILE
 
-$ngx_obj:	$ngx_obj_deps$ngx_cont$ngx_src
+$ngx_obj:	\$(ADDON_DEPS)$ngx_cont$ngx_src
 	$ngx_perl_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
 
 END
@@ -673,7 +662,7 @@
 
             cat << END                                        >> $NGX_MAKEFILE
 
-$ngx_obj:	$ngx_obj_deps$ngx_cont$ngx_src
+$ngx_obj:	\$(ADDON_DEPS)$ngx_cont$ngx_src
 	$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
 
 END
diff --git a/auto/module b/auto/module
index 3b00a07..a2b578d 100644
--- a/auto/module
+++ b/auto/module
@@ -35,6 +35,10 @@
         CORE_INCS="$CORE_INCS $ngx_module_incs"
     fi
 
+    if test -n "$ngx_module_deps"; then
+        NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_module_deps"
+    fi
+
     libs=
     for lib in $ngx_module_libs
     do
diff --git a/auto/modules b/auto/modules
index 3b052ab..7b4f2fe 100644
--- a/auto/modules
+++ b/auto/modules
@@ -1252,7 +1252,7 @@
     elif [ $MAIL = DYNAMIC ]; then
         ngx_module_name=$MAIL_MODULES
         ngx_module_incs=
-        ngx_module_deps=$MAIL_DEPS
+        ngx_module_deps=
         ngx_module_srcs=$MAIL_SRCS
         ngx_module_libs=
         ngx_module_link=DYNAMIC
@@ -1272,7 +1272,7 @@
     elif [ $STREAM = DYNAMIC ]; then
         ngx_module_name=$STREAM_MODULES
         ngx_module_incs=
-        ngx_module_deps=$STREAM_DEPS
+        ngx_module_deps=
         ngx_module_srcs=$STREAM_SRCS
         ngx_module_libs=
         ngx_module_link=DYNAMIC
diff --git a/build.bzl b/build.bzl
index 79fc28c..c3de23b 100644
--- a/build.bzl
+++ b/build.bzl
@@ -668,7 +668,7 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "f0a74a54c8948c668fe477f7fafb0f3a175ee19a",  # nginx-1.11.6
+        commit = "4a0aa6d52a93ef2ed0712edc3a67ac345caa9e14",  # nginx-1.11.7
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
     )
 
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 1376338..0a440e4 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,100 @@
 <change_log title="nginx">
 
 
+<changes ver="1.11.7" date="13.12.2016">
+
+<change type="change">
+<para lang="ru">
+переменная $ssl_client_verify теперь
+в случае ошибки проверки клиентского сертификата
+содержит строку с описанием ошибки,
+например, "FAILED:certificate has expired".
+</para>
+<para lang="en">
+now in case of a client certificate verification error
+the $ssl_client_verify variable contains a string with the failure reason,
+for example, "FAILED:certificate has expired".
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+переменные $ssl_ciphers, $ssl_curves,
+$ssl_client_v_start, $ssl_client_v_end и $ssl_client_v_remain.
+</para>
+<para lang="en">
+the $ssl_ciphers, $ssl_curves,
+$ssl_client_v_start, $ssl_client_v_end, and $ssl_client_v_remain variables.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+параметр volatile директивы map.
+</para>
+<para lang="en">
+the "volatile" parameter of the "map" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при сборке динамических модулей
+не учитывались заданные для модуля зависимости.
+</para>
+<para lang="en">
+dependencies specified for a module
+were ignored while building dynamic modules.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании HTTP/2 и директив limit_req или auth_request
+тело запроса могло быть повреждено;
+ошибка появилась в 1.11.0.
+</para>
+<para lang="en">
+when using HTTP/2 and the "limit_req" or "auth_request" directives
+client request body might be corrupted;
+the bug had appeared in 1.11.0.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании HTTP/2 в рабочем процессе мог произойти segmentation fault;
+ошибка появилась в 1.11.3.
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process when using HTTP/2;
+the bug had appeared in 1.11.3.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_http_mp4_module.<br/>
+Спасибо Congcong Hu.
+</para>
+<para lang="en">
+in the ngx_http_mp4_module.<br/>
+Thanks to Congcong Hu.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_http_perl_module.
+</para>
+<para lang="en">
+in the ngx_http_perl_module.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.11.6" date="15.11.2016">
 
 <change type="change">
diff --git a/src/core/nginx.c b/src/core/nginx.c
index 624608e..5e68f7d 100644
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -16,6 +16,7 @@
 
 static void ngx_show_version_info(void);
 static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);
+static void ngx_cleanup_environment(void *data);
 static ngx_int_t ngx_get_options(int argc, char *const *argv);
 static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);
 static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);
@@ -511,10 +512,11 @@
 char **
 ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)
 {
-    char             **p, **env;
-    ngx_str_t         *var;
-    ngx_uint_t         i, n;
-    ngx_core_conf_t   *ccf;
+    char                **p, **env;
+    ngx_str_t            *var;
+    ngx_uint_t            i, n;
+    ngx_core_conf_t      *ccf;
+    ngx_pool_cleanup_t   *cln;
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
@@ -566,14 +568,25 @@
 
     if (last) {
         env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log);
+        if (env == NULL) {
+            return NULL;
+        }
+
         *last = n;
 
     } else {
-        env = ngx_palloc(cycle->pool, (n + 1) * sizeof(char *));
-    }
+        cln = ngx_pool_cleanup_add(cycle->pool, 0);
+        if (cln == NULL) {
+            return NULL;
+        }
 
-    if (env == NULL) {
-        return NULL;
+        env = ngx_alloc((n + 1) * sizeof(char *), cycle->log);
+        if (env == NULL) {
+            return NULL;
+        }
+
+        cln->handler = ngx_cleanup_environment;
+        cln->data = env;
     }
 
     n = 0;
@@ -607,6 +620,25 @@
 }
 
 
+static void
+ngx_cleanup_environment(void *data)
+{
+    char  **env = data;
+
+    if (environ == env) {
+
+        /*
+         * if the environment is still used, as it happens on exit,
+         * the only option is to leak it
+         */
+
+        return;
+    }
+
+    ngx_free(env);
+}
+
+
 ngx_pid_t
 ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
 {
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 9f545d3..c8fb05f 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1011006
-#define NGINX_VERSION      "1.11.6"
+#define nginx_version      1011007
+#define NGINX_VERSION      "1.11.7"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index a57991c..5e95628 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -37,7 +37,7 @@
 ngx_init_cycle(ngx_cycle_t *old_cycle)
 {
     void                *rv;
-    char               **senv, **env;
+    char               **senv;
     ngx_uint_t           i, n;
     ngx_log_t           *log;
     ngx_time_t          *tp;
@@ -750,20 +750,9 @@
 
     if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {
 
-        /*
-         * perl_destruct() frees environ, if it is not the same as it was at
-         * perl_construct() time, therefore we save the previous cycle
-         * environment before ngx_conf_parse() where it will be changed.
-         */
-
-        env = environ;
-        environ = senv;
-
         ngx_destroy_pool(old_cycle->pool);
         cycle->old_cycle = NULL;
 
-        environ = env;
-
         return cycle;
     }
 
diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c
index f784578..7f5dc78 100644
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -512,7 +512,7 @@
     size = ngx_buf_size(src);
     size = ngx_min(size, dst->end - dst->pos);
 
-    sendfile = ctx->sendfile & !ctx->directio;
+    sendfile = ctx->sendfile && !ctx->directio;
 
 #if (NGX_SENDFILE_LIMIT)
 
diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c
index 56e7765..66faecc 100644
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -41,6 +41,19 @@
 #endif
 
 
+#define ngx_slab_slots(pool)                                                  \
+    (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t))
+
+#define ngx_slab_page_type(page)   ((page)->prev & NGX_SLAB_PAGE_MASK)
+
+#define ngx_slab_page_prev(page)                                              \
+    (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK)
+
+#define ngx_slab_page_addr(pool, page)                                        \
+    ((((page) - (pool)->pages) << ngx_pagesize_shift)                         \
+     + (uintptr_t) (pool)->start)
+
+
 #if (NGX_DEBUG_MALLOC)
 
 #define ngx_slab_junk(p, size)     ngx_memset(p, 0xA5, size)
@@ -76,7 +89,7 @@
     size_t            size;
     ngx_int_t         m;
     ngx_uint_t        i, n, pages;
-    ngx_slab_page_t  *slots;
+    ngx_slab_page_t  *slots, *page;
 
     /* STUB */
     if (ngx_slab_max_size == 0) {
@@ -90,15 +103,17 @@
 
     pool->min_size = 1 << pool->min_shift;
 
-    p = (u_char *) pool + sizeof(ngx_slab_pool_t);
+    slots = ngx_slab_slots(pool);
+
+    p = (u_char *) slots;
     size = pool->end - p;
 
     ngx_slab_junk(p, size);
 
-    slots = (ngx_slab_page_t *) p;
     n = ngx_pagesize_shift - pool->min_shift;
 
     for (i = 0; i < n; i++) {
+        /* only "next" is used in list head */
         slots[i].slab = 0;
         slots[i].next = &slots[i];
         slots[i].prev = 0;
@@ -106,30 +121,40 @@
 
     p += n * sizeof(ngx_slab_page_t);
 
+    pool->stats = (ngx_slab_stat_t *) p;
+    ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t));
+
+    p += n * sizeof(ngx_slab_stat_t);
+
+    size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t));
+
     pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));
 
-    ngx_memzero(p, pages * sizeof(ngx_slab_page_t));
-
     pool->pages = (ngx_slab_page_t *) p;
+    ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t));
 
+    page = pool->pages;
+
+    /* only "next" is used in list head */
+    pool->free.slab = 0;
+    pool->free.next = page;
     pool->free.prev = 0;
-    pool->free.next = (ngx_slab_page_t *) p;
 
-    pool->pages->slab = pages;
-    pool->pages->next = &pool->free;
-    pool->pages->prev = (uintptr_t) &pool->free;
+    page->slab = pages;
+    page->next = &pool->free;
+    page->prev = (uintptr_t) &pool->free;
 
-    pool->start = (u_char *)
-                  ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),
-                                 ngx_pagesize);
+    pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t),
+                                ngx_pagesize);
 
     m = pages - (pool->end - pool->start) / ngx_pagesize;
     if (m > 0) {
         pages -= m;
-        pool->pages->slab = pages;
+        page->slab = pages;
     }
 
     pool->last = pool->pages + pages;
+    pool->pfree = pages;
 
     pool->log_nomem = 1;
     pool->log_ctx = &pool->zero;
@@ -168,8 +193,7 @@
         page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)
                                           + ((size % ngx_pagesize) ? 1 : 0));
         if (page) {
-            p = (page - pool->pages) << ngx_pagesize_shift;
-            p += (uintptr_t) pool->start;
+            p = ngx_slab_page_addr(pool, page);
 
         } else {
             p = 0;
@@ -184,166 +208,140 @@
         slot = shift - pool->min_shift;
 
     } else {
-        size = pool->min_size;
         shift = pool->min_shift;
         slot = 0;
     }
 
+    pool->stats[slot].reqs++;
+
     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
                    "slab alloc: %uz slot: %ui", size, slot);
 
-    slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));
+    slots = ngx_slab_slots(pool);
     page = slots[slot].next;
 
     if (page->next != page) {
 
         if (shift < ngx_slab_exact_shift) {
 
-            do {
-                p = (page - pool->pages) << ngx_pagesize_shift;
-                bitmap = (uintptr_t *) (pool->start + p);
+            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);
 
-                map = (1 << (ngx_pagesize_shift - shift))
-                          / (sizeof(uintptr_t) * 8);
+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);
 
-                for (n = 0; n < map; n++) {
+            for (n = 0; n < map; n++) {
 
-                    if (bitmap[n] != NGX_SLAB_BUSY) {
+                if (bitmap[n] != NGX_SLAB_BUSY) {
 
-                        for (m = 1, i = 0; m; m <<= 1, i++) {
-                            if ((bitmap[n] & m)) {
-                                continue;
-                            }
-
-                            bitmap[n] |= m;
-
-                            i = ((n * sizeof(uintptr_t) * 8) << shift)
-                                + (i << shift);
-
-                            if (bitmap[n] == NGX_SLAB_BUSY) {
-                                for (n = n + 1; n < map; n++) {
-                                    if (bitmap[n] != NGX_SLAB_BUSY) {
-                                        p = (uintptr_t) bitmap + i;
-
-                                        goto done;
-                                    }
-                                }
-
-                                prev = (ngx_slab_page_t *)
-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);
-                                prev->next = page->next;
-                                page->next->prev = page->prev;
-
-                                page->next = NULL;
-                                page->prev = NGX_SLAB_SMALL;
-                            }
-
-                            p = (uintptr_t) bitmap + i;
-
-                            goto done;
+                    for (m = 1, i = 0; m; m <<= 1, i++) {
+                        if (bitmap[n] & m) {
+                            continue;
                         }
+
+                        bitmap[n] |= m;
+
+                        i = (n * sizeof(uintptr_t) * 8 + i) << shift;
+
+                        p = (uintptr_t) bitmap + i;
+
+                        pool->stats[slot].used++;
+
+                        if (bitmap[n] == NGX_SLAB_BUSY) {
+                            for (n = n + 1; n < map; n++) {
+                                if (bitmap[n] != NGX_SLAB_BUSY) {
+                                    goto done;
+                                }
+                            }
+
+                            prev = ngx_slab_page_prev(page);
+                            prev->next = page->next;
+                            page->next->prev = page->prev;
+
+                            page->next = NULL;
+                            page->prev = NGX_SLAB_SMALL;
+                        }
+
+                        goto done;
                     }
                 }
-
-                page = page->next;
-
-            } while (page);
+            }
 
         } else if (shift == ngx_slab_exact_shift) {
 
-            do {
-                if (page->slab != NGX_SLAB_BUSY) {
-
-                    for (m = 1, i = 0; m; m <<= 1, i++) {
-                        if ((page->slab & m)) {
-                            continue;
-                        }
-
-                        page->slab |= m;
-
-                        if (page->slab == NGX_SLAB_BUSY) {
-                            prev = (ngx_slab_page_t *)
-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);
-                            prev->next = page->next;
-                            page->next->prev = page->prev;
-
-                            page->next = NULL;
-                            page->prev = NGX_SLAB_EXACT;
-                        }
-
-                        p = (page - pool->pages) << ngx_pagesize_shift;
-                        p += i << shift;
-                        p += (uintptr_t) pool->start;
-
-                        goto done;
-                    }
+            for (m = 1, i = 0; m; m <<= 1, i++) {
+                if (page->slab & m) {
+                    continue;
                 }
 
-                page = page->next;
+                page->slab |= m;
 
-            } while (page);
+                if (page->slab == NGX_SLAB_BUSY) {
+                    prev = ngx_slab_page_prev(page);
+                    prev->next = page->next;
+                    page->next->prev = page->prev;
+
+                    page->next = NULL;
+                    page->prev = NGX_SLAB_EXACT;
+                }
+
+                p = ngx_slab_page_addr(pool, page) + (i << shift);
+
+                pool->stats[slot].used++;
+
+                goto done;
+            }
 
         } else { /* shift > ngx_slab_exact_shift */
 
-            n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);
-            n = 1 << n;
-            n = ((uintptr_t) 1 << n) - 1;
-            mask = n << NGX_SLAB_MAP_SHIFT;
+            mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1;
+            mask <<= NGX_SLAB_MAP_SHIFT;
 
-            do {
-                if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {
-
-                    for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
-                         m & mask;
-                         m <<= 1, i++)
-                    {
-                        if ((page->slab & m)) {
-                            continue;
-                        }
-
-                        page->slab |= m;
-
-                        if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {
-                            prev = (ngx_slab_page_t *)
-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);
-                            prev->next = page->next;
-                            page->next->prev = page->prev;
-
-                            page->next = NULL;
-                            page->prev = NGX_SLAB_BIG;
-                        }
-
-                        p = (page - pool->pages) << ngx_pagesize_shift;
-                        p += i << shift;
-                        p += (uintptr_t) pool->start;
-
-                        goto done;
-                    }
+            for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
+                 m & mask;
+                 m <<= 1, i++)
+            {
+                if (page->slab & m) {
+                    continue;
                 }
 
-                page = page->next;
+                page->slab |= m;
 
-            } while (page);
+                if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {
+                    prev = ngx_slab_page_prev(page);
+                    prev->next = page->next;
+                    page->next->prev = page->prev;
+
+                    page->next = NULL;
+                    page->prev = NGX_SLAB_BIG;
+                }
+
+                p = ngx_slab_page_addr(pool, page) + (i << shift);
+
+                pool->stats[slot].used++;
+
+                goto done;
+            }
         }
+
+        ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_alloc(): page is busy");
+        ngx_debug_point();
     }
 
     page = ngx_slab_alloc_pages(pool, 1);
 
     if (page) {
         if (shift < ngx_slab_exact_shift) {
-            p = (page - pool->pages) << ngx_pagesize_shift;
-            bitmap = (uintptr_t *) (pool->start + p);
+            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);
 
-            s = 1 << shift;
-            n = (1 << (ngx_pagesize_shift - shift)) / 8 / s;
+            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);
 
             if (n == 0) {
                 n = 1;
             }
 
-            bitmap[0] = (2 << n) - 1;
+            /* "n" elements for bitmap, plus one requested */
+            bitmap[0] = ((uintptr_t) 2 << n) - 1;
 
-            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);
 
             for (i = 1; i < map; i++) {
                 bitmap[i] = 0;
@@ -355,8 +353,11 @@
 
             slots[slot].next = page;
 
-            p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;
-            p += (uintptr_t) pool->start;
+            pool->stats[slot].total += (ngx_pagesize >> shift) - n;
+
+            p = ngx_slab_page_addr(pool, page) + (n << shift);
+
+            pool->stats[slot].used++;
 
             goto done;
 
@@ -368,8 +369,11 @@
 
             slots[slot].next = page;
 
-            p = (page - pool->pages) << ngx_pagesize_shift;
-            p += (uintptr_t) pool->start;
+            pool->stats[slot].total += sizeof(uintptr_t) * 8;
+
+            p = ngx_slab_page_addr(pool, page);
+
+            pool->stats[slot].used++;
 
             goto done;
 
@@ -381,8 +385,11 @@
 
             slots[slot].next = page;
 
-            p = (page - pool->pages) << ngx_pagesize_shift;
-            p += (uintptr_t) pool->start;
+            pool->stats[slot].total += ngx_pagesize >> shift;
+
+            p = ngx_slab_page_addr(pool, page);
+
+            pool->stats[slot].used++;
 
             goto done;
         }
@@ -390,6 +397,8 @@
 
     p = 0;
 
+    pool->stats[slot].fails++;
+
 done:
 
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
@@ -444,7 +453,7 @@
 {
     size_t            size;
     uintptr_t         slab, m, *bitmap;
-    ngx_uint_t        n, type, slot, shift, map;
+    ngx_uint_t        i, n, type, slot, shift, map;
     ngx_slab_page_t  *slots, *page;
 
     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);
@@ -457,7 +466,7 @@
     n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
     page = &pool->pages[n];
     slab = page->slab;
-    type = page->prev & NGX_SLAB_PAGE_MASK;
+    type = ngx_slab_page_type(page);
 
     switch (type) {
 
@@ -471,17 +480,16 @@
         }
 
         n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;
-        m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));
-        n /= (sizeof(uintptr_t) * 8);
+        m = (uintptr_t) 1 << (n % (sizeof(uintptr_t) * 8));
+        n /= sizeof(uintptr_t) * 8;
         bitmap = (uintptr_t *)
                              ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));
 
         if (bitmap[n] & m) {
+            slot = shift - pool->min_shift;
 
             if (page->next == NULL) {
-                slots = (ngx_slab_page_t *)
-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));
-                slot = shift - pool->min_shift;
+                slots = ngx_slab_slots(pool);
 
                 page->next = slots[slot].next;
                 slots[slot].next = page;
@@ -492,7 +500,7 @@
 
             bitmap[n] &= ~m;
 
-            n = (1 << (ngx_pagesize_shift - shift)) / 8 / (1 << shift);
+            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);
 
             if (n == 0) {
                 n = 1;
@@ -502,16 +510,18 @@
                 goto done;
             }
 
-            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);
 
-            for (n = 1; n < map; n++) {
-                if (bitmap[n]) {
+            for (i = 1; i < map; i++) {
+                if (bitmap[i]) {
                     goto done;
                 }
             }
 
             ngx_slab_free_pages(pool, page, 1);
 
+            pool->stats[slot].total -= (ngx_pagesize >> shift) - n;
+
             goto done;
         }
 
@@ -528,10 +538,10 @@
         }
 
         if (slab & m) {
+            slot = ngx_slab_exact_shift - pool->min_shift;
+
             if (slab == NGX_SLAB_BUSY) {
-                slots = (ngx_slab_page_t *)
-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));
-                slot = ngx_slab_exact_shift - pool->min_shift;
+                slots = ngx_slab_slots(pool);
 
                 page->next = slots[slot].next;
                 slots[slot].next = page;
@@ -548,6 +558,8 @@
 
             ngx_slab_free_pages(pool, page, 1);
 
+            pool->stats[slot].total -= sizeof(uintptr_t) * 8;
+
             goto done;
         }
 
@@ -566,11 +578,10 @@
                               + NGX_SLAB_MAP_SHIFT);
 
         if (slab & m) {
+            slot = shift - pool->min_shift;
 
             if (page->next == NULL) {
-                slots = (ngx_slab_page_t *)
-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));
-                slot = shift - pool->min_shift;
+                slots = ngx_slab_slots(pool);
 
                 page->next = slots[slot].next;
                 slots[slot].next = page;
@@ -587,6 +598,8 @@
 
             ngx_slab_free_pages(pool, page, 1);
 
+            pool->stats[slot].total -= ngx_pagesize >> shift;
+
             goto done;
         }
 
@@ -598,7 +611,7 @@
             goto wrong_chunk;
         }
 
-        if (slab == NGX_SLAB_PAGE_FREE) {
+        if (!(slab & NGX_SLAB_PAGE_START)) {
             ngx_slab_error(pool, NGX_LOG_ALERT,
                            "ngx_slab_free(): page is already free");
             goto fail;
@@ -626,6 +639,8 @@
 
 done:
 
+    pool->stats[slot].used--;
+
     ngx_slab_junk(p, size);
 
     return;
@@ -678,6 +693,8 @@
             page->next = NULL;
             page->prev = NGX_SLAB_PAGE;
 
+            pool->pfree -= pages;
+
             if (--pages == 0) {
                 return page;
             }
@@ -706,9 +723,10 @@
 ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
     ngx_uint_t pages)
 {
-    ngx_uint_t        type;
     ngx_slab_page_t  *prev, *join;
 
+    pool->pfree += pages;
+
     page->slab = pages--;
 
     if (pages) {
@@ -716,7 +734,7 @@
     }
 
     if (page->next) {
-        prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);
+        prev = ngx_slab_page_prev(page);
         prev->next = page->next;
         page->next->prev = page->prev;
     }
@@ -724,15 +742,14 @@
     join = page + page->slab;
 
     if (join < pool->last) {
-        type = join->prev & NGX_SLAB_PAGE_MASK;
 
-        if (type == NGX_SLAB_PAGE) {
+        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {
 
             if (join->next != NULL) {
                 pages += join->slab;
                 page->slab += join->slab;
 
-                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);
+                prev = ngx_slab_page_prev(join);
                 prev->next = join->next;
                 join->next->prev = join->prev;
 
@@ -745,19 +762,18 @@
 
     if (page > pool->pages) {
         join = page - 1;
-        type = join->prev & NGX_SLAB_PAGE_MASK;
 
-        if (type == NGX_SLAB_PAGE) {
+        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {
 
             if (join->slab == NGX_SLAB_PAGE_FREE) {
-                join = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);
+                join = ngx_slab_page_prev(join);
             }
 
             if (join->next != NULL) {
                 pages += join->slab;
                 join->slab += page->slab;
 
-                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);
+                prev = ngx_slab_page_prev(join);
                 prev->next = join->next;
                 join->next->prev = join->prev;
 
diff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h
index 2922a80..eff893c 100644
--- a/src/core/ngx_slab.h
+++ b/src/core/ngx_slab.h
@@ -23,6 +23,15 @@
 
 
 typedef struct {
+    ngx_uint_t        total;
+    ngx_uint_t        used;
+
+    ngx_uint_t        reqs;
+    ngx_uint_t        fails;
+} ngx_slab_stat_t;
+
+
+typedef struct {
     ngx_shmtx_sh_t    lock;
 
     size_t            min_size;
@@ -32,6 +41,9 @@
     ngx_slab_page_t  *last;
     ngx_slab_page_t   free;
 
+    ngx_slab_stat_t  *stats;
+    ngx_uint_t        pfree;
+
     u_char           *start;
     u_char           *end;
 
diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c
index 88d89b6..cbc009c 100644
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -483,13 +483,11 @@
                           fd, event_list[i].events, revents);
         }
 
-        if ((revents & (POLLERR|POLLHUP|POLLNVAL))
-             && (revents & (POLLIN|POLLOUT)) == 0)
-        {
+        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+
             /*
-             * if the error events were returned without POLLIN or POLLOUT,
-             * then add these flags to handle the events at least in one
-             * active handler
+             * if the error events were returned, add POLLIN and POLLOUT
+             * to handle the events at least in one active handler
              */
 
             revents |= POLLIN|POLLOUT;
diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c
index 3d8fd31..b74865a 100644
--- a/src/event/modules/ngx_epoll_module.c
+++ b/src/event/modules/ngx_epoll_module.c
@@ -871,6 +871,13 @@
             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                            "epoll_wait() error on fd:%d ev:%04XD",
                            c->fd, revents);
+
+            /*
+             * if the error events were returned, add EPOLLIN and EPOLLOUT
+             * to handle the events at least in one active handler
+             */
+
+            revents |= EPOLLIN|EPOLLOUT;
         }
 
 #if 0
@@ -881,18 +888,6 @@
         }
 #endif
 
-        if ((revents & (EPOLLERR|EPOLLHUP))
-             && (revents & (EPOLLIN|EPOLLOUT)) == 0)
-        {
-            /*
-             * if the error events were returned without EPOLLIN or EPOLLOUT,
-             * then add these flags to handle the events at least in one
-             * active handler
-             */
-
-            revents |= EPOLLIN|EPOLLOUT;
-        }
-
         if ((revents & EPOLLIN) && rev->active) {
 
 #if (NGX_HAVE_EPOLLRDHUP)
diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c
index aef1810..cc4af09 100644
--- a/src/event/modules/ngx_eventport_module.c
+++ b/src/event/modules/ngx_eventport_module.c
@@ -552,13 +552,11 @@
                               (int) event_list[i].portev_object, revents);
             }
 
-            if ((revents & (POLLERR|POLLHUP|POLLNVAL))
-                 && (revents & (POLLIN|POLLOUT)) == 0)
-            {
+            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+
                 /*
-                 * if the error events were returned without POLLIN or POLLOUT,
-                 * then add these flags to handle the events at least in one
-                 * active handler
+                 * if the error events were returned, add POLLIN and POLLOUT
+                 * to handle the events at least in one active handler
                  */
 
                 revents |= POLLIN|POLLOUT;
diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c
index d4777d0..d0a0e8f 100644
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -355,13 +355,11 @@
             continue;
         }
 
-        if ((revents & (POLLERR|POLLHUP|POLLNVAL))
-             && (revents & (POLLIN|POLLOUT)) == 0)
-        {
+        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+
             /*
-             * if the error events were returned without POLLIN or POLLOUT,
-             * then add these flags to handle the events at least in one
-             * active handler
+             * if the error events were returned, add POLLIN and POLLOUT
+             * to handle the events at least in one active handler
              */
 
             revents |= POLLIN|POLLOUT;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 9a257b5..d75b7f0 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -59,6 +59,12 @@
 static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);
 #endif
 
+static time_t ngx_ssl_parse_time(
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+    const
+#endif
+    ASN1_TIME *asn1time);
+
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
@@ -106,6 +112,7 @@
 int  ngx_ssl_session_ticket_keys_index;
 int  ngx_ssl_certificate_index;
 int  ngx_ssl_next_certificate_index;
+int  ngx_ssl_certificate_name_index;
 int  ngx_ssl_stapling_index;
 
 
@@ -193,6 +200,14 @@
         return NGX_ERROR;
     }
 
+    ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
+                                                           NULL);
+
+    if (ngx_ssl_certificate_name_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
     ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
 
     if (ngx_ssl_stapling_index == -1) {
@@ -385,6 +400,15 @@
         return NGX_ERROR;
     }
 
+    if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data)
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
+        X509_free(x509);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
+
     if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,
                       SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))
         == 0)
@@ -3273,6 +3297,158 @@
 
 
 ngx_int_t
+ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+#ifdef SSL_CTRL_GET_RAW_CIPHERLIST
+
+    int                n, i, bytes;
+    size_t             len;
+    u_char            *ciphers, *p;
+    const SSL_CIPHER  *cipher;
+
+    bytes = SSL_get0_raw_cipherlist(c->ssl->connection, NULL);
+    n = SSL_get0_raw_cipherlist(c->ssl->connection, &ciphers);
+
+    if (n <= 0) {
+        s->len = 0;
+        return NGX_OK;
+    }
+
+    len = 0;
+    n /= bytes;
+
+    for (i = 0; i < n; i++) {
+        cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);
+
+        if (cipher) {
+            len += ngx_strlen(SSL_CIPHER_get_name(cipher));
+
+        } else {
+            len += sizeof("0x") - 1 + bytes * (sizeof("00") - 1);
+        }
+
+        len += sizeof(":") - 1;
+    }
+
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = s->data;
+
+    for (i = 0; i < n; i++) {
+        cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);
+
+        if (cipher) {
+            p = ngx_sprintf(p, "%s", SSL_CIPHER_get_name(cipher));
+
+        } else {
+            p = ngx_sprintf(p, "0x");
+            p = ngx_hex_dump(p, ciphers + i * bytes, bytes);
+        }
+
+        *p++ = ':';
+    }
+
+    p--;
+
+    s->len = p - s->data;
+
+#else
+
+    u_char  buf[4096];
+
+    if (SSL_get_shared_ciphers(c->ssl->connection, (char *) buf, 4096)
+        == NULL)
+    {
+        s->len = 0;
+        return NGX_OK;
+    }
+
+    s->len = ngx_strlen(buf);
+    s->data = ngx_pnalloc(pool, s->len);
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->data, buf, s->len);
+
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+#ifdef SSL_CTRL_GET_CURVES
+
+    int         *curves, n, i, nid;
+    u_char      *p;
+    size_t       len;
+
+    n = SSL_get1_curves(c->ssl->connection, NULL);
+
+    if (n <= 0) {
+        s->len = 0;
+        return NGX_OK;
+    }
+
+    curves = ngx_palloc(pool, n * sizeof(int));
+
+    n = SSL_get1_curves(c->ssl->connection, curves);
+    len = 0;
+
+    for (i = 0; i < n; i++) {
+        nid = curves[i];
+
+        if (nid & TLSEXT_nid_unknown) {
+            len += sizeof("0x0000") - 1;
+
+        } else {
+            len += ngx_strlen(OBJ_nid2sn(nid));
+        }
+
+        len += sizeof(":") - 1;
+    }
+
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = s->data;
+
+    for (i = 0; i < n; i++) {
+        nid = curves[i];
+
+        if (nid & TLSEXT_nid_unknown) {
+            p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
+
+        } else {
+            p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));
+        }
+
+        *p++ = ':';
+    }
+
+    p--;
+
+    s->len = p - s->data;
+
+#else
+
+    s->len = 0;
+
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
     u_char        *buf;
@@ -3702,28 +3878,210 @@
 ngx_int_t
 ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
-    X509  *cert;
-
-    if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
-        ngx_str_set(s, "FAILED");
-        return NGX_OK;
-    }
+    X509        *cert;
+    long         rc;
+    const char  *str;
 
     cert = SSL_get_peer_certificate(c->ssl->connection);
-
-    if (cert) {
-        ngx_str_set(s, "SUCCESS");
-
-    } else {
+    if (cert == NULL) {
         ngx_str_set(s, "NONE");
+        return NGX_OK;
     }
 
     X509_free(cert);
 
+    rc = SSL_get_verify_result(c->ssl->connection);
+
+    if (rc == X509_V_OK) {
+        ngx_str_set(s, "SUCCESS");
+        return NGX_OK;
+    }
+
+    str = X509_verify_cert_error_string(rc);
+
+    s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str));
+    if (s->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    s->len = ngx_sprintf(s->data, "FAILED:%s", str) - s->data;
+
     return NGX_OK;
 }
 
 
+ngx_int_t
+ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    BIO     *bio;
+    X509    *cert;
+    size_t   len;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+    ASN1_TIME_print(bio, X509_get0_notBefore(cert));
+#else
+    ASN1_TIME_print(bio, X509_get_notBefore(cert));
+#endif
+
+    len = BIO_pending(bio);
+
+    s->len = len;
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        BIO_free(bio);
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+    BIO_read(bio, s->data, len);
+    BIO_free(bio);
+    X509_free(cert);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    BIO     *bio;
+    X509    *cert;
+    size_t   len;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+    ASN1_TIME_print(bio, X509_get0_notAfter(cert));
+#else
+    ASN1_TIME_print(bio, X509_get_notAfter(cert));
+#endif
+
+    len = BIO_pending(bio);
+
+    s->len = len;
+    s->data = ngx_pnalloc(pool, len);
+    if (s->data == NULL) {
+        BIO_free(bio);
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+    BIO_read(bio, s->data, len);
+    BIO_free(bio);
+    X509_free(cert);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    X509    *cert;
+    time_t   now, end;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+    end = ngx_ssl_parse_time(X509_get0_notAfter(cert));
+#else
+    end = ngx_ssl_parse_time(X509_get_notAfter(cert));
+#endif
+
+    if (end == (time_t) NGX_ERROR) {
+        X509_free(cert);
+        return NGX_OK;
+    }
+
+    now = ngx_time();
+
+    if (end < now + 86400) {
+        ngx_str_set(s, "0");
+        X509_free(cert);
+        return NGX_OK;
+    }
+
+    s->data = ngx_pnalloc(pool, NGX_TIME_T_LEN);
+    if (s->data == NULL) {
+        X509_free(cert);
+        return NGX_ERROR;
+    }
+
+    s->len = ngx_sprintf(s->data, "%T", (end - now) / 86400) - s->data;
+
+    X509_free(cert);
+
+    return NGX_OK;
+}
+
+
+static time_t
+ngx_ssl_parse_time(
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+    const
+#endif
+    ASN1_TIME *asn1time)
+{
+    BIO     *bio;
+    u_char  *value;
+    size_t   len;
+    time_t   time;
+
+    /*
+     * OpenSSL doesn't provide a way to convert ASN1_TIME
+     * into time_t.  To do this, we use ASN1_TIME_print(),
+     * which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g.,
+     * "Feb  3 00:55:52 2015 GMT"), and parse the result.
+     */
+
+    bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        return NGX_ERROR;
+    }
+
+    /* fake weekday prepended to match C asctime() format */
+
+    BIO_write(bio, "Tue ", sizeof("Tue ") - 1);
+    ASN1_TIME_print(bio, asn1time);
+    len = BIO_get_mem_data(bio, (char **) &value);
+
+    time = ngx_parse_http_time(value, len);
+
+    BIO_free(bio);
+
+    return time;
+}
+
+
 static void *
 ngx_openssl_create_conf(ngx_cycle_t *cycle)
 {
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index d233c02..c022307 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -191,6 +191,10 @@
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
@@ -215,6 +219,12 @@
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 
 
 ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
@@ -236,6 +246,7 @@
 extern int  ngx_ssl_session_ticket_keys_index;
 extern int  ngx_ssl_certificate_index;
 extern int  ngx_ssl_next_certificate_index;
+extern int  ngx_ssl_certificate_name_index;
 extern int  ngx_ssl_stapling_index;
 
 
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
index 09fab76..2100516 100644
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -31,6 +31,8 @@
     X509                        *cert;
     X509                        *issuer;
 
+    u_char                      *name;
+
     time_t                       valid;
     time_t                       refresh;
 
@@ -45,6 +47,8 @@
     X509                        *cert;
     X509                        *issuer;
 
+    u_char                      *name;
+
     ngx_uint_t                   naddrs;
 
     ngx_addr_t                  *addrs;
@@ -57,14 +61,14 @@
 
     ngx_msec_t                   timeout;
 
-    void                       (*handler)(ngx_ssl_ocsp_ctx_t *r);
+    void                       (*handler)(ngx_ssl_ocsp_ctx_t *ctx);
     void                        *data;
 
     ngx_buf_t                   *request;
     ngx_buf_t                   *response;
     ngx_peer_connection_t        peer;
 
-    ngx_int_t                  (*process)(ngx_ssl_ocsp_ctx_t *r);
+    ngx_int_t                  (*process)(ngx_ssl_ocsp_ctx_t *ctx);
 
     ngx_uint_t                   state;
 
@@ -173,6 +177,8 @@
     staple->timeout = 60000;
     staple->verify = verify;
     staple->cert = cert;
+    staple->name = X509_get_ex_data(staple->cert,
+                                    ngx_ssl_certificate_name_index);
 
     if (file->len) {
         /* use OCSP response from the file */
@@ -354,7 +360,9 @@
 
     if (rc == 0) {
         ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
-                      "\"ssl_stapling\" ignored, issuer certificate not found");
+                      "\"ssl_stapling\" ignored, "
+                      "issuer certificate not found for certificate \"%s\"",
+                      staple->name);
         X509_STORE_CTX_free(store_ctx);
         return NGX_DECLINED;
     }
@@ -374,9 +382,9 @@
 ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_ssl_stapling_t *staple, ngx_str_t *responder)
 {
-    ngx_url_t                  u;
     char                      *s;
     ngx_str_t                  rsp;
+    ngx_url_t                  u;
     STACK_OF(OPENSSL_STRING)  *aia;
 
     if (responder->len == 0) {
@@ -387,7 +395,8 @@
         if (aia == NULL) {
             ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
                           "\"ssl_stapling\" ignored, "
-                          "no OCSP responder URL in the certificate");
+                          "no OCSP responder URL in the certificate \"%s\"",
+                          staple->name);
             return NGX_DECLINED;
         }
 
@@ -399,7 +408,8 @@
         if (s == NULL) {
             ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
                           "\"ssl_stapling\" ignored, "
-                          "no OCSP responder URL in the certificate");
+                          "no OCSP responder URL in the certificate \"%s\"",
+                          staple->name);
             X509_email_free(aia);
             return NGX_DECLINED;
         }
@@ -432,7 +442,9 @@
     } else {
         ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
                       "\"ssl_stapling\" ignored, "
-                      "invalid URL prefix in OCSP responder \"%V\"", &u.url);
+                      "invalid URL prefix in OCSP responder \"%V\" "
+                      "in the certificate \"%s\"",
+                      &u.url, staple->name);
         return NGX_DECLINED;
     }
 
@@ -440,7 +452,9 @@
         if (u.err) {
             ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
                           "\"ssl_stapling\" ignored, "
-                          "%s in OCSP responder \"%V\"", u.err, &u.url);
+                          "%s in OCSP responder \"%V\" "
+                          "in the certificate \"%s\"",
+                          u.err, &u.url, staple->name);
             return NGX_DECLINED;
         }
 
@@ -547,6 +561,7 @@
 
     ctx->cert = staple->cert;
     ctx->issuer = staple->issuer;
+    ctx->name = staple->name;
 
     ctx->addrs = staple->addrs;
     ctx->host = staple->host;
@@ -757,10 +772,10 @@
 static time_t
 ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time)
 {
+    BIO     *bio;
     u_char  *value;
     size_t   len;
     time_t   time;
-    BIO     *bio;
 
     /*
      * OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME
@@ -1005,7 +1020,7 @@
 static void
 ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
 {
-    ngx_int_t    rc;
+    ngx_int_t  rc;
 
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                    "ssl ocsp connect");
@@ -1103,10 +1118,10 @@
 static void
 ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
 {
-    ssize_t            n, size;
-    ngx_int_t          rc;
-    ngx_ssl_ocsp_ctx_t    *ctx;
-    ngx_connection_t  *c;
+    ssize_t              n, size;
+    ngx_int_t            rc;
+    ngx_connection_t    *c;
+    ngx_ssl_ocsp_ctx_t  *ctx;
 
     c = rev->data;
     ctx = c->data;
@@ -1310,12 +1325,11 @@
     rc = ngx_ssl_ocsp_parse_status_line(ctx);
 
     if (rc == NGX_OK) {
-#if 0
-        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
-                       "ssl ocsp status line \"%*s\"",
-                       ctx->response->pos - ctx->response->start,
-                       ctx->response->start);
-#endif
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                       "ssl ocsp status %ui \"%*s\"",
+                       ctx->code,
+                       ctx->header_end - ctx->header_start,
+                       ctx->header_start);
 
         ctx->process = ngx_ssl_ocsp_process_headers;
         return ctx->process(ctx);
@@ -1476,6 +1490,7 @@
 
             if (++ctx->count == 3) {
                 state = sw_space_after_status;
+                ctx->header_start = p - 2;
             }
 
             break;
@@ -1493,6 +1508,7 @@
                 state = sw_almost_done;
                 break;
             case LF:
+                ctx->header_end = p;
                 goto done;
             default:
                 return NGX_ERROR;
@@ -1506,6 +1522,7 @@
                 state = sw_almost_done;
                 break;
             case LF:
+                ctx->header_end = p;
                 goto done;
             }
             break;
@@ -1514,6 +1531,7 @@
         case sw_almost_done:
             switch (ch) {
             case LF:
+                ctx->header_end = p - 1;
                 goto done;
             default:
                 return NGX_ERROR;
@@ -1608,10 +1626,11 @@
     return ctx->process(ctx);
 }
 
+
 static ngx_int_t
 ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
 {
-    u_char      c, ch, *p;
+    u_char  c, ch, *p;
     enum {
         sw_start = 0,
         sw_name,
@@ -1821,12 +1840,27 @@
     if (log->action) {
         p = ngx_snprintf(buf, len, " while %s", log->action);
         len -= p - buf;
+        buf = p;
     }
 
     ctx = log->data;
 
     if (ctx) {
-        p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);
+        p = ngx_snprintf(buf, len, ", responder: %V", &ctx->host);
+        len -= p - buf;
+        buf = p;
+    }
+
+    if (ctx && ctx->peer.name) {
+        p = ngx_snprintf(buf, len, ", peer: %V", ctx->peer.name);
+        len -= p - buf;
+        buf = p;
+    }
+
+    if (ctx && ctx->name) {
+        p = ngx_snprintf(buf, len, ", certificate: \"%s\"", ctx->name);
+        len -= p - buf;
+        buf = p;
     }
 
     return p;
@@ -1846,6 +1880,7 @@
     return NGX_OK;
 }
 
+
 ngx_int_t
 ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c
index 732aa5d..2fc9be9 100644
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -26,7 +26,8 @@
 
     ngx_http_variable_value_t  *default_value;
     ngx_conf_t                 *cf;
-    ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */
+    unsigned                    hostnames:1;
+    unsigned                    no_cacheable:1;
 } ngx_http_map_conf_ctx_t;
 
 
@@ -265,6 +266,7 @@
     ctx.default_value = NULL;
     ctx.cf = &save;
     ctx.hostnames = 0;
+    ctx.no_cacheable = 0;
 
     save = *cf;
     cf->pool = pool;
@@ -281,6 +283,10 @@
         return rv;
     }
 
+    if (ctx.no_cacheable) {
+        var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
+    }
+
     map->default_value = ctx.default_value ? ctx.default_value:
                                              &ngx_http_variable_null_value;
 
@@ -393,8 +399,16 @@
     {
         ctx->hostnames = 1;
         return NGX_CONF_OK;
+    }
 
-    } else if (cf->args->nelts != 2) {
+    if (cf->args->nelts == 1
+        && ngx_strcmp(value[0].data, "volatile") == 0)
+    {
+        ctx->no_cacheable = 1;
+        return NGX_CONF_OK;
+    }
+
+    if (cf->args->nelts != 2) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid number of the map parameters");
         return NGX_CONF_ERROR;
diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c
index 8f2920f..f3c0fdd 100644
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -1229,7 +1229,9 @@
 
     atom_header = mp4->mdat_atom_header;
 
-    if ((uint64_t) atom_data_size > (uint64_t) 0xffffffff) {
+    if ((uint64_t) atom_data_size
+        > (uint64_t) 0xffffffff - sizeof(ngx_mp4_atom_header_t))
+    {
         atom_size = 1;
         atom_header_size = sizeof(ngx_mp4_atom_header64_t);
         ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index e75f5d8..2771ac1 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -276,6 +276,12 @@
     { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
       (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_ciphers"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_ciphers, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
@@ -313,6 +319,15 @@
     { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_client_v_start"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_start, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_v_end"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_end, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index f9a9a84..2796319 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -207,6 +207,7 @@
 
     dTHXa(pmcf->perl);
     PERL_SET_CONTEXT(pmcf->perl);
+    PERL_SET_INTERP(pmcf->perl);
 
     if (ctx->next == NULL) {
         plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);
@@ -322,6 +323,7 @@
 
     dTHXa(pmcf->perl);
     PERL_SET_CONTEXT(pmcf->perl);
+    PERL_SET_INTERP(pmcf->perl);
 
     rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL,
                                     &pv->handler, &value);
@@ -387,6 +389,7 @@
 
     dTHXa(pmcf->perl);
     PERL_SET_CONTEXT(pmcf->perl);
+    PERL_SET_INTERP(pmcf->perl);
 
 #if 0
 
@@ -568,6 +571,7 @@
 
     dTHXa(perl);
     PERL_SET_CONTEXT(perl);
+    PERL_SET_INTERP(perl);
 
     perl_construct(perl);
 
@@ -828,6 +832,7 @@
     PerlInterpreter  *perl = data;
 
     PERL_SET_CONTEXT(perl);
+    PERL_SET_INTERP(perl);
 
     (void) perl_destruct(perl);
 
@@ -936,6 +941,7 @@
 
     dTHXa(pmcf->perl);
     PERL_SET_CONTEXT(pmcf->perl);
+    PERL_SET_INTERP(pmcf->perl);
 
     ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub);
 
@@ -1007,6 +1013,7 @@
 
     dTHXa(pmcf->perl);
     PERL_SET_CONTEXT(pmcf->perl);
+    PERL_SET_INTERP(pmcf->perl);
 
     ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub);
 
@@ -1039,6 +1046,7 @@
     if (pmcf) {
         dTHXa(pmcf->perl);
         PERL_SET_CONTEXT(pmcf->perl);
+        PERL_SET_INTERP(pmcf->perl);
 
         /* set worker's $$ */
 
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 8301630..f3050f1 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -286,7 +286,6 @@
                                             : ngx_http_v2_state_preface;
 
     ngx_queue_init(&h2c->waiting);
-    ngx_queue_init(&h2c->posted);
     ngx_queue_init(&h2c->dependencies);
     ngx_queue_init(&h2c->closed);
 
@@ -420,9 +419,7 @@
 ngx_http_v2_write_handler(ngx_event_t *wev)
 {
     ngx_int_t                  rc;
-    ngx_queue_t               *q;
     ngx_connection_t          *c;
-    ngx_http_v2_stream_t      *stream;
     ngx_http_v2_connection_t  *h2c;
 
     c = wev->data;
@@ -457,26 +454,6 @@
         return;
     }
 
-    while (!ngx_queue_empty(&h2c->posted)) {
-        q = ngx_queue_head(&h2c->posted);
-
-        ngx_queue_remove(q);
-
-        stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
-
-        stream->handled = 0;
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "run http2 stream %ui", stream->node->id);
-
-        wev = stream->request->connection->write;
-
-        wev->active = 0;
-        wev->ready = 1;
-
-        wev->handler(wev);
-    }
-
     h2c->blocked = 0;
 
     if (rc == NGX_AGAIN) {
@@ -2254,7 +2231,7 @@
 
         stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
 
-        stream->handled = 0;
+        stream->waiting = 0;
 
         wev = stream->request->connection->write;
 
@@ -3575,6 +3552,11 @@
         rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
 
     } else {
+        if (stream->preread) {
+            /* enforce writing preread buffer to file */
+            r->request_body_in_file_only = 1;
+        }
+
         rb->buf = ngx_calloc_buf(r->pool);
 
         if (rb->buf != NULL) {
@@ -4271,7 +4253,7 @@
                 continue;
             }
 
-            stream->handled = 0;
+            stream->waiting = 0;
 
             r = stream->request;
             fc = r->connection;
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
index 63bbdad..cddfccd 100644
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -137,7 +137,6 @@
 
     ngx_http_v2_out_frame_t         *last_out;
 
-    ngx_queue_t                      posted;
     ngx_queue_t                      dependencies;
     ngx_queue_t                      closed;
 
@@ -192,7 +191,7 @@
 
     ngx_pool_t                      *pool;
 
-    unsigned                         handled:1;
+    unsigned                         waiting:1;
     unsigned                         blocked:1;
     unsigned                         exhausted:1;
     unsigned                         in_closed:1;
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index c4606e8..bfe1815 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -1248,11 +1248,11 @@
     ngx_queue_t           *q;
     ngx_http_v2_stream_t  *s;
 
-    if (stream->handled) {
+    if (stream->waiting) {
         return;
     }
 
-    stream->handled = 1;
+    stream->waiting = 1;
 
     for (q = ngx_queue_last(&h2c->waiting);
          q != ngx_queue_sentinel(&h2c->waiting);
@@ -1442,20 +1442,29 @@
 ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
     ngx_http_v2_stream_t *stream)
 {
+    ngx_event_t       *wev;
     ngx_connection_t  *fc;
 
-    if (stream->handled || stream->blocked) {
+    if (stream->waiting || stream->blocked) {
         return;
     }
 
     fc = stream->request->connection;
 
-    if (!fc->error && (stream->exhausted || fc->write->delayed)) {
+    if (!fc->error && stream->exhausted) {
         return;
     }
 
-    stream->handled = 1;
-    ngx_queue_insert_tail(&h2c->posted, &stream->queue);
+    wev = fc->write;
+
+    wev->active = 0;
+    wev->ready = 1;
+
+    if (!fc->error && wev->delayed) {
+        return;
+    }
+
+    ngx_post_event(wev, &ngx_posted_events);
 }
 
 
@@ -1465,11 +1474,13 @@
     ngx_http_v2_stream_t *stream = data;
 
     size_t                     window;
+    ngx_event_t               *wev;
+    ngx_queue_t               *q;
     ngx_http_v2_out_frame_t   *frame, **fn;
     ngx_http_v2_connection_t  *h2c;
 
-    if (stream->handled) {
-        stream->handled = 0;
+    if (stream->waiting) {
+        stream->waiting = 0;
         ngx_queue_remove(&stream->queue);
     }
 
@@ -1503,9 +1514,26 @@
         fn = &frame->next;
     }
 
-    if (h2c->send_window == 0 && window && !ngx_queue_empty(&h2c->waiting)) {
-        ngx_queue_add(&h2c->posted, &h2c->waiting);
-        ngx_queue_init(&h2c->waiting);
+    if (h2c->send_window == 0 && window) {
+
+        while (!ngx_queue_empty(&h2c->waiting)) {
+            q = ngx_queue_head(&h2c->waiting);
+
+            ngx_queue_remove(q);
+
+            stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
+
+            stream->waiting = 0;
+
+            wev = stream->request->connection->write;
+
+            wev->active = 0;
+            wev->ready = 1;
+
+            if (!wev->delayed) {
+                ngx_post_event(wev, &ngx_posted_events);
+            }
+        }
     }
 
     h2c->send_window += window;
diff --git a/src/stream/ngx_stream_map_module.c b/src/stream/ngx_stream_map_module.c
index 47a15be..ef06b2d 100644
--- a/src/stream/ngx_stream_map_module.c
+++ b/src/stream/ngx_stream_map_module.c
@@ -26,7 +26,8 @@
 
     ngx_stream_variable_value_t  *default_value;
     ngx_conf_t                   *cf;
-    ngx_uint_t                    hostnames;      /* unsigned  hostnames:1 */
+    unsigned                      hostnames:1;
+    unsigned                      no_cacheable:1;
 } ngx_stream_map_conf_ctx_t;
 
 
@@ -264,6 +265,7 @@
     ctx.default_value = NULL;
     ctx.cf = &save;
     ctx.hostnames = 0;
+    ctx.no_cacheable = 0;
 
     save = *cf;
     cf->pool = pool;
@@ -280,6 +282,10 @@
         return rv;
     }
 
+    if (ctx.no_cacheable) {
+        var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
+    }
+
     map->default_value = ctx.default_value ? ctx.default_value:
                                              &ngx_stream_variable_null_value;
 
@@ -392,8 +398,16 @@
     {
         ctx->hostnames = 1;
         return NGX_CONF_OK;
+    }
 
-    } else if (cf->args->nelts != 2) {
+    if (cf->args->nelts == 1
+        && ngx_strcmp(value[0].data, "volatile") == 0)
+    {
+        ctx->no_cacheable = 1;
+        return NGX_CONF_OK;
+    }
+
+    if (cf->args->nelts != 2) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid number of the map parameters");
         return NGX_CONF_ERROR;
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index d00718b..9191641 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -182,6 +182,12 @@
     { ngx_string("ssl_cipher"), NULL, ngx_stream_ssl_static_variable,
       (uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_ciphers"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
     { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable,
       (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },