Merge branch 'nginx' (nginx-1.15.0).

Change-Id: Icd533f6d094374016ed7e0d2c2b050e9f485d96a
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 225c5e3..1b26568 100644
--- a/.hgtags
+++ b/.hgtags
@@ -425,3 +425,4 @@
 31c929e16910c38492581ef474e72fa67c28f124 release-1.13.10
 64179f242cb55fc206bca59de9bfdc4cf5ebcec7 release-1.13.11
 051e5fa03b92b8a564f6b12debd483d267391e82 release-1.13.12
+990b3e885636d763b97ed02d0d2cfc161a4e0c09 release-1.15.0
diff --git a/BUILD b/BUILD
index dc73842..9d519dd 100644
--- a/BUILD
+++ b/BUILD
@@ -248,6 +248,7 @@
         "src/event/ngx_event_posted.h",
         "src/event/ngx_event_timer.c",
         "src/event/ngx_event_timer.h",
+        "src/event/ngx_event_udp.c",
         "src/os/unix/ngx_alloc.c",
         "src/os/unix/ngx_alloc.h",
         "src/os/unix/ngx_atomic.h",
@@ -1502,5 +1503,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.13.12",
+    version = "1.15.0",
 )
diff --git a/auto/cc/clang b/auto/cc/clang
index 5ea6dac..47a4dce 100644
--- a/auto/cc/clang
+++ b/auto/cc/clang
@@ -6,7 +6,8 @@
 
 
 NGX_CLANG_VER=`$CC -v 2>&1 | grep 'version' 2>&1 \
-                           | sed -e 's/^.* version \(.*\)/\1/'`
+                           | sed -n -e 's/^.*clang version \(.*\)/\1/p' \
+                                    -e 's/^.*LLVM version \(.*\)/\1/p'`
 
 echo " + clang version: $NGX_CLANG_VER"
 
diff --git a/auto/sources b/auto/sources
index 71e6c37..9f9571e 100644
--- a/auto/sources
+++ b/auto/sources
@@ -94,6 +94,7 @@
             src/event/ngx_event_timer.c \
             src/event/ngx_event_posted.c \
             src/event/ngx_event_accept.c \
+            src/event/ngx_event_udp.c \
             src/event/ngx_event_connect.c \
             src/event/ngx_event_pipe.c"
 
diff --git a/build.bzl b/build.bzl
index e8109de..8867872 100644
--- a/build.bzl
+++ b/build.bzl
@@ -663,7 +663,7 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "8157e28e490a561530abb455d0df7b48a983b0f4",  # nginx-1.13.12
+        commit = "6afed1e761a8a163cdbdef51063eee91330a93ec",  # nginx-1.15.0
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
     )
 
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index da73db5..6bbff2c 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,137 @@
 <change_log title="nginx">
 
 
+<changes ver="1.15.0" date="2018-06-05">
+
+<change type="change">
+<para lang="ru">
+директива "ssl" теперь считается устаревшей;
+вместо неё следует использовать параметр ssl директивы listen.
+</para>
+<para lang="en">
+the "ssl" directive is deprecated;
+the "ssl" parameter of the "listen" directive should be used instead.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь при использовании директивы listen с параметром ssl
+nginx определяет отсутствие SSL-сертификатов при тестировании конфигурации.
+</para>
+<para lang="en">
+now nginx detects missing SSL certificates during configuration testing
+when using the "ssl" parameter of the "listen" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+теперь модуль stream умеет обрабатывать
+несколько входящих UDP-пакетов от клиента в рамках одной сессии.
+</para>
+<para lang="en">
+now the stream module can handle
+multiple incoming UDP datagrams from a client within a single session.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в директиве proxy_cache_valid
+можно было указать некорректный код ответа.
+</para>
+<para lang="en">
+it was possible to specify an incorrect response code
+in the "proxy_cache_valid" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не собирался gcc 8.1.
+</para>
+<para lang="en">
+nginx could not be built by gcc 8.1.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+логгирование в syslog останавливалось при изменении локального IP-адреса.
+</para>
+<para lang="en">
+logging to syslog stopped on local IP address changes.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не собирался компилятором clang, если был установлен CUDA SDK;
+ошибка появилась в 1.13.8.
+</para>
+<para lang="en">
+nginx could not be built by clang with CUDA SDK installed;
+the bug had appeared in 1.13.8.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании unix domain listen-сокетов на FreeBSD
+в процессе обновления исполняемого файла
+в логе могли появляться сообщения "getsockopt(TCP_FASTOPEN) ... failed".
+</para>
+<para lang="en">
+"getsockopt(TCP_FASTOPEN) ... failed" messages might appear in logs
+during binary upgrade
+when using unix domain listen sockets on FreeBSD.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не собирался на Fedora 28 Linux.
+</para>
+<para lang="en">
+nginx could not be built on Fedora 28 Linux.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании директивы limit_req
+заданная скорость обработки запросов могла не соблюдаться.
+</para>
+<para lang="en">
+request processing rate might exceed configured rate
+when using the "limit_req" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в обработке адресов клиентов при использовании unix domain listen-сокетов
+для работы с датаграммами на Linux.
+</para>
+<para lang="en">
+in handling of client addresses when using unix domain listen sockets
+to work with datagrams on Linux.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в обработке ошибок выделения памяти.
+</para>
+<para lang="en">
+in memory allocation error handling.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.13.12" date="2018-04-10">
 
 <change type="bugfix">
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 6166000..9c9679e 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1013012
-#define NGINX_VERSION      "1.13.12"
+#define nginx_version      1015000
+#define NGINX_VERSION      "1.15.0"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c
index 1862a06..c3783c4 100644
--- a/src/core/ngx_buf.c
+++ b/src/core/ngx_buf.c
@@ -137,6 +137,7 @@
     while (in) {
         cl = ngx_alloc_chain_link(pool);
         if (cl == NULL) {
+            *ll = NULL;
             return NGX_ERROR;
         }
 
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 9a74758..dc60679 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -72,6 +72,10 @@
 
     ngx_memcpy(ls->addr_text.data, text, len);
 
+#if !(NGX_WIN32)
+    ngx_rbtree_init(&ls->rbtree, &ls->sentinel, ngx_udp_rbtree_insert_value);
+#endif
+
     ls->fd = (ngx_socket_t) -1;
     ls->type = SOCK_STREAM;
 
@@ -305,7 +309,9 @@
         {
             err = ngx_socket_errno;
 
-            if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {
+            if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT
+                && err != NGX_EINVAL)
+            {
                 ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,
                               "getsockopt(TCP_FASTOPEN) %V failed, ignored",
                               &ls[i].addr_text);
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index e4dfe58..ef07558 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -51,6 +51,9 @@
     ngx_listening_t    *previous;
     ngx_connection_t   *connection;
 
+    ngx_rbtree_t        rbtree;
+    ngx_rbtree_node_t   sentinel;
+
     ngx_uint_t          worker;
 
     unsigned            open:1;
@@ -151,6 +154,8 @@
     ngx_ssl_connection_t  *ssl;
 #endif
 
+    ngx_udp_connection_t  *udp;
+
     struct sockaddr    *local_sockaddr;
     socklen_t           local_socklen;
 
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index 3fc751d..78ff7a7 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -27,6 +27,7 @@
 typedef struct ngx_thread_task_s     ngx_thread_task_t;
 typedef struct ngx_ssl_s             ngx_ssl_t;
 typedef struct ngx_ssl_connection_s  ngx_ssl_connection_t;
+typedef struct ngx_udp_connection_s  ngx_udp_connection_t;
 
 typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
 typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index cd55520..f904091 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -1299,16 +1299,23 @@
 
     n = ngx_send(rec->udp, query, qlen);
 
-    if (n == -1) {
-        return NGX_ERROR;
+    if (n == NGX_ERROR) {
+        goto failed;
     }
 
     if ((size_t) n != (size_t) qlen) {
         ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete");
-        return NGX_ERROR;
+        goto failed;
     }
 
     return NGX_OK;
+
+failed:
+
+    ngx_close_connection(rec->udp);
+    rec->udp = NULL;
+
+    return NGX_ERROR;
 }
 
 
@@ -4546,7 +4553,7 @@
                 level = NGX_LOG_CRIT;
             }
 
-            ngx_log_error(level, c->log, err, "connect() to %V failed",
+            ngx_log_error(level, &rec->log, err, "connect() to %V failed",
                           &rec->server);
 
             ngx_close_connection(c);
diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c
index 0a67928..3c7b63a 100644
--- a/src/core/ngx_syslog.c
+++ b/src/core/ngx_syslog.c
@@ -39,7 +39,8 @@
 char *
 ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
 {
-    peer->pool = cf->pool;
+    ngx_pool_cleanup_t  *cln;
+
     peer->facility = NGX_CONF_UNSET_UINT;
     peer->severity = NGX_CONF_UNSET_UINT;
 
@@ -67,6 +68,19 @@
 
     peer->conn.fd = (ngx_socket_t) -1;
 
+    peer->conn.read = &ngx_syslog_dummy_event;
+    peer->conn.write = &ngx_syslog_dummy_event;
+
+    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln->data = peer;
+    cln->handler = ngx_syslog_cleanup;
+
     return NGX_CONF_OK;
 }
 
@@ -289,9 +303,7 @@
         n = ngx_os_io.send(&peer->conn, buf, len);
     }
 
-#if (NGX_HAVE_UNIX_DOMAIN)
-
-    if (n == NGX_ERROR && peer->server.sockaddr->sa_family == AF_UNIX) {
+    if (n == NGX_ERROR) {
 
         if (ngx_close_socket(peer->conn.fd) == -1) {
             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
@@ -301,8 +313,6 @@
         peer->conn.fd = (ngx_socket_t) -1;
     }
 
-#endif
-
     return n;
 }
 
@@ -310,13 +320,7 @@
 static ngx_int_t
 ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
 {
-    ngx_socket_t         fd;
-    ngx_pool_cleanup_t  *cln;
-
-    peer->conn.read = &ngx_syslog_dummy_event;
-    peer->conn.write = &ngx_syslog_dummy_event;
-
-    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
+    ngx_socket_t  fd;
 
     fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
     if (fd == (ngx_socket_t) -1) {
@@ -337,14 +341,6 @@
         goto failed;
     }
 
-    cln = ngx_pool_cleanup_add(peer->pool, 0);
-    if (cln == NULL) {
-        goto failed;
-    }
-
-    cln->data = peer;
-    cln->handler = ngx_syslog_cleanup;
-
     peer->conn.fd = fd;
 
     /* UDP sockets are always ready to write */
diff --git a/src/core/ngx_syslog.h b/src/core/ngx_syslog.h
index cc4c842..50dcd35 100644
--- a/src/core/ngx_syslog.h
+++ b/src/core/ngx_syslog.h
@@ -9,7 +9,6 @@
 
 
 typedef struct {
-    ngx_pool_t       *pool;
     ngx_uint_t        facility;
     ngx_uint_t        severity;
     ngx_str_t         tag;
diff --git a/src/core/ngx_times.h b/src/core/ngx_times.h
index 94aedcd..49e0a8c 100644
--- a/src/core/ngx_times.h
+++ b/src/core/ngx_times.h
@@ -43,8 +43,8 @@
 extern volatile ngx_str_t    ngx_cached_syslog_time;
 
 /*
- * milliseconds elapsed since epoch and truncated to ngx_msec_t,
- * used in event timers
+ * milliseconds elapsed since some unspecified point in the past
+ * and truncated to ngx_msec_t, used in event timers
  */
 extern volatile ngx_msec_t  ngx_current_msec;
 
diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h
index 785253d..6783e71 100644
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -511,9 +511,15 @@
 void ngx_event_accept(ngx_event_t *ev);
 #if !(NGX_WIN32)
 void ngx_event_recvmsg(ngx_event_t *ev);
+void ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 #endif
 ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle);
+ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
 u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len);
+#if (NGX_DEBUG)
+void ngx_debug_accepted_connection(ngx_event_conf_t *ecf, ngx_connection_t *c);
+#endif
 
 
 void ngx_process_events_and_timers(ngx_cycle_t *cycle);
diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c
index 7756370..4364240 100644
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -10,13 +10,8 @@
 #include <ngx_event.h>
 
 
-static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
 static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);
 static void ngx_close_accepted_connection(ngx_connection_t *c);
-#if (NGX_DEBUG)
-static void ngx_debug_accepted_connection(ngx_event_conf_t *ecf,
-    ngx_connection_t *c);
-#endif
 
 
 void
@@ -320,329 +315,6 @@
 }
 
 
-#if !(NGX_WIN32)
-
-void
-ngx_event_recvmsg(ngx_event_t *ev)
-{
-    ssize_t            n;
-    ngx_log_t         *log;
-    ngx_err_t          err;
-    ngx_event_t       *rev, *wev;
-    struct iovec       iov[1];
-    struct msghdr      msg;
-    ngx_sockaddr_t     sa;
-    ngx_listening_t   *ls;
-    ngx_event_conf_t  *ecf;
-    ngx_connection_t  *c, *lc;
-    static u_char      buffer[65535];
-
-#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
-
-#if (NGX_HAVE_IP_RECVDSTADDR)
-    u_char             msg_control[CMSG_SPACE(sizeof(struct in_addr))];
-#elif (NGX_HAVE_IP_PKTINFO)
-    u_char             msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
-#endif
-
-#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
-    u_char             msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-#endif
-
-#endif
-
-    if (ev->timedout) {
-        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
-            return;
-        }
-
-        ev->timedout = 0;
-    }
-
-    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
-
-    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
-        ev->available = ecf->multi_accept;
-    }
-
-    lc = ev->data;
-    ls = lc->listening;
-    ev->ready = 0;
-
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
-                   "recvmsg on %V, ready: %d", &ls->addr_text, ev->available);
-
-    do {
-        ngx_memzero(&msg, sizeof(struct msghdr));
-
-        iov[0].iov_base = (void *) buffer;
-        iov[0].iov_len = sizeof(buffer);
-
-        msg.msg_name = &sa;
-        msg.msg_namelen = sizeof(ngx_sockaddr_t);
-        msg.msg_iov = iov;
-        msg.msg_iovlen = 1;
-
-#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
-
-        if (ls->wildcard) {
-
-#if (NGX_HAVE_IP_RECVDSTADDR || NGX_HAVE_IP_PKTINFO)
-            if (ls->sockaddr->sa_family == AF_INET) {
-                msg.msg_control = &msg_control;
-                msg.msg_controllen = sizeof(msg_control);
-            }
-#endif
-
-#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
-            if (ls->sockaddr->sa_family == AF_INET6) {
-                msg.msg_control = &msg_control6;
-                msg.msg_controllen = sizeof(msg_control6);
-            }
-#endif
-        }
-
-#endif
-
-        n = recvmsg(lc->fd, &msg, 0);
-
-        if (n == -1) {
-            err = ngx_socket_errno;
-
-            if (err == NGX_EAGAIN) {
-                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
-                               "recvmsg() not ready");
-                return;
-            }
-
-            ngx_log_error(NGX_LOG_ALERT, ev->log, err, "recvmsg() failed");
-
-            return;
-        }
-
-#if (NGX_STAT_STUB)
-        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
-#endif
-
-#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
-        if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
-            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
-                          "recvmsg() truncated data");
-            continue;
-        }
-#endif
-
-        ngx_accept_disabled = ngx_cycle->connection_n / 8
-                              - ngx_cycle->free_connection_n;
-
-        c = ngx_get_connection(lc->fd, ev->log);
-        if (c == NULL) {
-            return;
-        }
-
-        c->shared = 1;
-        c->type = SOCK_DGRAM;
-        c->socklen = msg.msg_namelen;
-
-        if (c->socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
-            c->socklen = sizeof(ngx_sockaddr_t);
-        }
-
-#if (NGX_STAT_STUB)
-        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
-#endif
-
-        c->pool = ngx_create_pool(ls->pool_size, ev->log);
-        if (c->pool == NULL) {
-            ngx_close_accepted_connection(c);
-            return;
-        }
-
-        c->sockaddr = ngx_palloc(c->pool, c->socklen);
-        if (c->sockaddr == NULL) {
-            ngx_close_accepted_connection(c);
-            return;
-        }
-
-        ngx_memcpy(c->sockaddr, msg.msg_name, c->socklen);
-
-        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
-        if (log == NULL) {
-            ngx_close_accepted_connection(c);
-            return;
-        }
-
-        *log = ls->log;
-
-        c->send = ngx_udp_send;
-        c->send_chain = ngx_udp_send_chain;
-
-        c->log = log;
-        c->pool->log = log;
-
-        c->listening = ls;
-        c->local_sockaddr = ls->sockaddr;
-        c->local_socklen = ls->socklen;
-
-#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
-
-        if (ls->wildcard) {
-            struct cmsghdr   *cmsg;
-            struct sockaddr  *sockaddr;
-
-            sockaddr = ngx_palloc(c->pool, c->local_socklen);
-            if (sockaddr == NULL) {
-                ngx_close_accepted_connection(c);
-                return;
-            }
-
-            ngx_memcpy(sockaddr, c->local_sockaddr, c->local_socklen);
-            c->local_sockaddr = sockaddr;
-
-            for (cmsg = CMSG_FIRSTHDR(&msg);
-                 cmsg != NULL;
-                 cmsg = CMSG_NXTHDR(&msg, cmsg))
-            {
-
-#if (NGX_HAVE_IP_RECVDSTADDR)
-
-                if (cmsg->cmsg_level == IPPROTO_IP
-                    && cmsg->cmsg_type == IP_RECVDSTADDR
-                    && sockaddr->sa_family == AF_INET)
-                {
-                    struct in_addr      *addr;
-                    struct sockaddr_in  *sin;
-
-                    addr = (struct in_addr *) CMSG_DATA(cmsg);
-                    sin = (struct sockaddr_in *) sockaddr;
-                    sin->sin_addr = *addr;
-
-                    break;
-                }
-
-#elif (NGX_HAVE_IP_PKTINFO)
-
-                if (cmsg->cmsg_level == IPPROTO_IP
-                    && cmsg->cmsg_type == IP_PKTINFO
-                    && sockaddr->sa_family == AF_INET)
-                {
-                    struct in_pktinfo   *pkt;
-                    struct sockaddr_in  *sin;
-
-                    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
-                    sin = (struct sockaddr_in *) sockaddr;
-                    sin->sin_addr = pkt->ipi_addr;
-
-                    break;
-                }
-
-#endif
-
-#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
-
-                if (cmsg->cmsg_level == IPPROTO_IPV6
-                    && cmsg->cmsg_type == IPV6_PKTINFO
-                    && sockaddr->sa_family == AF_INET6)
-                {
-                    struct in6_pktinfo   *pkt6;
-                    struct sockaddr_in6  *sin6;
-
-                    pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
-                    sin6 = (struct sockaddr_in6 *) sockaddr;
-                    sin6->sin6_addr = pkt6->ipi6_addr;
-
-                    break;
-                }
-
-#endif
-
-            }
-        }
-
-#endif
-
-        c->buffer = ngx_create_temp_buf(c->pool, n);
-        if (c->buffer == NULL) {
-            ngx_close_accepted_connection(c);
-            return;
-        }
-
-        c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
-
-        rev = c->read;
-        wev = c->write;
-
-        wev->ready = 1;
-
-        rev->log = log;
-        wev->log = log;
-
-        /*
-         * TODO: MT: - ngx_atomic_fetch_add()
-         *             or protection by critical section or light mutex
-         *
-         * TODO: MP: - allocated in a shared memory
-         *           - ngx_atomic_fetch_add()
-         *             or protection by critical section or light mutex
-         */
-
-        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
-
-#if (NGX_STAT_STUB)
-        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
-#endif
-
-        if (ls->addr_ntop) {
-            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
-            if (c->addr_text.data == NULL) {
-                ngx_close_accepted_connection(c);
-                return;
-            }
-
-            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
-                                             c->addr_text.data,
-                                             ls->addr_text_max_len, 0);
-            if (c->addr_text.len == 0) {
-                ngx_close_accepted_connection(c);
-                return;
-            }
-        }
-
-#if (NGX_DEBUG)
-        {
-        ngx_str_t  addr;
-        u_char     text[NGX_SOCKADDR_STRLEN];
-
-        ngx_debug_accepted_connection(ecf, c);
-
-        if (log->log_level & NGX_LOG_DEBUG_EVENT) {
-            addr.data = text;
-            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
-                                     NGX_SOCKADDR_STRLEN, 1);
-
-            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
-                           "*%uA recvmsg: %V fd:%d n:%z",
-                           c->number, &addr, c->fd, n);
-        }
-
-        }
-#endif
-
-        log->data = NULL;
-        log->handler = NULL;
-
-        ls->handler(c);
-
-        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
-            ev->available -= n;
-        }
-
-    } while (ev->available);
-}
-
-#endif
-
-
 ngx_int_t
 ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
 {
@@ -681,7 +353,7 @@
 }
 
 
-static ngx_int_t
+ngx_int_t
 ngx_enable_accept_events(ngx_cycle_t *cycle)
 {
     ngx_uint_t         i;
@@ -756,7 +428,7 @@
     fd = c->fd;
     c->fd = (ngx_socket_t) -1;
 
-    if (!c->shared && ngx_close_socket(fd) == -1) {
+    if (ngx_close_socket(fd) == -1) {
         ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
                       ngx_close_socket_n " failed");
     }
@@ -781,7 +453,7 @@
 
 #if (NGX_DEBUG)
 
-static void
+void
 ngx_debug_accepted_connection(ngx_event_conf_t *ecf, ngx_connection_t *c)
 {
     struct sockaddr_in   *sin;
diff --git a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c
new file mode 100644
index 0000000..8b0ae55
--- /dev/null
+++ b/src/event/ngx_event_udp.c
@@ -0,0 +1,641 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if !(NGX_WIN32)
+
+struct ngx_udp_connection_s {
+    ngx_rbtree_node_t   node;
+    ngx_connection_t   *connection;
+    ngx_buf_t          *buffer;
+};
+
+
+static void ngx_close_accepted_udp_connection(ngx_connection_t *c);
+static ssize_t ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf,
+    size_t size);
+static ngx_int_t ngx_insert_udp_connection(ngx_connection_t *c);
+static void ngx_delete_udp_connection(void *data);
+static ngx_connection_t *ngx_lookup_udp_connection(ngx_listening_t *ls,
+    struct sockaddr *sockaddr, socklen_t socklen,
+    struct sockaddr *local_sockaddr, socklen_t local_socklen);
+
+
+void
+ngx_event_recvmsg(ngx_event_t *ev)
+{
+    ssize_t            n;
+    ngx_buf_t          buf;
+    ngx_log_t         *log;
+    ngx_err_t          err;
+    socklen_t          socklen, local_socklen;
+    ngx_event_t       *rev, *wev;
+    struct iovec       iov[1];
+    struct msghdr      msg;
+    ngx_sockaddr_t     sa, lsa;
+    struct sockaddr   *sockaddr, *local_sockaddr;
+    ngx_listening_t   *ls;
+    ngx_event_conf_t  *ecf;
+    ngx_connection_t  *c, *lc;
+    static u_char      buffer[65535];
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+#if (NGX_HAVE_IP_RECVDSTADDR)
+    u_char             msg_control[CMSG_SPACE(sizeof(struct in_addr))];
+#elif (NGX_HAVE_IP_PKTINFO)
+    u_char             msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#endif
+
+#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
+    u_char             msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#endif
+
+#endif
+
+    if (ev->timedout) {
+        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
+            return;
+        }
+
+        ev->timedout = 0;
+    }
+
+    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
+
+    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
+        ev->available = ecf->multi_accept;
+    }
+
+    lc = ev->data;
+    ls = lc->listening;
+    ev->ready = 0;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "recvmsg on %V, ready: %d", &ls->addr_text, ev->available);
+
+    do {
+        ngx_memzero(&msg, sizeof(struct msghdr));
+
+        iov[0].iov_base = (void *) buffer;
+        iov[0].iov_len = sizeof(buffer);
+
+        msg.msg_name = &sa;
+        msg.msg_namelen = sizeof(ngx_sockaddr_t);
+        msg.msg_iov = iov;
+        msg.msg_iovlen = 1;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+        if (ls->wildcard) {
+
+#if (NGX_HAVE_IP_RECVDSTADDR || NGX_HAVE_IP_PKTINFO)
+            if (ls->sockaddr->sa_family == AF_INET) {
+                msg.msg_control = &msg_control;
+                msg.msg_controllen = sizeof(msg_control);
+            }
+#endif
+
+#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
+            if (ls->sockaddr->sa_family == AF_INET6) {
+                msg.msg_control = &msg_control6;
+                msg.msg_controllen = sizeof(msg_control6);
+            }
+#endif
+        }
+
+#endif
+
+        n = recvmsg(lc->fd, &msg, 0);
+
+        if (n == -1) {
+            err = ngx_socket_errno;
+
+            if (err == NGX_EAGAIN) {
+                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
+                               "recvmsg() not ready");
+                return;
+            }
+
+            ngx_log_error(NGX_LOG_ALERT, ev->log, err, "recvmsg() failed");
+
+            return;
+        }
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+        if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                          "recvmsg() truncated data");
+            continue;
+        }
+#endif
+
+        sockaddr = msg.msg_name;
+        socklen = msg.msg_namelen;
+
+        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
+            socklen = sizeof(ngx_sockaddr_t);
+        }
+
+        if (socklen == 0) {
+
+            /*
+             * on Linux recvmsg() returns zero msg_namelen
+             * when receiving packets from unbound AF_UNIX sockets
+             */
+
+            socklen = sizeof(struct sockaddr);
+            ngx_memzero(&sa, sizeof(struct sockaddr));
+            sa.sockaddr.sa_family = ls->sockaddr->sa_family;
+        }
+
+        local_sockaddr = ls->sockaddr;
+        local_socklen = ls->socklen;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+        if (ls->wildcard) {
+            struct cmsghdr  *cmsg;
+
+            ngx_memcpy(&lsa, local_sockaddr, local_socklen);
+            local_sockaddr = &lsa.sockaddr;
+
+            for (cmsg = CMSG_FIRSTHDR(&msg);
+                 cmsg != NULL;
+                 cmsg = CMSG_NXTHDR(&msg, cmsg))
+            {
+
+#if (NGX_HAVE_IP_RECVDSTADDR)
+
+                if (cmsg->cmsg_level == IPPROTO_IP
+                    && cmsg->cmsg_type == IP_RECVDSTADDR
+                    && local_sockaddr->sa_family == AF_INET)
+                {
+                    struct in_addr      *addr;
+                    struct sockaddr_in  *sin;
+
+                    addr = (struct in_addr *) CMSG_DATA(cmsg);
+                    sin = (struct sockaddr_in *) local_sockaddr;
+                    sin->sin_addr = *addr;
+
+                    break;
+                }
+
+#elif (NGX_HAVE_IP_PKTINFO)
+
+                if (cmsg->cmsg_level == IPPROTO_IP
+                    && cmsg->cmsg_type == IP_PKTINFO
+                    && local_sockaddr->sa_family == AF_INET)
+                {
+                    struct in_pktinfo   *pkt;
+                    struct sockaddr_in  *sin;
+
+                    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
+                    sin = (struct sockaddr_in *) local_sockaddr;
+                    sin->sin_addr = pkt->ipi_addr;
+
+                    break;
+                }
+
+#endif
+
+#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
+
+                if (cmsg->cmsg_level == IPPROTO_IPV6
+                    && cmsg->cmsg_type == IPV6_PKTINFO
+                    && local_sockaddr->sa_family == AF_INET6)
+                {
+                    struct in6_pktinfo   *pkt6;
+                    struct sockaddr_in6  *sin6;
+
+                    pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+                    sin6 = (struct sockaddr_in6 *) local_sockaddr;
+                    sin6->sin6_addr = pkt6->ipi6_addr;
+
+                    break;
+                }
+
+#endif
+
+            }
+        }
+
+#endif
+
+        c = ngx_lookup_udp_connection(ls, sockaddr, socklen, local_sockaddr,
+                                      local_socklen);
+
+        if (c) {
+
+#if (NGX_DEBUG)
+            if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+                ngx_log_handler_pt  handler;
+
+                handler = c->log->handler;
+                c->log->handler = NULL;
+
+                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "recvmsg: fd:%d n:%z", c->fd, n);
+
+                c->log->handler = handler;
+            }
+#endif
+
+            ngx_memzero(&buf, sizeof(ngx_buf_t));
+
+            buf.pos = buffer;
+            buf.last = buffer + n;
+
+            rev = c->read;
+
+            c->udp->buffer = &buf;
+            rev->ready = 1;
+
+            rev->handler(rev);
+
+            c->udp->buffer = NULL;
+            rev->ready = 0;
+
+            goto next;
+        }
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
+#endif
+
+        ngx_accept_disabled = ngx_cycle->connection_n / 8
+                              - ngx_cycle->free_connection_n;
+
+        c = ngx_get_connection(lc->fd, ev->log);
+        if (c == NULL) {
+            return;
+        }
+
+        c->shared = 1;
+        c->type = SOCK_DGRAM;
+        c->socklen = socklen;
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
+#endif
+
+        c->pool = ngx_create_pool(ls->pool_size, ev->log);
+        if (c->pool == NULL) {
+            ngx_close_accepted_udp_connection(c);
+            return;
+        }
+
+        c->sockaddr = ngx_palloc(c->pool, socklen);
+        if (c->sockaddr == NULL) {
+            ngx_close_accepted_udp_connection(c);
+            return;
+        }
+
+        ngx_memcpy(c->sockaddr, sockaddr, socklen);
+
+        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
+        if (log == NULL) {
+            ngx_close_accepted_udp_connection(c);
+            return;
+        }
+
+        *log = ls->log;
+
+        c->recv = ngx_udp_shared_recv;
+        c->send = ngx_udp_send;
+        c->send_chain = ngx_udp_send_chain;
+
+        c->log = log;
+        c->pool->log = log;
+        c->listening = ls;
+
+        if (local_sockaddr == &lsa.sockaddr) {
+            local_sockaddr = ngx_palloc(c->pool, local_socklen);
+            if (local_sockaddr == NULL) {
+                ngx_close_accepted_udp_connection(c);
+                return;
+            }
+
+            ngx_memcpy(local_sockaddr, &lsa, local_socklen);
+        }
+
+        c->local_sockaddr = local_sockaddr;
+        c->local_socklen = local_socklen;
+
+        c->buffer = ngx_create_temp_buf(c->pool, n);
+        if (c->buffer == NULL) {
+            ngx_close_accepted_udp_connection(c);
+            return;
+        }
+
+        c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
+
+        rev = c->read;
+        wev = c->write;
+
+        wev->ready = 1;
+
+        rev->log = log;
+        wev->log = log;
+
+        /*
+         * TODO: MT: - ngx_atomic_fetch_add()
+         *             or protection by critical section or light mutex
+         *
+         * TODO: MP: - allocated in a shared memory
+         *           - ngx_atomic_fetch_add()
+         *             or protection by critical section or light mutex
+         */
+
+        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
+#endif
+
+        if (ls->addr_ntop) {
+            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
+            if (c->addr_text.data == NULL) {
+                ngx_close_accepted_udp_connection(c);
+                return;
+            }
+
+            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
+                                             c->addr_text.data,
+                                             ls->addr_text_max_len, 0);
+            if (c->addr_text.len == 0) {
+                ngx_close_accepted_udp_connection(c);
+                return;
+            }
+        }
+
+#if (NGX_DEBUG)
+        {
+        ngx_str_t  addr;
+        u_char     text[NGX_SOCKADDR_STRLEN];
+
+        ngx_debug_accepted_connection(ecf, c);
+
+        if (log->log_level & NGX_LOG_DEBUG_EVENT) {
+            addr.data = text;
+            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
+                                     NGX_SOCKADDR_STRLEN, 1);
+
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
+                           "*%uA recvmsg: %V fd:%d n:%z",
+                           c->number, &addr, c->fd, n);
+        }
+
+        }
+#endif
+
+        if (ngx_insert_udp_connection(c) != NGX_OK) {
+            ngx_close_accepted_udp_connection(c);
+            return;
+        }
+
+        log->data = NULL;
+        log->handler = NULL;
+
+        ls->handler(c);
+
+    next:
+
+        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+            ev->available -= n;
+        }
+
+    } while (ev->available);
+}
+
+
+static void
+ngx_close_accepted_udp_connection(ngx_connection_t *c)
+{
+    ngx_free_connection(c);
+
+    c->fd = (ngx_socket_t) -1;
+
+    if (c->pool) {
+        ngx_destroy_pool(c->pool);
+    }
+
+#if (NGX_STAT_STUB)
+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+}
+
+
+static ssize_t
+ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+    ssize_t     n;
+    ngx_buf_t  *b;
+
+    if (c->udp == NULL || c->udp->buffer == NULL) {
+        return NGX_AGAIN;
+    }
+
+    b = c->udp->buffer;
+
+    n = ngx_min(b->last - b->pos, (ssize_t) size);
+
+    ngx_memcpy(buf, b->pos, n);
+
+    c->udp->buffer = NULL;
+    c->read->ready = 0;
+
+    return n;
+}
+
+
+void
+ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_int_t               rc;
+    ngx_connection_t       *c, *ct;
+    ngx_rbtree_node_t     **p;
+    ngx_udp_connection_t   *udp, *udpt;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            udp = (ngx_udp_connection_t *) node;
+            c = udp->connection;
+
+            udpt = (ngx_udp_connection_t *) temp;
+            ct = udpt->connection;
+
+            rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen,
+                                  ct->sockaddr, ct->socklen, 1);
+
+            if (rc == 0 && c->listening->wildcard) {
+                rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
+                                      ct->local_sockaddr, ct->local_socklen, 1);
+            }
+
+            p = (rc < 0) ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_insert_udp_connection(ngx_connection_t *c)
+{
+    uint32_t               hash;
+    ngx_pool_cleanup_t    *cln;
+    ngx_udp_connection_t  *udp;
+
+    if (c->udp) {
+        return NGX_OK;
+    }
+
+    udp = ngx_pcalloc(c->pool, sizeof(ngx_udp_connection_t));
+    if (udp == NULL) {
+        return NGX_ERROR;
+    }
+
+    udp->connection = c;
+
+    ngx_crc32_init(hash);
+    ngx_crc32_update(&hash, (u_char *) c->sockaddr, c->socklen);
+
+    if (c->listening->wildcard) {
+        ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen);
+    }
+
+    ngx_crc32_final(hash);
+
+    udp->node.key = hash;
+
+    cln = ngx_pool_cleanup_add(c->pool, 0);
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    cln->data = c;
+    cln->handler = ngx_delete_udp_connection;
+
+    ngx_rbtree_insert(&c->listening->rbtree, &udp->node);
+
+    c->udp = udp;
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_delete_udp_connection(void *data)
+{
+    ngx_connection_t  *c = data;
+
+    ngx_rbtree_delete(&c->listening->rbtree, &c->udp->node);
+}
+
+
+static ngx_connection_t *
+ngx_lookup_udp_connection(ngx_listening_t *ls, struct sockaddr *sockaddr,
+    socklen_t socklen, struct sockaddr *local_sockaddr, socklen_t local_socklen)
+{
+    uint32_t               hash;
+    ngx_int_t              rc;
+    ngx_connection_t      *c;
+    ngx_rbtree_node_t     *node, *sentinel;
+    ngx_udp_connection_t  *udp;
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+    if (sockaddr->sa_family == AF_UNIX) {
+        struct sockaddr_un *saun = (struct sockaddr_un *) sockaddr;
+
+        if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)
+            || saun->sun_path[0] == '\0')
+        {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+                           "unbound unix socket");
+            return NULL;
+        }
+    }
+
+#endif
+
+    node = ls->rbtree.root;
+    sentinel = ls->rbtree.sentinel;
+
+    ngx_crc32_init(hash);
+    ngx_crc32_update(&hash, (u_char *) sockaddr, socklen);
+
+    if (ls->wildcard) {
+        ngx_crc32_update(&hash, (u_char *) local_sockaddr, local_socklen);
+    }
+
+    ngx_crc32_final(hash);
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        udp = (ngx_udp_connection_t *) node;
+
+        c = udp->connection;
+
+        rc = ngx_cmp_sockaddr(sockaddr, socklen,
+                              c->sockaddr, c->socklen, 1);
+
+        if (rc == 0 && ls->wildcard) {
+            rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen,
+                                  c->local_sockaddr, c->local_socklen, 1);
+        }
+
+        if (rc == 0) {
+            return c;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
+    }
+
+    return NULL;
+}
+
+#endif
diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
index bc43f53..3eec1b7 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -3264,7 +3264,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
         copy = ngx_array_push_n(params->lengths,
@@ -3273,7 +3274,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].skip_empty;
 
 
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 837ad9a..300d927 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -4389,7 +4389,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
         size = (sizeof(ngx_http_script_copy_code_t)
diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c
index 9e7def8..6648c4d 100644
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -399,7 +399,14 @@
 
             ms = (ngx_msec_int_t) (now - lr->last);
 
-            excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+            if (ms < -60000) {
+                ms = 1;
+
+            } else if (ms < 0) {
+                ms = 0;
+            }
+
+            excess = lr->excess - ctx->rate * ms / 1000 + 1000;
 
             if (excess < 0) {
                 excess = 0;
@@ -413,7 +420,11 @@
 
             if (account) {
                 lr->excess = excess;
-                lr->last = now;
+
+                if (ms) {
+                    lr->last = now;
+                }
+
                 return NGX_OK;
             }
 
@@ -509,13 +520,23 @@
         now = ngx_current_msec;
         ms = (ngx_msec_int_t) (now - lr->last);
 
-        excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+        if (ms < -60000) {
+            ms = 1;
+
+        } else if (ms < 0) {
+            ms = 0;
+        }
+
+        excess = lr->excess - ctx->rate * ms / 1000 + 1000;
 
         if (excess < 0) {
             excess = 0;
         }
 
-        lr->last = now;
+        if (ms) {
+            lr->last = now;
+        }
+
         lr->excess = excess;
         lr->count--;
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 8c2f8cb..37dc9e8 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -3523,7 +3523,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
         size = (sizeof(ngx_http_script_copy_code_t)
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
index 3fb227b..9bd45bd 100644
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -1724,7 +1724,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].key.len + 1;
 
         copy = ngx_array_push_n(params->lengths,
@@ -1733,7 +1734,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].skip_empty;
 
 
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 0ac8596..37438bd 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -69,6 +69,11 @@
 };
 
 
+static ngx_conf_deprecated_t  ngx_http_ssl_deprecated = {
+    ngx_conf_deprecated, "ssl", "listen ... ssl"
+};
+
+
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -76,7 +81,7 @@
       ngx_http_ssl_enable,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, enable),
-      NULL },
+      &ngx_http_ssl_deprecated },
 
     { ngx_string("ssl_certificate"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
@@ -964,10 +969,12 @@
 static ngx_int_t
 ngx_http_ssl_init(ngx_conf_t *cf)
 {
-    ngx_uint_t                   s;
+    ngx_uint_t                   a, p, s;
+    ngx_http_conf_addr_t        *addr;
+    ngx_http_conf_port_t        *port;
     ngx_http_ssl_srv_conf_t     *sscf;
     ngx_http_core_loc_conf_t    *clcf;
-    ngx_http_core_srv_conf_t   **cscfp;
+    ngx_http_core_srv_conf_t   **cscfp, *cscf;
     ngx_http_core_main_conf_t   *cmcf;
 
     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
@@ -991,5 +998,32 @@
         }
     }
 
+    if (cmcf->ports == NULL) {
+        return NGX_OK;
+    }
+
+    port = cmcf->ports->elts;
+    for (p = 0; p < cmcf->ports->nelts; p++) {
+
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
+
+            if (!addr[a].opt.ssl) {
+                continue;
+            }
+
+            cscf = addr[a].default_server;
+            sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+
+            if (sscf->certificates == NULL) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate\" is defined for "
+                              "the \"listen ... ssl\" directive in %s:%ui",
+                              cscf->file_name, cscf->line);
+                return NGX_ERROR;
+            }
+        }
+    }
+
     return NGX_OK;
 }
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 124da4d..238bcf8 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -1987,7 +1987,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
         copy = ngx_array_push_n(params->lengths,
@@ -1996,7 +1997,8 @@
             return NGX_ERROR;
         }
 
-        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
         copy->len = src[i].skip_empty;
 
 
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 0153a15..7a57739 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -3256,6 +3256,9 @@
     cscf->merge_slashes = NGX_CONF_UNSET;
     cscf->underscores_in_headers = NGX_CONF_UNSET;
 
+    cscf->file_name = cf->conf_file->file.name.data;
+    cscf->line = cf->conf_file->line;
+
     return cscf;
 }
 
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 66c029f..f53b5f2 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -184,6 +184,9 @@
     /* server ctx */
     ngx_http_conf_ctx_t        *ctx;
 
+    u_char                     *file_name;
+    ngx_uint_t                  line;
+
     ngx_str_t                   server_name;
 
     size_t                      connection_pool_size;
diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
index 3b2b68a..56866fa 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -2620,7 +2620,8 @@
 
     time_t                    valid;
     ngx_str_t                *value;
-    ngx_uint_t                i, n, status;
+    ngx_int_t                 status;
+    ngx_uint_t                i, n;
     ngx_array_t             **a;
     ngx_http_cache_valid_t   *v;
     static ngx_uint_t         statuses[] = { 200, 301, 302 };
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index b46922b..808d1c1 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -336,19 +336,8 @@
     sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
 
     if (sscf->enable || hc->addr_conf->ssl) {
-
-        c->log->action = "SSL handshaking";
-
-        if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
-            ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                          "no \"ssl_certificate\" is defined "
-                          "in server listening on SSL port");
-            ngx_http_close_connection(c);
-            return;
-        }
-
         hc->ssl = 1;
-
+        c->log->action = "SSL handshaking";
         rev->handler = ngx_http_ssl_handshake;
     }
     }
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 96f3ec6..1a87735 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -695,7 +695,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+    code->code = (ngx_http_script_code_pt) (void *)
+                                                 ngx_http_script_copy_len_code;
     code->len = len;
 
     size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
@@ -784,7 +785,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;
+    code->code = (ngx_http_script_code_pt) (void *)
+                                             ngx_http_script_copy_var_len_code;
     code->index = (uintptr_t) index;
 
     code = ngx_http_script_add_code(*sc->values,
@@ -1178,8 +1180,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_http_script_code_pt)
-                      ngx_http_script_copy_capture_len_code;
+    code->code = (ngx_http_script_code_pt) (void *)
+                                         ngx_http_script_copy_capture_len_code;
     code->n = 2 * n;
 
 
@@ -1293,7 +1295,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code;
+    code->code = (ngx_http_script_code_pt) (void *)
+                                            ngx_http_script_full_name_len_code;
     code->conf_prefix = sc->conf_prefix;
 
     code = ngx_http_script_add_code(*sc->values,
diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c
index 276b8ee..dd4e980 100644
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -474,7 +474,16 @@
 
         if (ngx_strcmp(value[i].data, "ssl") == 0) {
 #if (NGX_MAIL_SSL)
+            ngx_mail_ssl_conf_t  *sslcf;
+
+            sslcf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_ssl_module);
+
+            sslcf->listen = 1;
+            sslcf->file = cf->conf_file->file.name.data;
+            sslcf->line = cf->conf_file->line;
+
             ls->ssl = 1;
+
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
index bc3e6b9..803a247 100644
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -165,29 +165,13 @@
 
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
-    if (sslcf->enable) {
+    if (sslcf->enable || addr_conf->ssl) {
         c->log->action = "SSL handshaking";
 
         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
         return;
     }
 
-    if (addr_conf->ssl) {
-
-        c->log->action = "SSL handshaking";
-
-        if (sslcf->ssl.ctx == NULL) {
-            ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                          "no \"ssl_certificate\" is defined "
-                          "in server listening on SSL port");
-            ngx_mail_close_connection(c);
-            return;
-        }
-
-        ngx_mail_ssl_init_connection(&sslcf->ssl, c);
-        return;
-    }
-
     }
 #endif
 
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index aebd179..4509597 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -56,6 +56,11 @@
 };
 
 
+static ngx_conf_deprecated_t  ngx_mail_ssl_deprecated = {
+    ngx_conf_deprecated, "ssl", "listen ... ssl"
+};
+
+
 static ngx_command_t  ngx_mail_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -63,7 +68,7 @@
       ngx_mail_ssl_enable,
       NGX_MAIL_SRV_CONF_OFFSET,
       offsetof(ngx_mail_ssl_conf_t, enable),
-      NULL },
+      &ngx_mail_ssl_deprecated },
 
     { ngx_string("starttls"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
@@ -238,6 +243,7 @@
     /*
      * set by ngx_pcalloc():
      *
+     *     scf->listen = 0;
      *     scf->protocols = 0;
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
@@ -313,14 +319,17 @@
 
     conf->ssl.log = cf->log;
 
-    if (conf->enable) {
+    if (conf->listen) {
+        mode = "listen ... ssl";
+
+    } else if (conf->enable) {
         mode = "ssl";
 
     } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {
         mode = "starttls";
 
     } else {
-        mode = "";
+        return NGX_CONF_OK;
     }
 
     if (conf->file == NULL) {
@@ -328,51 +337,31 @@
         conf->line = prev->line;
     }
 
-    if (*mode) {
+    if (conf->certificates == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no \"ssl_certificate\" is defined for "
+                      "the \"%s\" directive in %s:%ui",
+                      mode, conf->file, conf->line);
+        return NGX_CONF_ERROR;
+    }
 
-        if (conf->certificates == NULL) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate\" is defined for "
-                          "the \"%s\" directive in %s:%ui",
-                          mode, conf->file, conf->line);
-            return NGX_CONF_ERROR;
-        }
+    if (conf->certificate_keys == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no \"ssl_certificate_key\" is defined for "
+                      "the \"%s\" directive in %s:%ui",
+                      mode, conf->file, conf->line);
+        return NGX_CONF_ERROR;
+    }
 
-        if (conf->certificate_keys == NULL) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate_key\" is defined for "
-                          "the \"%s\" directive in %s:%ui",
-                          mode, conf->file, conf->line);
-            return NGX_CONF_ERROR;
-        }
-
-        if (conf->certificate_keys->nelts < conf->certificates->nelts) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate_key\" is defined "
-                          "for certificate \"%V\" and "
-                          "the \"ssl\" directive in %s:%ui",
-                          ((ngx_str_t *) conf->certificates->elts)
-                          + conf->certificates->nelts - 1,
-                          conf->file, conf->line);
-            return NGX_CONF_ERROR;
-        }
-
-    } else {
-
-        if (conf->certificates == NULL) {
-            return NGX_CONF_OK;
-        }
-
-        if (conf->certificate_keys == NULL
-            || conf->certificate_keys->nelts < conf->certificates->nelts)
-        {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "no \"ssl_certificate_key\" is defined "
-                          "for certificate \"%V\"",
-                          ((ngx_str_t *) conf->certificates->elts)
-                          + conf->certificates->nelts - 1);
-            return NGX_CONF_ERROR;
-        }
+    if (conf->certificate_keys->nelts < conf->certificates->nelts) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no \"ssl_certificate_key\" is defined "
+                      "for certificate \"%V\" and "
+                      "the \"%s\" directive in %s:%ui",
+                      ((ngx_str_t *) conf->certificates->elts)
+                      + conf->certificates->nelts - 1,
+                      mode, conf->file, conf->line);
+        return NGX_CONF_ERROR;
     }
 
     if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
@@ -494,8 +483,10 @@
         return NGX_CONF_ERROR;
     }
 
-    scf->file = cf->conf_file->file.name.data;
-    scf->line = cf->conf_file->line;
+    if (!scf->listen) {
+        scf->file = cf->conf_file->file.name.data;
+        scf->line = cf->conf_file->line;
+    }
 
     return NGX_CONF_OK;
 }
@@ -520,8 +511,10 @@
         return NGX_CONF_ERROR;
     }
 
-    scf->file = cf->conf_file->file.name.data;
-    scf->line = cf->conf_file->line;
+    if (!scf->listen) {
+        scf->file = cf->conf_file->file.name.data;
+        scf->line = cf->conf_file->line;
+    }
 
     return NGX_CONF_OK;
 }
diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h
index 26628d5..d6b0b8e 100644
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -26,6 +26,7 @@
     ngx_ssl_t        ssl;
 
     ngx_uint_t       starttls;
+    ngx_uint_t       listen;
     ngx_uint_t       protocols;
 
     ngx_uint_t       verify;
diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
index 7ebe2b5..b3d81d0 100644
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -21,10 +21,6 @@
     struct crypt_data   cd;
 
     cd.initialized = 0;
-#ifdef __GLIBC__
-    /* work around the glibc bug */
-    cd.current_salt[0] = ~salt[0];
-#endif
 
     value = crypt_r((char *) key, (char *) salt, &cd);
 
diff --git a/src/os/win32/ngx_time.c b/src/os/win32/ngx_time.c
index bd6d287..79149b2 100644
--- a/src/os/win32/ngx_time.c
+++ b/src/os/win32/ngx_time.c
@@ -23,7 +23,7 @@
      * January 1, 1601 12:00 A.M. UTC.
      *
      * Between January 1, 1970 (Epoch) and January 1, 1601 there were
-     * 134744 days,
+     * 134774 days,
      * 11644473600 seconds or
      * 11644473600,000,000,0 100-nanosecond intervals.
      *
diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
index 272708d..96e7c9a 100644
--- a/src/stream/ngx_stream_core_module.c
+++ b/src/stream/ngx_stream_core_module.c
@@ -734,7 +734,17 @@
 
         if (ngx_strcmp(value[i].data, "ssl") == 0) {
 #if (NGX_STREAM_SSL)
+            ngx_stream_ssl_conf_t  *sslcf;
+
+            sslcf = ngx_stream_conf_get_module_srv_conf(cf,
+                                                        ngx_stream_ssl_module);
+
+            sslcf->listen = 1;
+            sslcf->file = cf->conf_file->file.name.data;
+            sslcf->line = cf->conf_file->line;
+
             ls->ssl = 1;
+
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index 30572cd..792bb77 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -377,6 +377,8 @@
 
     s->log_handler = ngx_stream_proxy_log_error;
 
+    u->requests = 1;
+
     u->peer.log = c->log;
     u->peer.log_error = NGX_ERROR_ERR;
 
@@ -398,21 +400,19 @@
         return;
     }
 
-    if (c->type == SOCK_STREAM) {
-        p = ngx_pnalloc(c->pool, pscf->buffer_size);
-        if (p == NULL) {
-            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
-            return;
-        }
+    p = ngx_pnalloc(c->pool, pscf->buffer_size);
+    if (p == NULL) {
+        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+        return;
+    }
 
-        u->downstream_buf.start = p;
-        u->downstream_buf.end = p + pscf->buffer_size;
-        u->downstream_buf.pos = p;
-        u->downstream_buf.last = p;
+    u->downstream_buf.start = p;
+    u->downstream_buf.end = p + pscf->buffer_size;
+    u->downstream_buf.pos = p;
+    u->downstream_buf.last = p;
 
-        if (c->read->ready) {
-            ngx_post_event(c->read, &ngx_posted_events);
-        }
+    if (c->read->ready) {
+        ngx_post_event(c->read, &ngx_posted_events);
     }
 
     if (pscf->upstream_value) {
@@ -829,7 +829,6 @@
 
         cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module;
         cl->buf->flush = 1;
-        cl->buf->last_buf = (c->type == SOCK_DGRAM);
 
         cl->next = u->upstream_out;
         u->upstream_out = cl;
@@ -871,17 +870,12 @@
         u->proxy_protocol = 0;
     }
 
-    if (c->type == SOCK_DGRAM && pscf->responses == 0) {
-        pc->read->ready = 0;
-        pc->read->eof = 1;
-    }
-
     u->connected = 1;
 
     pc->read->handler = ngx_stream_proxy_upstream_handler;
     pc->write->handler = ngx_stream_proxy_upstream_handler;
 
-    if (pc->read->ready || pc->read->eof) {
+    if (pc->read->ready) {
         ngx_post_event(pc->read, &ngx_posted_events);
     }
 
@@ -1280,6 +1274,7 @@
 ngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream)
 {
     ngx_connection_t             *c, *pc;
+    ngx_log_handler_pt            handler;
     ngx_stream_session_t         *s;
     ngx_stream_upstream_t        *u;
     ngx_stream_proxy_srv_conf_t  *pscf;
@@ -1328,25 +1323,37 @@
                      * with unspecified number of responses
                      */
 
-                    pc->read->ready = 0;
-                    pc->read->eof = 1;
+                    handler = c->log->handler;
+                    c->log->handler = NULL;
 
-                    ngx_stream_proxy_process(s, 1, 0);
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                                  "udp timed out"
+                                  ", packets from/to client:%ui/%ui"
+                                  ", bytes from/to client:%O/%O"
+                                  ", bytes from/to upstream:%O/%O",
+                                  u->requests, u->responses,
+                                  s->received, c->sent, u->received,
+                                  pc ? pc->sent : 0);
+
+                    c->log->handler = handler;
+
+                    ngx_stream_proxy_finalize(s, NGX_STREAM_OK);
                     return;
                 }
 
                 ngx_connection_error(pc, NGX_ETIMEDOUT, "upstream timed out");
 
-                if (u->received == 0) {
-                    ngx_stream_proxy_next_upstream(s);
-                    return;
-                }
+                pc->read->error = 1;
 
-            } else {
-                ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out");
+                ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY);
+
+                return;
             }
 
+            ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out");
+
             ngx_stream_proxy_finalize(s, NGX_STREAM_OK);
+
             return;
         }
 
@@ -1453,7 +1460,7 @@
     ssize_t                       n;
     ngx_buf_t                    *b;
     ngx_int_t                     rc;
-    ngx_uint_t                    flags;
+    ngx_uint_t                    flags, *packets;
     ngx_msec_t                    delay;
     ngx_chain_t                  *cl, **ll, **out, **busy;
     ngx_connection_t             *c, *pc, *src, *dst;
@@ -1489,6 +1496,7 @@
         b = &u->upstream_buf;
         limit_rate = pscf->download_rate;
         received = &u->received;
+        packets = &u->responses;
         out = &u->downstream_out;
         busy = &u->downstream_busy;
         recv_action = "proxying and reading from upstream";
@@ -1500,6 +1508,7 @@
         b = &u->downstream_buf;
         limit_rate = pscf->upload_rate;
         received = &s->received;
+        packets = &u->requests;
         out = &u->upstream_out;
         busy = &u->upstream_busy;
         recv_action = "proxying and reading from client";
@@ -1516,11 +1525,6 @@
                 rc = ngx_stream_top_filter(s, *out, from_upstream);
 
                 if (rc == NGX_ERROR) {
-                    if (c->type == SOCK_DGRAM && !from_upstream) {
-                        ngx_stream_proxy_next_upstream(s);
-                        return;
-                    }
-
                     ngx_stream_proxy_finalize(s, NGX_STREAM_OK);
                     return;
                 }
@@ -1565,11 +1569,6 @@
             }
 
             if (n == NGX_ERROR) {
-                if (c->type == SOCK_DGRAM && u->received == 0) {
-                    ngx_stream_proxy_next_upstream(s);
-                    return;
-                }
-
                 src->read->eof = 1;
                 n = 0;
             }
@@ -1591,12 +1590,6 @@
                     }
                 }
 
-                if (c->type == SOCK_DGRAM && ++u->responses == pscf->responses)
-                {
-                    src->read->ready = 0;
-                    src->read->eof = 1;
-                }
-
                 for (ll = out; *ll; ll = &(*ll)->next) { /* void */ }
 
                 cl = ngx_chain_get_free_buf(c->pool, &u->free);
@@ -1616,6 +1609,7 @@
                 cl->buf->last_buf = src->read->eof;
                 cl->buf->flush = 1;
 
+                (*packets)++;
                 *received += n;
                 b->last += n;
                 do_write = 1;
@@ -1629,15 +1623,38 @@
 
     c->log->action = "proxying connection";
 
-    if (src->read->eof && dst && (dst->read->eof || !dst->buffered)) {
+    if (c->type == SOCK_DGRAM
+        && pscf->responses != NGX_MAX_INT32_VALUE
+        && u->responses >= pscf->responses * u->requests
+        && !src->buffered && dst && !dst->buffered)
+    {
         handler = c->log->handler;
         c->log->handler = NULL;
 
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "%s%s disconnected"
+                      "udp done"
+                      ", packets from/to client:%ui/%ui"
                       ", bytes from/to client:%O/%O"
                       ", bytes from/to upstream:%O/%O",
-                      src->type == SOCK_DGRAM ? "udp " : "",
+                      u->requests, u->responses,
+                      s->received, c->sent, u->received, pc ? pc->sent : 0);
+
+        c->log->handler = handler;
+
+        ngx_stream_proxy_finalize(s, NGX_STREAM_OK);
+        return;
+    }
+
+    if (c->type == SOCK_STREAM
+        && src->read->eof && dst && (dst->read->eof || !dst->buffered))
+    {
+        handler = c->log->handler;
+        c->log->handler = NULL;
+
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "%s disconnected"
+                      ", bytes from/to client:%O/%O"
+                      ", bytes from/to upstream:%O/%O",
                       from_upstream ? "upstream" : "client",
                       s->received, c->sent, u->received, pc ? pc->sent : 0);
 
@@ -1739,6 +1756,7 @@
 static void
 ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc)
 {
+    ngx_uint_t              state;
     ngx_connection_t       *pc;
     ngx_stream_upstream_t  *u;
 
@@ -1768,7 +1786,15 @@
     }
 
     if (u->peer.free && u->peer.sockaddr) {
-        u->peer.free(&u->peer, u->peer.data, 0);
+        state = 0;
+
+        if (pc && pc->type == SOCK_DGRAM
+            && (pc->read->error || pc->write->error))
+        {
+            state = NGX_PEER_FAILED;
+        }
+
+        u->peer.free(&u->peer, u->peer.data, state);
         u->peer.sockaddr = NULL;
     }
 
diff --git a/src/stream/ngx_stream_script.c b/src/stream/ngx_stream_script.c
index aa555ca..b00e708 100644
--- a/src/stream/ngx_stream_script.c
+++ b/src/stream/ngx_stream_script.c
@@ -587,7 +587,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_stream_script_code_pt) ngx_stream_script_copy_len_code;
+    code->code = (ngx_stream_script_code_pt) (void *)
+                                               ngx_stream_script_copy_len_code;
     code->len = len;
 
     size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
@@ -677,8 +678,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_stream_script_code_pt)
-                      ngx_stream_script_copy_var_len_code;
+    code->code = (ngx_stream_script_code_pt) (void *)
+                                           ngx_stream_script_copy_var_len_code;
     code->index = (uintptr_t) index;
 
     code = ngx_stream_script_add_code(*sc->values,
@@ -767,8 +768,8 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_stream_script_code_pt)
-                      ngx_stream_script_copy_capture_len_code;
+    code->code = (ngx_stream_script_code_pt) (void *)
+                                       ngx_stream_script_copy_capture_len_code;
     code->n = 2 * n;
 
 
@@ -859,7 +860,7 @@
         return NGX_ERROR;
     }
 
-    code->code = (ngx_stream_script_code_pt)
+    code->code = (ngx_stream_script_code_pt) (void *)
                                           ngx_stream_script_full_name_len_code;
     code->conf_prefix = sc->conf_prefix;
 
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index 3e5a1f2..dcc33e1 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -304,13 +304,6 @@
     if (c->ssl == NULL) {
         c->log->action = "SSL handshaking";
 
-        if (sslcf->ssl.ctx == NULL) {
-            ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                          "no \"ssl_certificate\" is defined "
-                          "in server listening on SSL port");
-            return NGX_ERROR;
-        }
-
         rv = ngx_stream_ssl_init_connection(&sslcf->ssl, c);
 
         if (rv != NGX_OK) {
@@ -510,6 +503,7 @@
     /*
      * set by ngx_pcalloc():
      *
+     *     scf->listen = 0;
      *     scf->protocols = 0;
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
@@ -582,18 +576,34 @@
 
     conf->ssl.log = cf->log;
 
-    if (conf->certificates == NULL) {
+    if (!conf->listen) {
         return NGX_CONF_OK;
     }
 
-    if (conf->certificate_keys == NULL
-        || conf->certificate_keys->nelts < conf->certificates->nelts)
-    {
+    if (conf->certificates == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no \"ssl_certificate\" is defined for "
+                      "the \"listen ... ssl\" directive in %s:%ui",
+                      conf->file, conf->line);
+        return NGX_CONF_ERROR;
+    }
+
+    if (conf->certificate_keys == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no \"ssl_certificate_key\" is defined for "
+                      "the \"listen ... ssl\" directive in %s:%ui",
+                      conf->file, conf->line);
+        return NGX_CONF_ERROR;
+    }
+
+    if (conf->certificate_keys->nelts < conf->certificates->nelts) {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "no \"ssl_certificate_key\" is defined "
-                      "for certificate \"%V\"",
+                      "for certificate \"%V\" and "
+                      "the \"listen ... ssl\" directive in %s:%ui",
                       ((ngx_str_t *) conf->certificates->elts)
-                      + conf->certificates->nelts - 1);
+                      + conf->certificates->nelts - 1,
+                      conf->file, conf->line);
         return NGX_CONF_ERROR;
     }
 
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index 65f5d45..9f8f01c 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -21,6 +21,7 @@
 
     ngx_ssl_t        ssl;
 
+    ngx_uint_t       listen;
     ngx_uint_t       protocols;
 
     ngx_uint_t       verify;
@@ -47,6 +48,9 @@
 
     ngx_flag_t       session_tickets;
     ngx_array_t     *session_ticket_keys;
+
+    u_char          *file;
+    ngx_uint_t       line;
 } ngx_stream_ssl_conf_t;
 
 
diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h
index af1ed04..8931522 100644
--- a/src/stream/ngx_stream_upstream.h
+++ b/src/stream/ngx_stream_upstream.h
@@ -128,6 +128,7 @@
 
     off_t                              received;
     time_t                             start_sec;
+    ngx_uint_t                         requests;
     ngx_uint_t                         responses;
 
     ngx_str_t                          ssl_name;