Merge branch 'nginx' (nginx-1.15.9).

Change-Id: I95518364d57aef7427d33ea61a5c6f48545e402b
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 0f43222..1228a94 100644
--- a/.hgtags
+++ b/.hgtags
@@ -434,3 +434,4 @@
 2351853ce6867b6166823bdf94333c0a76633c0a release-1.15.6
 051a039ce1c7e09144de4a4846669ec7116cecea release-1.15.7
 ee551e3f6dba336c0d875e266d7d55385f379b42 release-1.15.8
+d2fd76709909767fc727a5b4affcf1dc9ca488a7 release-1.15.9
diff --git a/BUILD b/BUILD
index b1f4c83..a830a04 100644
--- a/BUILD
+++ b/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -1537,5 +1537,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.15.8",
+    version = "1.15.9",
 )
diff --git a/LICENSE b/LICENSE
index e8d4bc5..7abb171 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2002-2018 Igor Sysoev
- * Copyright (C) 2011-2018 Nginx, Inc.
- * Copyright (C) 2015-2018 Google Inc.
+ * Copyright (C) 2002-2019 Igor Sysoev
+ * Copyright (C) 2011-2019 Nginx, Inc.
+ * Copyright (C) 2015-2019 Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index 8ab737b..09da59b 100644
--- a/README.md
+++ b/README.md
@@ -21,9 +21,9 @@
 
 ## License
 
-    Copyright (C) 2002-2018 Igor Sysoev
-    Copyright (C) 2011-2018 Nginx, Inc.
-    Copyright (C) 2015-2018 Google Inc.
+    Copyright (C) 2002-2019 Igor Sysoev
+    Copyright (C) 2011-2019 Nginx, Inc.
+    Copyright (C) 2015-2019 Google Inc.
     All rights reserved.
 
     Redistribution and use in source and binary forms, with or without
diff --git a/WORKSPACE b/WORKSPACE
index abb72d5..513aac5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/auto/make b/auto/make
index 7ddd100..34c40cd 100644
--- a/auto/make
+++ b/auto/make
@@ -229,7 +229,7 @@
 binary:	$NGX_OBJS${ngx_dirsep}nginx$ngx_binext
 
 $NGX_OBJS${ngx_dirsep}nginx$ngx_binext:	$ngx_deps$ngx_spacer
-	\$(LINK) $ngx_long_start$ngx_binout$NGX_OBJS${ngx_dirsep}nginx$ngx_long_cont$ngx_objs$ngx_libs$ngx_link$ngx_main_link
+	\$(LINK) $ngx_long_start$ngx_binout$NGX_OBJS${ngx_dirsep}nginx$ngx_binext$ngx_long_cont$ngx_objs$ngx_libs$ngx_link$ngx_main_link
 	$ngx_rcc
 $ngx_long_end
 
diff --git a/auto/os/win32 b/auto/os/win32
index a0c9862..3b6e2e8 100644
--- a/auto/os/win32
+++ b/auto/os/win32
@@ -11,6 +11,7 @@
 OS_CONFIG="$WIN32_CONFIG"
 NGX_ICONS="$NGX_WIN32_ICONS"
 SELECT_SRCS=$WIN32_SELECT_SRCS
+POLL_SRCS=$WIN32_POLL_SRCS
 
 ngx_pic_opt=
 ngx_binext=".exe"
@@ -31,12 +32,7 @@
 esac
 
 EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE"
-EVENT_FOUND=YES
-
-if [ $EVENT_SELECT = NO ]; then
-    CORE_SRCS="$CORE_SRCS $SELECT_SRCS"
-    EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE"
-fi
+#EVENT_FOUND=YES
 
 have=NGX_HAVE_INET6 . $NGX_AUTO/have
 
diff --git a/auto/sources b/auto/sources
index 9f9571e..8610764 100644
--- a/auto/sources
+++ b/auto/sources
@@ -105,6 +105,7 @@
 
 POLL_MODULE=ngx_poll_module
 POLL_SRCS=src/event/modules/ngx_poll_module.c
+WIN32_POLL_SRCS=src/event/modules/ngx_win32_poll_module.c
 
 KQUEUE_MODULE=ngx_kqueue_module
 KQUEUE_SRCS=src/event/modules/ngx_kqueue_module.c
diff --git a/build.bzl b/build.bzl
index 73549af..5f00eb8 100644
--- a/build.bzl
+++ b/build.bzl
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -54,7 +54,7 @@
 ]
 
 _NGX_BROTLI_BUILD_FILE = """
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -136,7 +136,7 @@
 """
 
 _PCRE_BUILD_FILE = """
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -268,7 +268,7 @@
 """
 
 _PKGOSS_BUILD_FILE = """
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -544,7 +544,7 @@
 """
 
 _ZLIB_BUILD_FILE = """
-# Copyright (C) 2015-2018 Google Inc.
+# Copyright (C) 2015-2019 Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -673,9 +673,9 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "2456bf617acaa11b06c11481082797909b300f45",  # nginx-1.15.8
+        commit = "894beef672e913605c6b93be022933c9ca22cd7b",  # nginx-1.15.9
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1545733310 +0300",
+        shallow_since = "1551190491 +0300",
     )
 
 def nginx_repositories_zlib(bind):
diff --git a/docs/text/LICENSE b/docs/text/LICENSE
index 9401174..c63e0ba 100644
--- a/docs/text/LICENSE
+++ b/docs/text/LICENSE
@@ -1,6 +1,6 @@
 /* 
- * Copyright (C) 2002-2018 Igor Sysoev
- * Copyright (C) 2011-2018 Nginx, Inc.
+ * Copyright (C) 2002-2019 Igor Sysoev
+ * Copyright (C) 2011-2019 Nginx, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 4c471d8..e7dcf95 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,59 @@
 <change_log title="nginx">
 
 
+<changes ver="1.15.9" date="2019-02-26">
+
+<change type="feature">
+<para lang="ru">
+директивы ssl_certificate и ssl_certificate_key
+поддерживают переменные.
+</para>
+<para lang="en">
+variables support
+in the "ssl_certificate" and "ssl_certificate_key" directives.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+метод poll теперь доступен на Windows
+при использовании Windows Vista и новее.
+</para>
+<para lang="en">
+the "poll" method is now available on Windows
+when using Windows Vista or newer.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+если при использовании метода select на Windows
+происходила ошибка при установлении соединения с бэкендом,
+nginx ожидал истечения таймаута на установление соединения.
+</para>
+<para lang="en">
+if the "select" method was used on Windows
+and an error occurred while establishing a backend connection,
+nginx waited for the connection establishment timeout to expire.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+директивы proxy_upload_rate и proxy_download_rate
+в модуле stream
+работали некорректно при проксировании UDP-пакетов.
+</para>
+<para lang="en">
+the "proxy_upload_rate" and "proxy_download_rate" directives
+in the stream module
+worked incorrectly when proxying UDP datagrams.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.15.8" date="2018-12-25">
 
 <change type="feature">
diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index d68ceca..bf91174 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.0.2q
+OPENSSL =	openssl-1.1.1b
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.42
 
@@ -65,7 +65,6 @@
 		--with-cc-opt=-DFD_SETSIZE=1024				\
 		--with-pcre=$(OBJS)/lib/$(PCRE)				\
 		--with-zlib=$(OBJS)/lib/$(ZLIB)				\
-		--with-select_module					\
 		--with-http_v2_module					\
 		--with-http_realip_module				\
 		--with-http_addition_module				\
diff --git a/src/core/nginx.h b/src/core/nginx.h
index b130fae..2795d87 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1015008
-#define NGINX_VERSION      "1.15.8"
+#define nginx_version      1015009
+#define NGINX_VERSION      "1.15.9"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c
index e92cd33..6d1629e 100644
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -310,7 +310,7 @@
                 goto failed;
             }
 
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", rv);
 
             goto failed;
         }
diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c
index 4023870..b8577ce 100644
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -635,10 +635,9 @@
             goto fail;
         }
 
-        n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
         size = slab & ~NGX_SLAB_PAGE_START;
 
-        ngx_slab_free_pages(pool, &pool->pages[n], size);
+        ngx_slab_free_pages(pool, page, size);
 
         ngx_slab_junk(p, size << ngx_pagesize_shift);
 
diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c
index 28c3c92..061c996 100644
--- a/src/event/modules/ngx_eventport_module.c
+++ b/src/event/modules/ngx_eventport_module.c
@@ -250,9 +250,7 @@
 
         ngx_memzero(&sev, sizeof(struct sigevent));
         sev.sigev_notify = SIGEV_PORT;
-#if !(NGX_TEST_BUILD_EVENTPORT)
         sev.sigev_value.sival_ptr = &pn;
-#endif
 
         if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c
index 83e5f8d..a12c112 100644
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -86,7 +86,7 @@
         }
 
         if (event_list) {
-            ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents);
+            ngx_memcpy(list, event_list, sizeof(struct pollfd) * nevents);
             ngx_free(event_list);
         }
 
diff --git a/src/event/modules/ngx_win32_poll_module.c b/src/event/modules/ngx_win32_poll_module.c
new file mode 100644
index 0000000..9fe867f
--- /dev/null
+++ b/src/event/modules/ngx_win32_poll_module.c
@@ -0,0 +1,435 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_poll_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,
+    ngx_uint_t flags);
+static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,
+    ngx_uint_t flags);
+static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+    ngx_uint_t flags);
+static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static struct pollfd      *event_list;
+static ngx_connection_t  **event_index;
+static ngx_uint_t          nevents;
+
+
+static ngx_str_t           poll_name = ngx_string("poll");
+
+static ngx_event_module_t  ngx_poll_module_ctx = {
+    &poll_name,
+    NULL,                                  /* create configuration */
+    ngx_poll_init_conf,                    /* init configuration */
+
+    {
+        ngx_poll_add_event,                /* add an event */
+        ngx_poll_del_event,                /* delete an event */
+        ngx_poll_add_event,                /* enable an event */
+        ngx_poll_del_event,                /* disable an event */
+        NULL,                              /* add an connection */
+        NULL,                              /* delete an connection */
+        NULL,                              /* trigger a notify */
+        ngx_poll_process_events,           /* process the events */
+        ngx_poll_init,                     /* init the events */
+        ngx_poll_done                      /* done the events */
+    }
+
+};
+
+ngx_module_t  ngx_poll_module = {
+    NGX_MODULE_V1,
+    &ngx_poll_module_ctx,                  /* module context */
+    NULL,                                  /* module directives */
+    NGX_EVENT_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_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+    struct pollfd      *list;
+    ngx_connection_t  **index;
+
+    if (event_list == NULL) {
+        nevents = 0;
+    }
+
+    if (ngx_process >= NGX_PROCESS_WORKER
+        || cycle->old_cycle == NULL
+        || cycle->old_cycle->connection_n < cycle->connection_n)
+    {
+        list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,
+                         cycle->log);
+        if (list == NULL) {
+            return NGX_ERROR;
+        }
+
+        if (event_list) {
+            ngx_memcpy(list, event_list, sizeof(struct pollfd) * nevents);
+            ngx_free(event_list);
+        }
+
+        event_list = list;
+
+        index = ngx_alloc(sizeof(ngx_connection_t *) * cycle->connection_n,
+                          cycle->log);
+        if (index == NULL) {
+            return NGX_ERROR;
+        }
+
+        if (event_index) {
+            ngx_memcpy(index, event_index,
+                       sizeof(ngx_connection_t *) * nevents);
+            ngx_free(event_index);
+        }
+
+        event_index = index;
+    }
+
+    ngx_io = ngx_os_io;
+
+    ngx_event_actions = ngx_poll_module_ctx.actions;
+
+    ngx_event_flags = NGX_USE_LEVEL_EVENT;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_poll_done(ngx_cycle_t *cycle)
+{
+    ngx_free(event_list);
+    ngx_free(event_index);
+
+    event_list = NULL;
+    event_index = NULL;
+}
+
+
+static ngx_int_t
+ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+    ngx_event_t       *e;
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ev->active = 1;
+
+    if (ev->index != NGX_INVALID_INDEX) {
+        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                      "poll event fd:%d ev:%i is already set", c->fd, event);
+        return NGX_OK;
+    }
+
+    if (event == NGX_READ_EVENT) {
+        e = c->write;
+#if (NGX_READ_EVENT != POLLIN)
+        event = POLLIN;
+#endif
+
+    } else {
+        e = c->read;
+#if (NGX_WRITE_EVENT != POLLOUT)
+        event = POLLOUT;
+#endif
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "poll add event: fd:%d ev:%i", c->fd, event);
+
+    if (e == NULL || e->index == NGX_INVALID_INDEX) {
+
+        event_list[nevents].fd = c->fd;
+        event_list[nevents].events = (short) event;
+        event_list[nevents].revents = 0;
+
+        event_index[nevents] = c;
+
+        ev->index = nevents;
+        nevents++;
+
+    } else {
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                       "poll add index: %i", e->index);
+
+        event_list[e->index].events |= (short) event;
+        ev->index = e->index;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+    ngx_event_t       *e;
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ev->active = 0;
+
+    if (ev->index == NGX_INVALID_INDEX) {
+        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                      "poll event fd:%d ev:%i is already deleted",
+                      c->fd, event);
+        return NGX_OK;
+    }
+
+    if (event == NGX_READ_EVENT) {
+        e = c->write;
+#if (NGX_READ_EVENT != POLLIN)
+        event = POLLIN;
+#endif
+
+    } else {
+        e = c->read;
+#if (NGX_WRITE_EVENT != POLLOUT)
+        event = POLLOUT;
+#endif
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "poll del event: fd:%d ev:%i", c->fd, event);
+
+    if (e == NULL || e->index == NGX_INVALID_INDEX) {
+        nevents--;
+
+        if (ev->index < nevents) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                           "index: copy event %ui to %i", nevents, ev->index);
+
+            event_list[ev->index] = event_list[nevents];
+            event_index[ev->index] = event_index[nevents];
+
+            c = event_index[ev->index];
+
+            if (c->fd == (ngx_socket_t) -1) {
+                ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                              "unexpected last event");
+
+            } else {
+                if (c->read->index == nevents) {
+                    c->read->index = ev->index;
+                }
+
+                if (c->write->index == nevents) {
+                    c->write->index = ev->index;
+                }
+            }
+        }
+
+    } else {
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                       "poll del index: %i", e->index);
+
+        event_list[e->index].events &= (short) ~event;
+    }
+
+    ev->index = NGX_INVALID_INDEX;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
+{
+    int                 ready, revents;
+    ngx_err_t           err;
+    ngx_uint_t          i, found;
+    ngx_event_t        *ev;
+    ngx_queue_t        *queue;
+    ngx_connection_t   *c;
+
+    /* NGX_TIMER_INFINITE == INFTIM */
+
+#if (NGX_DEBUG0)
+    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
+        for (i = 0; i < nevents; i++) {
+            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                           "poll: %ui: fd:%d ev:%04Xd",
+                           i, event_list[i].fd, event_list[i].events);
+        }
+    }
+#endif
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer);
+
+    ready = WSAPoll(event_list, (u_int) nevents, (int) timer);
+
+    err = (ready == -1) ? ngx_errno : 0;
+
+    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+        ngx_time_update();
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                   "poll ready %d of %ui", ready, nevents);
+
+    if (err) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "WSAPoll() failed");
+        return NGX_ERROR;
+    }
+
+    if (ready == 0) {
+        if (timer != NGX_TIMER_INFINITE) {
+            return NGX_OK;
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "WSAPoll() returned no events without timeout");
+        return NGX_ERROR;
+    }
+
+    for (i = 0; i < nevents && ready; i++) {
+
+        revents = event_list[i].revents;
+
+#if 1
+        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                       "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
+                       i, event_list[i].fd, event_list[i].events, revents);
+#else
+        if (revents) {
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                           "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
+                           i, event_list[i].fd, event_list[i].events, revents);
+        }
+#endif
+
+        if (revents & POLLNVAL) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          "poll() error fd:%d ev:%04Xd rev:%04Xd",
+                          event_list[i].fd, event_list[i].events, revents);
+        }
+
+        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          "strange poll() events fd:%d ev:%04Xd rev:%04Xd",
+                          event_list[i].fd, event_list[i].events, revents);
+        }
+
+        if (event_list[i].fd == (ngx_socket_t) -1) {
+            /*
+             * the disabled event, a workaround for our possible bug,
+             * see the comment below
+             */
+            continue;
+        }
+
+        c = event_index[i];
+
+        if (c->fd == (ngx_socket_t) -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
+
+            /*
+             * it is certainly our fault and it should be investigated,
+             * in the meantime we disable this event to avoid a CPU spinning
+             */
+
+            if (i == nevents - 1) {
+                nevents--;
+            } else {
+                event_list[i].fd = (ngx_socket_t) -1;
+            }
+
+            continue;
+        }
+
+        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+
+            /*
+             * if the error events were returned, add POLLIN and POLLOUT
+             * to handle the events at least in one active handler
+             */
+
+            revents |= POLLIN|POLLOUT;
+        }
+
+        found = 0;
+
+        if ((revents & POLLIN) && c->read->active) {
+            found = 1;
+
+            ev = c->read;
+            ev->ready = 1;
+
+            queue = ev->accept ? &ngx_posted_accept_events
+                               : &ngx_posted_events;
+
+            ngx_post_event(ev, queue);
+        }
+
+        if ((revents & POLLOUT) && c->write->active) {
+            found = 1;
+
+            ev = c->write;
+            ev->ready = 1;
+
+            ngx_post_event(ev, &ngx_posted_events);
+        }
+
+        if (found) {
+            ready--;
+            continue;
+        }
+    }
+
+    if (ready != 0) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events");
+    }
+
+    return NGX_OK;
+}
+
+
+static char *
+ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+    ngx_event_conf_t  *ecf;
+
+    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+    if (ecf->use != ngx_poll_module.ctx_index) {
+        return NGX_CONF_OK;
+    }
+
+#if (NGX_LOAD_WSAPOLL)
+
+    if (!ngx_have_wsapoll) {
+        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                      "poll is not available on this platform");
+        return NGX_CONF_ERROR;
+    }
+
+#endif
+
+    return NGX_CONF_OK;
+}
diff --git a/src/event/modules/ngx_win32_select_module.c b/src/event/modules/ngx_win32_select_module.c
index ba98f07..c464805 100644
--- a/src/event/modules/ngx_win32_select_module.c
+++ b/src/event/modules/ngx_win32_select_module.c
@@ -26,6 +26,7 @@
 static fd_set         master_write_fd_set;
 static fd_set         work_read_fd_set;
 static fd_set         work_write_fd_set;
+static fd_set         work_except_fd_set;
 
 static ngx_uint_t     max_read;
 static ngx_uint_t     max_write;
@@ -253,9 +254,11 @@
 
     work_read_fd_set = master_read_fd_set;
     work_write_fd_set = master_write_fd_set;
+    work_except_fd_set = master_write_fd_set;
 
     if (max_read || max_write) {
-        ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+        ready = select(0, &work_read_fd_set, &work_write_fd_set,
+                       &work_except_fd_set, tp);
 
     } else {
 
@@ -308,14 +311,20 @@
 
         if (ev->write) {
             if (FD_ISSET(c->fd, &work_write_fd_set)) {
-                found = 1;
+                found++;
                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                                "select write %d", c->fd);
             }
 
+            if (FD_ISSET(c->fd, &work_except_fd_set)) {
+                found++;
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                               "select except %d", c->fd);
+            }
+
         } else {
             if (FD_ISSET(c->fd, &work_read_fd_set)) {
-                found = 1;
+                found++;
                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                                "select read %d", c->fd);
             }
@@ -329,7 +338,7 @@
 
             ngx_post_event(ev, queue);
 
-            nready++;
+            nready += found;
         }
     }
 
diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h
index 7253ebb..d3b2378 100644
--- a/src/event/ngx_event_connect.h
+++ b/src/event/ngx_event_connect.h
@@ -63,6 +63,7 @@
     unsigned                         cached:1;
     unsigned                         transparent:1;
     unsigned                         so_keepalive:1;
+    unsigned                         down:1;
 
                                      /* ngx_connection_log_error_e */
     unsigned                         log_error:2;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 958483d..b42b145 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -18,6 +18,10 @@
 } ngx_openssl_conf_t;
 
 
+static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
+    ngx_str_t *cert, STACK_OF(X509) **chain);
+static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
+    ngx_str_t *key, ngx_array_t *passwords);
 static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
     void *userdata);
 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
@@ -50,7 +54,7 @@
 static void ngx_ssl_clear_error(ngx_log_t *log);
 
 static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
-    ngx_str_t *sess_ctx);
+    ngx_str_t *sess_ctx, ngx_array_t *certificates);
 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,
@@ -68,6 +72,7 @@
 static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
     unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
     HMAC_CTX *hctx, int enc);
+static void ngx_ssl_session_ticket_keys_cleanup(void *data);
 #endif
 
 #ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
@@ -406,16 +411,209 @@
 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
     ngx_str_t *key, ngx_array_t *passwords)
 {
-    BIO         *bio;
-    X509        *x509;
-    u_long       n;
-    ngx_str_t   *pwd;
-    ngx_uint_t   tries;
+    char            *err;
+    X509            *x509;
+    EVP_PKEY        *pkey;
+    STACK_OF(X509)  *chain;
 
-    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+    x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
+    if (x509 == NULL) {
+        if (err != NULL) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "cannot load certificate \"%s\": %s",
+                          cert->data, err);
+        }
+
         return NGX_ERROR;
     }
 
+    if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
+        X509_free(x509);
+        sk_X509_pop_free(chain, X509_free);
+        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);
+        sk_X509_pop_free(chain, X509_free);
+        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)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
+        X509_free(x509);
+        sk_X509_pop_free(chain, X509_free);
+        return NGX_ERROR;
+    }
+
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        X509_free(x509);
+        sk_X509_pop_free(chain, X509_free);
+        return NGX_ERROR;
+    }
+
+    /*
+     * Note that x509 is not freed here, but will be instead freed in
+     * ngx_ssl_cleanup_ctx().  This is because we need to preserve all
+     * certificates to be able to iterate all of them through exdata
+     * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index),
+     * while OpenSSL can free a certificate if it is replaced with another
+     * certificate of the same type.
+     */
+
+#ifdef SSL_CTX_set0_chain
+
+    if (SSL_CTX_set0_chain(ssl->ctx, chain) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set0_chain(\"%s\") failed", cert->data);
+        sk_X509_pop_free(chain, X509_free);
+        return NGX_ERROR;
+    }
+
+#else
+    {
+    int  n;
+
+    /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
+
+    n = sk_X509_num(chain);
+
+    while (n--) {
+        x509 = sk_X509_shift(chain);
+
+        if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
+                          cert->data);
+            sk_X509_pop_free(chain, X509_free);
+            return NGX_ERROR;
+        }
+    }
+
+    sk_X509_free(chain);
+    }
+#endif
+
+    pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
+    if (pkey == NULL) {
+        if (err != NULL) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "cannot load certificate key \"%s\": %s",
+                          key->data, err);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
+        EVP_PKEY_free(pkey);
+        return NGX_ERROR;
+    }
+
+    EVP_PKEY_free(pkey);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)
+{
+    char            *err;
+    X509            *x509;
+    EVP_PKEY        *pkey;
+    STACK_OF(X509)  *chain;
+
+    x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain);
+    if (x509 == NULL) {
+        if (err != NULL) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "cannot load certificate \"%s\": %s",
+                          cert->data, err);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (SSL_use_certificate(c->ssl->connection, x509) == 0) {
+        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                      "SSL_use_certificate(\"%s\") failed", cert->data);
+        X509_free(x509);
+        sk_X509_pop_free(chain, X509_free);
+        return NGX_ERROR;
+    }
+
+    X509_free(x509);
+
+#ifdef SSL_set0_chain
+
+    /*
+     * SSL_set0_chain() is only available in OpenSSL 1.0.2+,
+     * but this function is only called via certificate callback,
+     * which is only available in OpenSSL 1.0.2+ as well
+     */
+
+    if (SSL_set0_chain(c->ssl->connection, chain) == 0) {
+        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                      "SSL_set0_chain(\"%s\") failed", cert->data);
+        sk_X509_pop_free(chain, X509_free);
+        return NGX_ERROR;
+    }
+
+#endif
+
+    pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords);
+    if (pkey == NULL) {
+        if (err != NULL) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "cannot load certificate key \"%s\": %s",
+                          key->data, err);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (SSL_use_PrivateKey(c->ssl->connection, pkey) == 0) {
+        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                      "SSL_use_PrivateKey(\"%s\") failed", key->data);
+        EVP_PKEY_free(pkey);
+        return NGX_ERROR;
+    }
+
+    EVP_PKEY_free(pkey);
+
+    return NGX_OK;
+}
+
+
+static X509 *
+ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
+    STACK_OF(X509) **chain)
+{
+    BIO     *bio;
+    X509    *x509, *temp;
+    u_long   n;
+
+    if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert)
+        != NGX_OK)
+    {
+        *err = NULL;
+        return NULL;
+    }
+
     /*
      * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
      * allow to access certificate later from SSL_CTX, so we reimplement
@@ -424,62 +622,33 @@
 
     bio = BIO_new_file((char *) cert->data, "r");
     if (bio == NULL) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "BIO_new_file(\"%s\") failed", cert->data);
-        return NGX_ERROR;
+        *err = "BIO_new_file() failed";
+        return NULL;
     }
 
+    /* certificate itself */
+
     x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
     if (x509 == NULL) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
+        *err = "PEM_read_bio_X509_AUX() failed";
         BIO_free(bio);
-        return NGX_ERROR;
+        return NULL;
     }
 
-    if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
+    /* rest of the chain */
+
+    *chain = sk_X509_new_null();
+    if (*chain == NULL) {
+        *err = "sk_X509_new_null() failed";
+        BIO_free(bio);
         X509_free(x509);
-        BIO_free(bio);
-        return NGX_ERROR;
+        return NULL;
     }
 
-    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)
-    {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
-        X509_free(x509);
-        BIO_free(bio);
-        return NGX_ERROR;
-    }
-
-    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
-        == 0)
-    {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "SSL_CTX_set_ex_data() failed");
-        X509_free(x509);
-        BIO_free(bio);
-        return NGX_ERROR;
-    }
-
-    /* read rest of the chain */
-
     for ( ;; ) {
 
-        x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-        if (x509 == NULL) {
+        temp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+        if (temp == NULL) {
             n = ERR_peek_last_error();
 
             if (ERR_GET_LIB(n) == ERR_LIB_PEM
@@ -492,43 +661,38 @@
 
             /* some real error */
 
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "PEM_read_bio_X509(\"%s\") failed", cert->data);
+            *err = "PEM_read_bio_X509() failed";
             BIO_free(bio);
-            return NGX_ERROR;
-        }
-
-#ifdef SSL_CTRL_CHAIN_CERT
-
-        /*
-         * SSL_CTX_add0_chain_cert() is needed to add chain to
-         * a particular certificate when multiple certificates are used;
-         * only available in OpenSSL 1.0.2+
-         */
-
-        if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "SSL_CTX_add0_chain_cert(\"%s\") failed",
-                          cert->data);
             X509_free(x509);
-            BIO_free(bio);
-            return NGX_ERROR;
+            sk_X509_pop_free(*chain, X509_free);
+            return NULL;
         }
 
-#else
-        if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
-                          cert->data);
-            X509_free(x509);
+        if (sk_X509_push(*chain, temp) == 0) {
+            *err = "sk_X509_push() failed";
             BIO_free(bio);
-            return NGX_ERROR;
+            X509_free(x509);
+            sk_X509_pop_free(*chain, X509_free);
+            return NULL;
         }
-#endif
     }
 
     BIO_free(bio);
 
+    return x509;
+}
+
+
+static EVP_PKEY *
+ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
+    ngx_str_t *key, ngx_array_t *passwords)
+{
+    BIO              *bio;
+    EVP_PKEY         *pkey;
+    ngx_str_t        *pwd;
+    ngx_uint_t        tries;
+    pem_password_cb  *cb;
+
     if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
 
 #ifndef OPENSSL_NO_ENGINE
@@ -541,9 +705,8 @@
         last = (u_char *) ngx_strchr(p, ':');
 
         if (last == NULL) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid syntax in \"%V\"", key);
-            return NGX_ERROR;
+            *err = "invalid syntax";
+            return NULL;
         }
 
         *last = '\0';
@@ -551,9 +714,8 @@
         engine = ENGINE_by_id((char *) p);
 
         if (engine == NULL) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "ENGINE_by_id(\"%s\") failed", p);
-            return NGX_ERROR;
+            *err = "ENGINE_by_id() failed";
+            return NULL;
         }
 
         *last++ = ':';
@@ -561,76 +723,69 @@
         pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
 
         if (pkey == NULL) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "ENGINE_load_private_key(\"%s\") failed", last);
+            *err = "ENGINE_load_private_key() failed";
             ENGINE_free(engine);
-            return NGX_ERROR;
+            return NULL;
         }
 
         ENGINE_free(engine);
 
-        if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
-            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                          "SSL_CTX_use_PrivateKey(\"%s\") failed", last);
-            EVP_PKEY_free(pkey);
-            return NGX_ERROR;
-        }
-
-        EVP_PKEY_free(pkey);
-
-        return NGX_OK;
+        return pkey;
 
 #else
 
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "loading \"engine:...\" certificate keys "
-                           "is not supported");
-        return NGX_ERROR;
+        *err = "loading \"engine:...\" certificate keys is not supported";
+        return NULL;
 
 #endif
     }
 
-    if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
-        return NGX_ERROR;
+    if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)
+        != NGX_OK)
+    {
+        *err = NULL;
+        return NULL;
+    }
+
+    bio = BIO_new_file((char *) key->data, "r");
+    if (bio == NULL) {
+        *err = "BIO_new_file() failed";
+        return NULL;
     }
 
     if (passwords) {
         tries = passwords->nelts;
         pwd = passwords->elts;
-
-        SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
-        SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
+        cb = ngx_ssl_password_callback;
 
     } else {
         tries = 1;
-#if (NGX_SUPPRESS_WARN)
         pwd = NULL;
-#endif
+        cb = NULL;
     }
 
     for ( ;; ) {
 
-        if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
-                                        SSL_FILETYPE_PEM)
-            != 0)
-        {
+        pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
+        if (pkey != NULL) {
             break;
         }
 
-        if (--tries) {
+        if (tries-- > 1) {
             ERR_clear_error();
-            SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);
+            (void) BIO_reset(bio);
+            pwd++;
             continue;
         }
 
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
-        return NGX_ERROR;
+        *err = "PEM_read_bio_PrivateKey() failed";
+        BIO_free(bio);
+        return NULL;
     }
 
-    SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);
+    BIO_free(bio);
 
-    return NGX_OK;
+    return pkey;
 }
 
 
@@ -645,6 +800,10 @@
         return 0;
     }
 
+    if (pwd == NULL) {
+        return 0;
+    }
+
     if (pwd->len > (size_t) size) {
         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                       "password is truncated to %d bytes", size);
@@ -969,10 +1128,13 @@
         return NULL;
     }
 
-    cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
     passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
+    if (passwords == NULL) {
+        return NULL;
+    }
 
-    if (cln == NULL || passwords == NULL) {
+    cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
+    if (cln == NULL) {
         return NULL;
     }
 
@@ -1080,6 +1242,69 @@
 }
 
 
+ngx_array_t *
+ngx_ssl_preserve_passwords(ngx_conf_t *cf, ngx_array_t *passwords)
+{
+    ngx_str_t           *opwd, *pwd;
+    ngx_uint_t           i;
+    ngx_array_t         *pwds;
+    ngx_pool_cleanup_t  *cln;
+    static ngx_array_t   empty_passwords;
+
+    if (passwords == NULL) {
+
+        /*
+         * If there are no passwords, an empty array is used
+         * to make sure OpenSSL's default password callback
+         * won't block on reading from stdin.
+         */
+
+        return &empty_passwords;
+    }
+
+    /*
+     * Passwords are normally allocated from the temporary pool
+     * and cleared after parsing configuration.  To be used at
+     * runtime they have to be copied to the configuration pool.
+     */
+
+    pwds = ngx_array_create(cf->pool, passwords->nelts, sizeof(ngx_str_t));
+    if (pwds == NULL) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_ssl_passwords_cleanup;
+    cln->data = pwds;
+
+    opwd = passwords->elts;
+
+    for (i = 0; i < passwords->nelts; i++) {
+
+        pwd = ngx_array_push(pwds);
+        if (pwd == NULL) {
+            return NULL;
+        }
+
+        pwd->len = opwd[i].len;
+        pwd->data = ngx_pnalloc(cf->pool, pwd->len);
+
+        if (pwd->data == NULL) {
+            pwds->nelts--;
+            return NULL;
+        }
+
+        ngx_memcpy(pwd->data, opwd[i].data, opwd[i].len);
+    }
+
+    return pwds;
+}
+
+
 static void
 ngx_ssl_passwords_cleanup(void *data)
 {
@@ -2686,6 +2911,9 @@
 #ifdef SSL_R_INAPPROPRIATE_FALLBACK
             || n == SSL_R_INAPPROPRIATE_FALLBACK                     /*  373 */
 #endif
+#ifdef SSL_R_CERT_CB_ERROR
+            || n == SSL_R_CERT_CB_ERROR                              /*  377 */
+#endif
 #ifdef SSL_R_VERSION_TOO_LOW
             || n == SSL_R_VERSION_TOO_LOW                            /*  396 */
 #endif
@@ -2765,53 +2993,60 @@
     p = ngx_vslprintf(errstr, last - 1, fmt, args);
     va_end(args);
 
-    p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
+    if (ERR_peek_error()) {
+        p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
 
-    for ( ;; ) {
+        for ( ;; ) {
 
-        n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
+            n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
 
-        if (n == 0) {
-            break;
+            if (n == 0) {
+                break;
+            }
+
+            /* ERR_error_string_n() requires at least one byte */
+
+            if (p >= last - 1) {
+                goto next;
+            }
+
+            *p++ = ' ';
+
+            ERR_error_string_n(n, (char *) p, last - p);
+
+            while (p < last && *p) {
+                p++;
+            }
+
+            if (p < last && *data && (flags & ERR_TXT_STRING)) {
+                *p++ = ':';
+                p = ngx_cpystrn(p, (u_char *) data, last - p);
+            }
+
+        next:
+
+            (void) ERR_get_error();
         }
 
-        /* ERR_error_string_n() requires at least one byte */
-
-        if (p >= last - 1) {
-            goto next;
+        if (p < last) {
+            *p++ = ')';
         }
-
-        *p++ = ' ';
-
-        ERR_error_string_n(n, (char *) p, last - p);
-
-        while (p < last && *p) {
-            p++;
-        }
-
-        if (p < last && *data && (flags & ERR_TXT_STRING)) {
-            *p++ = ':';
-            p = ngx_cpystrn(p, (u_char *) data, last - p);
-        }
-
-    next:
-
-        (void) ERR_get_error();
     }
 
-    ngx_log_error(level, log, err, "%*s)", p - errstr, errstr);
+    ngx_log_error(level, log, err, "%*s", p - errstr, errstr);
 }
 
 
 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_array_t *certificates, ssize_t builtin_session_cache,
+    ngx_shm_zone_t *shm_zone, time_t timeout)
 {
     long  cache_mode;
 
     SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
 
-    if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) {
+    if (ngx_ssl_session_id_context(ssl, sess_ctx, certificates) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -2877,11 +3112,14 @@
 
 
 static ngx_int_t
-ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx)
+ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
+    ngx_array_t *certificates)
 {
     int                   n, i;
     X509                 *cert;
     X509_NAME            *name;
+    ngx_str_t            *certs;
+    ngx_uint_t            k;
     EVP_MD_CTX           *md;
     unsigned int          len;
     STACK_OF(X509_NAME)  *list;
@@ -2926,6 +3164,24 @@
         }
     }
 
+    if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL) {
+
+        /*
+         * If certificates are loaded dynamically, we use certificate
+         * names as specified in the configuration (with variables).
+         */
+
+        certs = certificates->elts;
+        for (k = 0; k < certificates->nelts; k++) {
+
+            if (EVP_DigestUpdate(md, certs[k].data, certs[k].len) == 0) {
+                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                              "EVP_DigestUpdate() failed");
+                goto failed;
+            }
+        }
+    }
+
     list = SSL_CTX_get_client_CA_list(ssl->ctx);
 
     if (list != NULL) {
@@ -2950,7 +3206,7 @@
 
     if (EVP_DigestFinal_ex(md, buf, &len) == 0) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "EVP_DigestUpdate() failed");
+                      "EVP_DigestFinal_ex() failed");
         goto failed;
     }
 
@@ -3481,6 +3737,7 @@
     ngx_uint_t                     i;
     ngx_array_t                   *keys;
     ngx_file_info_t                fi;
+    ngx_pool_cleanup_t            *cln;
     ngx_ssl_session_ticket_key_t  *key;
 
     if (paths == NULL) {
@@ -3493,6 +3750,14 @@
         return NGX_ERROR;
     }
 
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    cln->handler = ngx_ssl_session_ticket_keys_cleanup;
+    cln->data = keys;
+
     path = paths->elts;
     for (i = 0; i < paths->nelts; i++) {
 
@@ -3564,6 +3829,8 @@
             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
                           ngx_close_file_n " \"%V\" failed", &file.name);
         }
+
+        ngx_explicit_memzero(&buf, 80);
     }
 
     if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)
@@ -3594,6 +3861,8 @@
                       ngx_close_file_n " \"%V\" failed", &file.name);
     }
 
+    ngx_explicit_memzero(&buf, 80);
+
     return NGX_ERROR;
 }
 
@@ -3722,6 +3991,16 @@
     }
 }
 
+
+static void
+ngx_ssl_session_ticket_keys_cleanup(void *data)
+{
+    ngx_array_t  *keys = data;
+
+    ngx_explicit_memzero(keys->elts,
+                         keys->nelts * sizeof(ngx_ssl_session_ticket_key_t));
+}
+
 #else
 
 ngx_int_t
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 2495e48..f34ccf9 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -161,10 +161,14 @@
 
 ngx_int_t ngx_ssl_init(ngx_log_t *log);
 ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
+
 ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords);
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+
 ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
     ngx_uint_t prefer_server_ciphers);
 ngx_int_t ngx_ssl_alpn_protos(ngx_conf_t *cf, ngx_ssl_t *ssl,
@@ -181,6 +185,8 @@
 RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
     int key_length);
 ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
+ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
+    ngx_array_t *passwords);
 ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
 ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
 ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
@@ -188,7 +194,8 @@
 ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_uint_t enable);
 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_array_t *certificates, ssize_t builtin_session_cache,
+    ngx_shm_zone_t *shm_zone, time_t timeout);
 ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_array_t *paths);
 ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
diff --git a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c
index 65eb22f..5572830 100644
--- a/src/event/ngx_event_udp.c
+++ b/src/event/ngx_event_udp.c
@@ -256,7 +256,9 @@
             rev = c->read;
 
             c->udp->buffer = &buf;
+
             rev->ready = 1;
+            rev->active = 0;
 
             rev->handler(rev);
 
@@ -265,6 +267,7 @@
             }
 
             rev->ready = 0;
+            rev->active = 1;
 
             goto next;
         }
@@ -343,6 +346,7 @@
         rev = c->read;
         wev = c->write;
 
+        rev->active = 1;
         wev->ready = 1;
 
         rev->log = log;
@@ -453,7 +457,9 @@
     ngx_memcpy(buf, b->pos, n);
 
     c->udp->buffer = NULL;
+
     c->read->ready = 0;
+    c->read->active = 1;
 
     return n;
 }
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index dfff2b8..c184606 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -39,6 +39,9 @@
 static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
     void *parent, void *child);
 
+static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
+    ngx_http_ssl_srv_conf_t *conf);
+
 static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -548,6 +551,7 @@
      * set by ngx_pcalloc():
      *
      *     sscf->protocols = 0;
+     *     sscf->certificate_values = NULL;
      *     sscf->dhparam = { 0, NULL };
      *     sscf->ecdh_curve = { 0, NULL };
      *     sscf->client_certificate = { 0, NULL };
@@ -740,13 +744,38 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
-    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
-                             conf->certificate_keys, conf->passwords)
-        != NGX_OK)
-    {
+    if (ngx_http_ssl_compile_certificates(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
+    if (conf->certificate_values) {
+
+#ifdef SSL_R_CERT_CB_ERROR
+
+        /* install callback to lookup certificates */
+
+        SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_http_ssl_certificate, conf);
+
+#else
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "variables in "
+                      "\"ssl_certificate\" and \"ssl_certificate_key\" "
+                      "directives are not supported on this platform");
+        return NGX_CONF_ERROR;
+#endif
+
+    } else {
+
+        /* configure certificates */
+
+        if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+                                 conf->certificate_keys, conf->passwords)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
     if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
                         conf->prefer_server_ciphers)
         != NGX_OK)
@@ -801,7 +830,7 @@
     }
 
     if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
-                              conf->builtin_session_cache,
+                              conf->certificates, conf->builtin_session_cache,
                               conf->shm_zone, conf->session_timeout)
         != NGX_OK)
     {
@@ -844,6 +873,90 @@
 }
 
 
+static ngx_int_t
+ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
+    ngx_http_ssl_srv_conf_t *conf)
+{
+    ngx_str_t                         *cert, *key;
+    ngx_uint_t                         i, nelts;
+    ngx_http_complex_value_t          *cv;
+    ngx_http_compile_complex_value_t   ccv;
+
+    cert = conf->certificates->elts;
+    key = conf->certificate_keys->elts;
+    nelts = conf->certificates->nelts;
+
+    for (i = 0; i < nelts; i++) {
+
+        if (ngx_http_script_variables_count(&cert[i])) {
+            goto found;
+        }
+
+        if (ngx_http_script_variables_count(&key[i])) {
+            goto found;
+        }
+    }
+
+    return NGX_OK;
+
+found:
+
+    conf->certificate_values = ngx_array_create(cf->pool, nelts,
+                                             sizeof(ngx_http_complex_value_t));
+    if (conf->certificate_values == NULL) {
+        return NGX_ERROR;
+    }
+
+    conf->certificate_key_values = ngx_array_create(cf->pool, nelts,
+                                             sizeof(ngx_http_complex_value_t));
+    if (conf->certificate_key_values == NULL) {
+        return NGX_ERROR;
+    }
+
+    for (i = 0; i < nelts; i++) {
+
+        cv = ngx_array_push(conf->certificate_values);
+        if (cv == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &cert[i];
+        ccv.complex_value = cv;
+        ccv.zero = 1;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        cv = ngx_array_push(conf->certificate_key_values);
+        if (cv == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &key[i];
+        ccv.complex_value = cv;
+        ccv.zero = 1;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    conf->passwords = ngx_ssl_preserve_passwords(cf, conf->passwords);
+    if (conf->passwords == NULL) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
 static char *
 ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index fb3219b..26fdccf 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -36,6 +36,9 @@
     ngx_array_t                    *certificates;
     ngx_array_t                    *certificate_keys;
 
+    ngx_array_t                    *certificate_values;
+    ngx_array_t                    *certificate_key_values;
+
     ngx_str_t                       dhparam;
     ngx_str_t                       ecdh_curve;
     ngx_str_t                       client_certificate;
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
index f6bece5..7758c0a 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -96,6 +96,10 @@
 #endif
 int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
 #endif
+#if (NGX_HTTP_SSL && defined SSL_R_CERT_CB_ERROR)
+int ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg);
+#endif
+
 
 ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);
 ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index eb5ead5..3762aef 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -11,6 +11,7 @@
 
 
 static void ngx_http_wait_request_handler(ngx_event_t *ev);
+static ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c);
 static void ngx_http_process_request_line(ngx_event_t *rev);
 static void ngx_http_process_request_headers(ngx_event_t *rev);
 static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
@@ -503,17 +504,45 @@
 ngx_http_request_t *
 ngx_http_create_request(ngx_connection_t *c)
 {
+    ngx_http_request_t        *r;
+    ngx_http_log_ctx_t        *ctx;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    r = ngx_http_alloc_request(c);
+    if (r == NULL) {
+        return NULL;
+    }
+
+    c->requests++;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    ngx_set_connection_log(c, clcf->error_log);
+
+    ctx = c->log->data;
+    ctx->request = r;
+    ctx->current_request = r;
+
+#if (NGX_STAT_STUB)
+    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
+    r->stat_reading = 1;
+    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
+#endif
+
+    return r;
+}
+
+
+static ngx_http_request_t *
+ngx_http_alloc_request(ngx_connection_t *c)
+{
     ngx_pool_t                 *pool;
     ngx_time_t                 *tp;
     ngx_http_request_t         *r;
-    ngx_http_log_ctx_t         *ctx;
     ngx_http_connection_t      *hc;
     ngx_http_core_srv_conf_t   *cscf;
-    ngx_http_core_loc_conf_t   *clcf;
     ngx_http_core_main_conf_t  *cmcf;
 
-    c->requests++;
-
     hc = c->data;
 
     cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
@@ -541,10 +570,6 @@
 
     r->read_event_handler = ngx_http_block_reading;
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    ngx_set_connection_log(r->connection, clcf->error_log);
-
     r->header_in = hc->busy ? hc->busy->buf : c->buffer;
 
     if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
@@ -604,17 +629,8 @@
 
     r->http_state = NGX_HTTP_READING_REQUEST_STATE;
 
-    ctx = c->log->data;
-    ctx->request = r;
-    ctx->current_request = r;
     r->log_handler = ngx_http_log_error_handler;
 
-#if (NGX_STAT_STUB)
-    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
-    r->stat_reading = 1;
-    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
-#endif
-
     return r;
 }
 
@@ -833,6 +849,7 @@
     ngx_http_close_connection(c);
 }
 
+
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 
 #if defined(OPENSSL_IS_BORINGSSL)
@@ -1085,6 +1102,75 @@
 
 #endif
 
+
+#ifdef SSL_R_CERT_CB_ERROR
+
+int
+ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
+{
+    ngx_str_t                  cert, key;
+    ngx_uint_t                 i, nelts;
+    ngx_connection_t          *c;
+    ngx_http_request_t        *r;
+    ngx_http_ssl_srv_conf_t   *sscf;
+    ngx_http_complex_value_t  *certs, *keys;
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    if (c->ssl->handshaked) {
+        return 0;
+    }
+
+    r = ngx_http_alloc_request(c);
+    if (r == NULL) {
+        return 0;
+    }
+
+    r->logged = 1;
+
+    sscf = arg;
+
+    nelts = sscf->certificate_values->nelts;
+    certs = sscf->certificate_values->elts;
+    keys = sscf->certificate_key_values->elts;
+
+    for (i = 0; i < nelts; i++) {
+
+        if (ngx_http_complex_value(r, &certs[i], &cert) != NGX_OK) {
+            goto failed;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "ssl cert: \"%s\"", cert.data);
+
+        if (ngx_http_complex_value(r, &keys[i], &key) != NGX_OK) {
+            goto failed;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "ssl key: \"%s\"", key.data);
+
+        if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,
+                                           sscf->passwords)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+    }
+
+    ngx_http_free_request(r, 0);
+    c->destroyed = 0;
+    return 1;
+
+failed:
+
+    ngx_http_free_request(r, 0);
+    c->destroyed = 0;
+    return 0;
+}
+
+#endif
+
 #endif
 
 
@@ -3684,9 +3770,11 @@
         r->headers_out.status = rc;
     }
 
-    log->action = "logging request";
+    if (!r->logged) {
+        log->action = "logging request";
 
-    ngx_http_log_request(r);
+        ngx_http_log_request(r);
+    }
 
     log->action = "closing request";
 
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 1a87735..4153889 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -271,6 +271,34 @@
 }
 
 
+ngx_int_t
+ngx_http_test_required_predicates(ngx_http_request_t *r,
+    ngx_array_t *predicates)
+{
+    ngx_str_t                  val;
+    ngx_uint_t                 i;
+    ngx_http_complex_value_t  *cv;
+
+    if (predicates == NULL) {
+        return NGX_OK;
+    }
+
+    cv = predicates->elts;
+
+    for (i = 0; i < predicates->nelts; i++) {
+        if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
+            return NGX_DECLINED;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 char *
 ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
index a5116d7..25bb6d7 100644
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -214,6 +214,8 @@
 
 ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,
     ngx_array_t *predicates);
+ngx_int_t ngx_http_test_required_predicates(ngx_http_request_t *r,
+    ngx_array_t *predicates);
 char *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 7356bd1..fea4ea1 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -850,7 +850,7 @@
 
         ngx_http_file_cache_create_key(r);
 
-        if (r->cache->header_start + 256 >= u->conf->buffer_size) {
+        if (r->cache->header_start + 256 > u->conf->buffer_size) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                           "%V_buffer_size %uz is not enough for cache key, "
                           "it should be increased to at least %uz",
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index 4509597..10e982e 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -435,7 +435,7 @@
     }
 
     if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,
-                              conf->builtin_session_cache,
+                              conf->certificates, conf->builtin_session_cache,
                               conf->shm_zone, conf->session_timeout)
         != NGX_OK)
     {
diff --git a/src/os/unix/ngx_file_aio_read.c b/src/os/unix/ngx_file_aio_read.c
index aedc3c9..bb60ee8 100644
--- a/src/os/unix/ngx_file_aio_read.c
+++ b/src/os/unix/ngx_file_aio_read.c
@@ -110,7 +110,7 @@
 #if (NGX_HAVE_KQUEUE)
     aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
     aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
-    aio->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;
+    aio->aiocb.aio_sigevent.sigev_value.sival_ptr = ev;
 #endif
     ev->handler = ngx_file_aio_event_handler;
 
diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h
index b7da48c..c641108 100644
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -89,8 +89,14 @@
 
 
 #if (NGX_HAVE_FILE_AIO)
+
 #include <aio.h>
 typedef struct aiocb  ngx_aiocb_t;
+
+#if (__FreeBSD_version < 700005 && !defined __DragonFly__)
+#define sival_ptr     sigval_ptr
+#endif
+
 #endif
 
 
diff --git a/src/os/win32/ngx_socket.h b/src/os/win32/ngx_socket.h
index a9e26c2..f8a453d 100644
--- a/src/os/win32/ngx_socket.h
+++ b/src/os/win32/ngx_socket.h
@@ -200,6 +200,49 @@
 extern LPFN_DISCONNECTEX          ngx_disconnectex;
 
 
+#if (NGX_HAVE_POLL && !defined POLLIN)
+
+/*
+ * WSAPoll() is only available if _WIN32_WINNT >= 0x0600.
+ * If it is not available during compilation, we try to
+ * load it dynamically at runtime.
+ */
+
+#define NGX_LOAD_WSAPOLL 1
+
+#define POLLRDNORM  0x0100
+#define POLLRDBAND  0x0200
+#define POLLIN      (POLLRDNORM | POLLRDBAND)
+#define POLLPRI     0x0400
+
+#define POLLWRNORM  0x0010
+#define POLLOUT     (POLLWRNORM)
+#define POLLWRBAND  0x0020
+
+#define POLLERR     0x0001
+#define POLLHUP     0x0002
+#define POLLNVAL    0x0004
+
+typedef struct pollfd {
+
+    SOCKET  fd;
+    SHORT   events;
+    SHORT   revents;
+
+} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
+
+typedef int (WSAAPI *ngx_wsapoll_pt)(
+    LPWSAPOLLFD fdArray,
+    ULONG fds,
+    INT timeout
+    );
+
+extern ngx_wsapoll_pt             WSAPoll;
+extern ngx_uint_t                 ngx_have_wsapoll;
+
+#endif
+
+
 int ngx_tcp_push(ngx_socket_t s);
 #define ngx_tcp_push_n            "tcp_push()"
 
diff --git a/src/os/win32/ngx_win32_init.c b/src/os/win32/ngx_win32_init.c
index ec9b51e..70bee8e 100644
--- a/src/os/win32/ngx_win32_init.c
+++ b/src/os/win32/ngx_win32_init.c
@@ -58,6 +58,12 @@
 static GUID dx_guid = WSAID_DISCONNECTEX;
 
 
+#if (NGX_LOAD_WSAPOLL)
+ngx_wsapoll_pt             WSAPoll;
+ngx_uint_t                 ngx_have_wsapoll;
+#endif
+
+
 ngx_int_t
 ngx_os_init(ngx_log_t *log)
 {
@@ -223,6 +229,32 @@
                       ngx_close_socket_n " failed");
     }
 
+#if (NGX_LOAD_WSAPOLL)
+    {
+    HMODULE  hmod;
+
+    hmod = GetModuleHandle("ws2_32.dll");
+    if (hmod == NULL) {
+        ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno,
+                      "GetModuleHandle(\"ws2_32.dll\") failed");
+        goto nopoll;
+    }
+
+    WSAPoll = (ngx_wsapoll_pt) GetProcAddress(hmod, "WSAPoll");
+    if (WSAPoll == NULL) {
+        ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno,
+                      "GetProcAddress(\"WSAPoll\") failed");
+        goto nopoll;
+    }
+
+    ngx_have_wsapoll = 1;
+
+    }
+
+nopoll:
+
+#endif
+
     if (GetEnvironmentVariable("ngx_unique", ngx_unique, NGX_INT32_LEN + 1)
         != 0)
     {
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index 0949313..d7bdec2 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -1593,7 +1593,7 @@
                     break;
                 }
 
-                if ((off_t) size > limit) {
+                if (c->type == SOCK_STREAM && (off_t) size > limit) {
                     size = (size_t) limit;
                 }
             }
@@ -1667,13 +1667,13 @@
 
     flags = src->read->eof ? NGX_CLOSE_EVENT : 0;
 
-    if (!src->shared && ngx_handle_read_event(src->read, flags) != NGX_OK) {
+    if (ngx_handle_read_event(src->read, flags) != NGX_OK) {
         ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
         return;
     }
 
     if (dst) {
-        if (!dst->shared && ngx_handle_write_event(dst->write, 0) != NGX_OK) {
+        if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
             ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
             return;
         }
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index dcc33e1..9266e99 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -22,6 +22,9 @@
 static ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl,
     ngx_connection_t *c);
 static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c);
+#ifdef SSL_R_CERT_CB_ERROR
+static int ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg);
+#endif
 static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s,
@@ -32,6 +35,9 @@
 static char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent,
     void *child);
 
+static ngx_int_t ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,
+    ngx_stream_ssl_conf_t *conf);
+
 static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -408,6 +414,62 @@
 }
 
 
+#ifdef SSL_R_CERT_CB_ERROR
+
+int
+ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
+{
+    ngx_str_t                    cert, key;
+    ngx_uint_t                   i, nelts;
+    ngx_connection_t            *c;
+    ngx_stream_session_t        *s;
+    ngx_stream_ssl_conf_t       *sslcf;
+    ngx_stream_complex_value_t  *certs, *keys;
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    if (c->ssl->handshaked) {
+        return 0;
+    }
+
+    s = c->data;
+
+    sslcf = arg;
+
+    nelts = sslcf->certificate_values->nelts;
+    certs = sslcf->certificate_values->elts;
+    keys = sslcf->certificate_key_values->elts;
+
+    for (i = 0; i < nelts; i++) {
+
+        if (ngx_stream_complex_value(s, &certs[i], &cert) != NGX_OK) {
+            return 0;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                       "ssl cert: \"%s\"", cert.data);
+
+        if (ngx_stream_complex_value(s, &keys[i], &key) != NGX_OK) {
+            return 0;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                       "ssl key: \"%s\"", key.data);
+
+        if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,
+                                           sslcf->passwords)
+            != NGX_OK)
+        {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+#endif
+
+
 static ngx_int_t
 ngx_stream_ssl_static_variable(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data)
@@ -505,6 +567,7 @@
      *
      *     scf->listen = 0;
      *     scf->protocols = 0;
+     *     scf->certificate_values = NULL;
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
      *     scf->client_certificate = { 0, NULL };
@@ -619,13 +682,38 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
-    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
-                             conf->certificate_keys, conf->passwords)
-        != NGX_OK)
-    {
+    if (ngx_stream_ssl_compile_certificates(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
+    if (conf->certificate_values) {
+
+#ifdef SSL_R_CERT_CB_ERROR
+
+        /* install callback to lookup certificates */
+
+        SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_stream_ssl_certificate, conf);
+
+#else
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "variables in "
+                      "\"ssl_certificate\" and \"ssl_certificate_key\" "
+                      "directives are not supported on this platform");
+        return NGX_CONF_ERROR;
+#endif
+
+    } else {
+
+        /* configure certificates */
+
+        if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+                                 conf->certificate_keys, conf->passwords)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
     if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
                         conf->prefer_server_ciphers)
         != NGX_OK)
@@ -678,7 +766,7 @@
     }
 
     if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx,
-                              conf->builtin_session_cache,
+                              conf->certificates, conf->builtin_session_cache,
                               conf->shm_zone, conf->session_timeout)
         != NGX_OK)
     {
@@ -707,6 +795,90 @@
 }
 
 
+static ngx_int_t
+ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,
+    ngx_stream_ssl_conf_t *conf)
+{
+    ngx_str_t                           *cert, *key;
+    ngx_uint_t                           i, nelts;
+    ngx_stream_complex_value_t          *cv;
+    ngx_stream_compile_complex_value_t   ccv;
+
+    cert = conf->certificates->elts;
+    key = conf->certificate_keys->elts;
+    nelts = conf->certificates->nelts;
+
+    for (i = 0; i < nelts; i++) {
+
+        if (ngx_stream_script_variables_count(&cert[i])) {
+            goto found;
+        }
+
+        if (ngx_stream_script_variables_count(&key[i])) {
+            goto found;
+        }
+    }
+
+    return NGX_OK;
+
+found:
+
+    conf->certificate_values = ngx_array_create(cf->pool, nelts,
+                                           sizeof(ngx_stream_complex_value_t));
+    if (conf->certificate_values == NULL) {
+        return NGX_ERROR;
+    }
+
+    conf->certificate_key_values = ngx_array_create(cf->pool, nelts,
+                                           sizeof(ngx_stream_complex_value_t));
+    if (conf->certificate_key_values == NULL) {
+        return NGX_ERROR;
+    }
+
+    for (i = 0; i < nelts; i++) {
+
+        cv = ngx_array_push(conf->certificate_values);
+        if (cv == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &cert[i];
+        ccv.complex_value = cv;
+        ccv.zero = 1;
+
+        if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        cv = ngx_array_push(conf->certificate_key_values);
+        if (cv == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &key[i];
+        ccv.complex_value = cv;
+        ccv.zero = 1;
+
+        if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    conf->passwords = ngx_ssl_preserve_passwords(cf, conf->passwords);
+    if (conf->passwords == NULL) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
 static char *
 ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index 9f8f01c..6cb4140 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -34,6 +34,9 @@
     ngx_array_t     *certificates;
     ngx_array_t     *certificate_keys;
 
+    ngx_array_t     *certificate_values;
+    ngx_array_t     *certificate_key_values;
+
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
     ngx_str_t        client_certificate;