Merge branch 'nginx' (nginx-1.11.8).

Change-Id: I5bdbcb1d9b3cba7334721307bf21d86536e73cd6
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index ffd9d06..43df105 100644
--- a/.hgtags
+++ b/.hgtags
@@ -406,3 +406,4 @@
 5253015a339aaca0a3111473d3e931b6d4752393 release-1.11.5
 5e371426b3bcba4312ce08606194b89b758927d1 release-1.11.6
 5c8f60faf33ca8926473d2da27b4c3c417bd4630 release-1.11.7
+4591da489a30f790def29bc5987f43409b503cae release-1.11.8
diff --git a/BUILD b/BUILD
index 1d3b91a..2cfb7b5 100644
--- a/BUILD
+++ b/BUILD
@@ -1468,5 +1468,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.11.7",
+    version = "1.11.8",
 )
diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf
index dbe2e0a..d316ec7 100644
--- a/auto/lib/openssl/conf
+++ b/auto/lib/openssl/conf
@@ -15,8 +15,16 @@
 
             CORE_INCS="$CORE_INCS $OPENSSL/openssl/include"
             CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h"
-            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib"
-            CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib"
+
+            if [ -f $OPENSSL/ms/do_ms.bat ]; then
+                # before OpenSSL 1.1.0
+                CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib"
+                CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib"
+            else
+                # OpenSSL 1.1.0+
+                CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libssl.lib"
+                CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libcrypto.lib"
+            fi
 
             # libeay32.lib requires gdi32.lib
             CORE_LIBS="$CORE_LIBS gdi32.lib"
diff --git a/auto/lib/openssl/makefile.msvc b/auto/lib/openssl/makefile.msvc
index fc9e578..5b90dcb 100644
--- a/auto/lib/openssl/makefile.msvc
+++ b/auto/lib/openssl/makefile.msvc
@@ -6,9 +6,16 @@
 all:
 	cd $(OPENSSL)
 
-	perl Configure VC-WIN32 no-shared --prefix=openssl $(OPENSSL_OPT)
+	perl Configure VC-WIN32 no-shared				\
+		--prefix="%cd%/openssl" 				\
+		--openssldir="%cd%/openssl/ssl" 			\
+		$(OPENSSL_OPT)
 
-	ms\do_ms
-
-	$(MAKE) -f ms\nt.mak
-	$(MAKE) -f ms\nt.mak install
+	if exist ms\do_ms.bat (						\
+		ms\do_ms						\
+		&& $(MAKE) -f ms\nt.mak					\
+		&& $(MAKE) -f ms\nt.mak install				\
+	) else (							\
+		$(MAKE)							\
+		&& $(MAKE) install_sw					\
+	)
diff --git a/auto/lib/perl/make b/auto/lib/perl/make
index 350090c..74e0f3a 100644
--- a/auto/lib/perl/make
+++ b/auto/lib/perl/make
@@ -3,9 +3,6 @@
 # Copyright (C) Nginx, Inc.
 
 
-v=`grep 'define NGINX_VERSION' src/core/nginx.h | sed -e 's/^.*"\(.*\)".*/\1/'`
-
-
 cat << END                                                    >> $NGX_MAKEFILE
 
 $NGX_OBJS/src/http/modules/perl/ngx_http_perl_module.o: \\
@@ -27,7 +24,11 @@
 		src/http/modules/perl/nginx.pm \\
 		src/http/modules/perl/nginx.xs \\
 		src/http/modules/perl/typemap
-	sed "s/%%VERSION%%/$v/" src/http/modules/perl/nginx.pm > \\
+	grep 'define NGINX_VERSION' src/core/nginx.h \\
+		| sed -e 's/^.*"\(.*\)".*/\1/' > \\
+		$NGX_OBJS/src/http/modules/perl/version
+	sed "s/%%VERSION%%/\`cat $NGX_OBJS/src/http/modules/perl/version\`/" \\
+		src/http/modules/perl/nginx.pm > \\
 		$NGX_OBJS/src/http/modules/perl/nginx.pm
 	cp -p src/http/modules/perl/nginx.xs $NGX_OBJS/src/http/modules/perl/
 	cp -p src/http/modules/perl/typemap $NGX_OBJS/src/http/modules/perl/
diff --git a/build.bzl b/build.bzl
index c3de23b..404bbeb 100644
--- a/build.bzl
+++ b/build.bzl
@@ -668,7 +668,7 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "4a0aa6d52a93ef2ed0712edc3a67ac345caa9e14",  # nginx-1.11.7
+        commit = "9f56e1d4b0141318a00babc933b18d6e85638372",  # nginx-1.11.8
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
     )
 
diff --git a/contrib/vim/ftplugin/nginx.vim b/contrib/vim/ftplugin/nginx.vim
new file mode 100644
index 0000000..463eea9
--- /dev/null
+++ b/contrib/vim/ftplugin/nginx.vim
@@ -0,0 +1 @@
+setlocal commentstring=#\ %s
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 0a440e4..1f0e2c6 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,113 @@
 <change_log title="nginx">
 
 
+<changes ver="1.11.8" date="27.12.2016">
+
+<change type="feature">
+<para lang="ru">
+директива absolute_redirect.
+</para>
+<para lang="en">
+the "absolute_redirect" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+параметр escape директивы log_format.
+</para>
+<para lang="en">
+the "escape" parameter of the "log_format" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+проверка клиентских SSL-сертификатов в модуле stream.
+</para>
+<para lang="en">
+client SSL certificates verification in the stream module.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+директива ssl_session_ticket_key поддерживает
+шифрование TLS session tickets с помощью AES256
+при использовании с 80-байтными ключами.
+</para>
+<para lang="en">
+the "ssl_session_ticket_key" directive supports
+AES256 encryption of TLS session tickets
+when used with 80-byte keys.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+поддержка vim-commentary в скриптах для vim.<br/>
+Спасибо Armin Grodon.
+</para>
+<para lang="en">
+vim-commentary support in vim scripts.<br/>
+Thanks to Armin Grodon.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+рекурсия при получении значений переменных не ограничивалась.
+</para>
+<para lang="en">
+recursion when evaluating variables was not limited.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_stream_ssl_preread_module.
+</para>
+<para lang="en">
+in the ngx_stream_ssl_preread_module.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+если сервер, описанный в блоке upstream в модуле stream,
+был признан неработающим, то после истечения fail_timeout он
+признавался работающим только после завершения тестового соединения;
+теперь достаточно, чтобы соединение было успешно установлено.
+</para>
+<para lang="en">
+if a server in an upstream in the stream module failed,
+it was considered alive only when a test connection sent
+to it after fail_timeout was closed;
+now a successfully established connection is enough.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx/Windows не собирался с 64-битным Visual Studio.
+</para>
+<para lang="en">
+nginx/Windows could not be built with 64-bit Visual Studio.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx/Windows не собирался с OpenSSL 1.1.0.
+</para>
+<para lang="en">
+nginx/Windows could not be built with OpenSSL 1.1.0.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.11.7" date="13.12.2016">
 
 <change type="change">
diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index 9ab4193..064fac1 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -4,6 +4,7 @@
 NGINX =		nginx-$(VER)
 TEMP =		tmp
 
+CC =		cl
 OBJS =		objs.msvc8
 OPENSSL =	openssl-1.0.2j
 ZLIB =		zlib-1.2.8
@@ -47,7 +48,7 @@
 
 win32:
 	./auto/configure						\
-		--with-cc=cl						\
+		--with-cc=$(CC)						\
 		--builddir=$(OBJS)					\
 		--with-debug						\
 		--prefix= 						\
diff --git a/src/core/nginx.h b/src/core/nginx.h
index c8fb05f..3ee7ea8 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1011007
-#define NGINX_VERSION      "1.11.7"
+#define nginx_version      1011008
+#define NGINX_VERSION      "1.11.8"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index bdfed88..2065f75 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -300,6 +300,10 @@
 #endif
 
         if (r->event) {
+            if (r->event->timer_set) {
+                ngx_del_timer(r->event);
+            }
+
             ngx_free(r->event);
         }
 
@@ -347,6 +351,10 @@
             next = ctx->next;
 
             if (ctx->event) {
+                if (ctx->event->timer_set) {
+                    ngx_del_timer(ctx->event);
+                }
+
                 ngx_resolver_free(r, ctx->event);
             }
 
@@ -772,7 +780,7 @@
 #endif
 
         if (rn->nsrvs) {
-            for (i = 0; i < rn->nsrvs; i++) {
+            for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
                 if (rn->u.srvs[i].name.data) {
                     ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);
                 }
@@ -867,7 +875,7 @@
         ngx_add_timer(ctx->event, ctx->timeout);
     }
 
-    if (ngx_queue_empty(resend_queue)) {
+    if (ngx_resolver_resend_empty(r)) {
         ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
     }
 
@@ -1090,7 +1098,7 @@
         ngx_add_timer(ctx->event, ctx->timeout);
     }
 
-    if (ngx_queue_empty(resend_queue)) {
+    if (ngx_resolver_resend_empty(r)) {
         ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
     }
 
@@ -1548,6 +1556,7 @@
 ngx_resolver_resend_empty(ngx_resolver_t *r)
 {
     return ngx_queue_empty(&r->name_resend_queue)
+           && ngx_queue_empty(&r->srv_resend_queue)
 #if (NGX_HAVE_INET6)
            && ngx_queue_empty(&r->addr6_resend_queue)
 #endif
@@ -2946,7 +2955,11 @@
     ctx->srvs = srvs;
     ctx->nsrvs = rn->nsrvs;
 
-    for (i = 0; i < rn->nsrvs; i++) {
+    if (ctx->event && ctx->event->timer_set) {
+        ngx_del_timer(ctx->event);
+    }
+
+    for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
         srvs[i].name.data = ngx_resolver_alloc(r, rn->u.srvs[i].name.len);
         if (srvs[i].name.data == NULL) {
             goto failed;
@@ -2965,7 +2978,7 @@
         cctx->handler = ngx_resolver_srv_names_handler;
         cctx->data = ctx;
         cctx->srvs = &srvs[i];
-        cctx->timeout = 0;
+        cctx->timeout = ctx->timeout;
 
         srvs[i].priority = rn->u.srvs[i].priority;
         srvs[i].weight = rn->u.srvs[i].weight;
@@ -4064,7 +4077,7 @@
 #endif
 
     if (rn->nsrvs) {
-        for (i = 0; i < rn->nsrvs; i++) {
+        for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
             if (rn->u.srvs[i].name.data) {
                 ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);
             }
@@ -4193,10 +4206,10 @@
                 d = 0;
             }
 
-            if (j == rn->naddrs) {
+            if (j == (ngx_uint_t) rn->naddrs) {
                 j = 0;
             }
-        } while (++i < rn->naddrs);
+        } while (++i < (ngx_uint_t) rn->naddrs);
     }
 
 #if (NGX_HAVE_INET6)
diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c
index 66faecc..1d4ce2b 100644
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -101,7 +101,7 @@
     }
     /**/
 
-    pool->min_size = 1 << pool->min_shift;
+    pool->min_size = (size_t) 1 << pool->min_shift;
 
     slots = ngx_slab_slots(pool);
 
@@ -473,7 +473,7 @@
     case NGX_SLAB_SMALL:
 
         shift = slab & NGX_SLAB_SHIFT_MASK;
-        size = 1 << shift;
+        size = (size_t) 1 << shift;
 
         if ((uintptr_t) p & (size - 1)) {
             goto wrong_chunk;
@@ -568,7 +568,7 @@
     case NGX_SLAB_BIG:
 
         shift = slab & NGX_SLAB_SHIFT_MASK;
-        size = 1 << shift;
+        size = (size_t) 1 << shift;
 
         if ((uintptr_t) p & (size - 1)) {
             goto wrong_chunk;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index d75b7f0..a2fa97d 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -2859,7 +2859,8 @@
 ngx_int_t
 ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
 {
-    u_char                         buf[48];
+    u_char                         buf[80];
+    size_t                         size;
     ssize_t                        n;
     ngx_str_t                     *path;
     ngx_file_t                     file;
@@ -2902,13 +2903,15 @@
             goto failed;
         }
 
-        if (ngx_file_size(&fi) != 48) {
+        size = ngx_file_size(&fi);
+
+        if (size != 48 && size != 80) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "\"%V\" must be 48 bytes", &file.name);
+                               "\"%V\" must be 48 or 80 bytes", &file.name);
             goto failed;
         }
 
-        n = ngx_read_file(&file, buf, 48, 0);
+        n = ngx_read_file(&file, buf, size, 0);
 
         if (n == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
@@ -2916,10 +2919,10 @@
             goto failed;
         }
 
-        if (n != 48) {
+        if ((size_t) n != size) {
             ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
                                ngx_read_file_n " \"%V\" returned only "
-                               "%z bytes instead of 48", &file.name, n);
+                               "%z bytes instead of %uz", &file.name, n, size);
             goto failed;
         }
 
@@ -2928,9 +2931,18 @@
             goto failed;
         }
 
-        ngx_memcpy(key->name, buf, 16);
-        ngx_memcpy(key->aes_key, buf + 16, 16);
-        ngx_memcpy(key->hmac_key, buf + 32, 16);
+        if (size == 48) {
+            key->size = 48;
+            ngx_memcpy(key->name, buf, 16);
+            ngx_memcpy(key->aes_key, buf + 16, 16);
+            ngx_memcpy(key->hmac_key, buf + 32, 16);
+
+        } else {
+            key->size = 80;
+            ngx_memcpy(key->name, buf, 16);
+            ngx_memcpy(key->hmac_key, buf + 16, 32);
+            ngx_memcpy(key->aes_key, buf + 48, 32);
+        }
 
         if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
@@ -2975,6 +2987,7 @@
     unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
     HMAC_CTX *hctx, int enc)
 {
+    size_t                         size;
     SSL_CTX                       *ssl_ctx;
     ngx_uint_t                     i;
     ngx_array_t                   *keys;
@@ -2989,7 +3002,6 @@
     c = ngx_ssl_get_connection(ssl_conn);
     ssl_ctx = c->ssl->session_ctx;
 
-    cipher = EVP_aes_128_cbc();
 #ifdef OPENSSL_NO_SHA256
     digest = EVP_sha1();
 #else
@@ -3011,6 +3023,15 @@
                        ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
                        SSL_session_reused(ssl_conn) ? "reused" : "new");
 
+        if (key[0].size == 48) {
+            cipher = EVP_aes_128_cbc();
+            size = 16;
+
+        } else {
+            cipher = EVP_aes_256_cbc();
+            size = 32;
+        }
+
         if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "RAND_bytes() failed");
             return -1;
@@ -3023,12 +3044,12 @@
         }
 
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
-        if (HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL) != 1) {
+        if (HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
             return -1;
         }
 #else
-        HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL);
+        HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL);
 #endif
 
         ngx_memcpy(name, key[0].name, 16);
@@ -3057,13 +3078,22 @@
                        ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
                        (i == 0) ? " (default)" : "");
 
+        if (key[i].size == 48) {
+            cipher = EVP_aes_128_cbc();
+            size = 16;
+
+        } else {
+            cipher = EVP_aes_256_cbc();
+            size = 32;
+        }
+
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
-        if (HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL) != 1) {
+        if (HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
             return -1;
         }
 #else
-        HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL);
+        HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL);
 #endif
 
         if (EVP_DecryptInit_ex(ectx, cipher, NULL, key[i].aes_key, iv) != 1) {
@@ -4052,7 +4082,7 @@
     ASN1_TIME *asn1time)
 {
     BIO     *bio;
-    u_char  *value;
+    char    *value;
     size_t   len;
     time_t   time;
 
@@ -4072,9 +4102,9 @@
 
     BIO_write(bio, "Tue ", sizeof("Tue ") - 1);
     ASN1_TIME_print(bio, asn1time);
-    len = BIO_get_mem_data(bio, (char **) &value);
+    len = BIO_get_mem_data(bio, &value);
 
-    time = ngx_parse_http_time(value, len);
+    time = ngx_parse_http_time((u_char *) value, len);
 
     BIO_free(bio);
 
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index c022307..e093e10 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -117,9 +117,10 @@
 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
 
 typedef struct {
+    size_t                      size;
     u_char                      name[16];
-    u_char                      aes_key[16];
-    u_char                      hmac_key[16];
+    u_char                      hmac_key[32];
+    u_char                      aes_key[32];
 } ngx_ssl_session_ticket_key_t;
 
 #endif
diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c
index 2100516..d332c11 100644
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -773,7 +773,7 @@
 ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time)
 {
     BIO     *bio;
-    u_char  *value;
+    char    *value;
     size_t   len;
     time_t   time;
 
@@ -795,7 +795,7 @@
     ASN1_GENERALIZEDTIME_print(bio, asn1time);
     len = BIO_get_mem_data(bio, &value);
 
-    time = ngx_parse_http_time(value, len);
+    time = ngx_parse_http_time((u_char *) value, len);
 
     BIO_free(bio);
 
diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c
index 012a0fb..895a52d 100644
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -1067,7 +1067,7 @@
     u_char                    *location;
     ngx_http_core_loc_conf_t  *clcf;
 
-    r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+    r->headers_out.location = ngx_list_push(&r->headers_out.headers);
     if (r->headers_out.location == NULL) {
         return NGX_ERROR;
     }
@@ -1086,11 +1086,8 @@
         ngx_memcpy(location, r->uri.data, r->uri.len);
     }
 
-    /*
-     * we do not need to set the r->headers_out.location->hash and
-     * r->headers_out.location->key fields
-     */
-
+    r->headers_out.location->hash = 1;
+    ngx_str_set(&r->headers_out.location->key, "Location");
     r->headers_out.location->value.len = r->uri.len;
     r->headers_out.location->value.data = location;
 
diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c
index c42fb08..ff8572b 100644
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -126,12 +126,16 @@
     ngx_http_log_op_t *op);
 
 static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,
-    ngx_http_log_op_t *op, ngx_str_t *value);
+    ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t json);
 static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,
     uintptr_t data);
 static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op);
 static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);
+static size_t ngx_http_log_json_variable_getlen(ngx_http_request_t *r,
+    uintptr_t data);
+static u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
+    ngx_http_log_op_t *op);
 
 
 static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
@@ -909,7 +913,7 @@
 
 static ngx_int_t
 ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
-    ngx_str_t *value)
+    ngx_str_t *value, ngx_uint_t json)
 {
     ngx_int_t  index;
 
@@ -919,8 +923,16 @@
     }
 
     op->len = 0;
-    op->getlen = ngx_http_log_variable_getlen;
-    op->run = ngx_http_log_variable;
+
+    if (json) {
+        op->getlen = ngx_http_log_json_variable_getlen;
+        op->run = ngx_http_log_json_variable;
+
+    } else {
+        op->getlen = ngx_http_log_variable_getlen;
+        op->run = ngx_http_log_variable;
+    }
+
     op->data = index;
 
     return NGX_OK;
@@ -1028,6 +1040,47 @@
 }
 
 
+static size_t
+ngx_http_log_json_variable_getlen(ngx_http_request_t *r, uintptr_t data)
+{
+    uintptr_t                   len;
+    ngx_http_variable_value_t  *value;
+
+    value = ngx_http_get_indexed_variable(r, data);
+
+    if (value == NULL || value->not_found) {
+        return 0;
+    }
+
+    len = ngx_escape_json(NULL, value->data, value->len);
+
+    value->escape = len ? 1 : 0;
+
+    return value->len + len;
+}
+
+
+static u_char *
+ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
+    ngx_http_log_op_t *op)
+{
+    ngx_http_variable_value_t  *value;
+
+    value = ngx_http_get_indexed_variable(r, op->data);
+
+    if (value == NULL || value->not_found) {
+        return buf;
+    }
+
+    if (value->escape == 0) {
+        return ngx_cpymem(buf, value->data, value->len);
+
+    } else {
+        return (u_char *) ngx_escape_json(buf, value->data, value->len);
+    }
+}
+
+
 static void *
 ngx_http_log_create_main_conf(ngx_conf_t *cf)
 {
@@ -1491,12 +1544,28 @@
     size_t               i, len;
     ngx_str_t           *value, var;
     ngx_int_t           *flush;
-    ngx_uint_t           bracket;
+    ngx_uint_t           bracket, json;
     ngx_http_log_op_t   *op;
     ngx_http_log_var_t  *v;
 
+    json = 0;
     value = args->elts;
 
+    if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
+        data = value[s].data + 7;
+
+        if (ngx_strcmp(data, "json") == 0) {
+            json = 1;
+
+        } else if (ngx_strcmp(data, "default") != 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unknown log format escaping \"%s\"", data);
+            return NGX_CONF_ERROR;
+        }
+
+        s++;
+    }
+
     for ( /* void */ ; s < args->nelts; s++) {
 
         i = 0;
@@ -1575,7 +1644,9 @@
                     }
                 }
 
-                if (ngx_http_log_variable_compile(cf, op, &var) != NGX_OK) {
+                if (ngx_http_log_variable_compile(cf, op, &var, json)
+                    != NGX_OK)
+                {
                     return NGX_CONF_ERROR;
                 }
 
diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c
index f79c4ae..07b9580 100644
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -150,7 +150,7 @@
 
         ngx_http_clear_location(r);
 
-        r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+        r->headers_out.location = ngx_list_push(&r->headers_out.headers);
         if (r->headers_out.location == NULL) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
@@ -182,11 +182,8 @@
             }
         }
 
-        /*
-         * we do not need to set the r->headers_out.location->hash and
-         * r->headers_out.location->key fields
-         */
-
+        r->headers_out.location->hash = 1;
+        ngx_str_set(&r->headers_out.location->key, "Location");
         r->headers_out.location->value.len = len;
         r->headers_out.location->value.data = location;
 
diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c
index e8d1d80..de58c6f 100644
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -645,7 +645,7 @@
 
         start = offset - (ngx_int_t) tables->min_match_len + 1;
 
-        i = ngx_max(tables->index[c], ctx->index);
+        i = ngx_max((ngx_uint_t) tables->index[c], ctx->index);
         j = tables->index[c + 1];
 
         while (i != j) {
@@ -978,7 +978,7 @@
         }
 
         c = match[i].match.data[tables->min_match_len - 1];
-        while (ch <= c) {
+        while (ch <= (ngx_uint_t) c) {
             tables->index[ch++] = (u_char) i;
         }
     }
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index ba559f2..c036389 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -457,7 +457,10 @@
     use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
     use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
 
-    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;
+    n = 1                  /* find config phase */
+        + use_rewrite      /* post rewrite phase */
+        + use_access       /* post access phase */
+        + cmcf->try_files;
 
     for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
         n += cmcf->phases[i].handlers.nelts;
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 9f40173..dda23e0 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -542,6 +542,13 @@
       offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection),
       NULL },
 
+    { ngx_string("absolute_redirect"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, absolute_redirect),
+      NULL },
+
     { ngx_string("server_name_in_redirect"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -976,10 +983,8 @@
             return NGX_OK;
         }
 
-        /*
-         * we do not need to set the r->headers_out.location->hash and
-         * r->headers_out.location->key fields
-         */
+        r->headers_out.location->hash = 1;
+        ngx_str_set(&r->headers_out.location->key, "Location");
 
         if (r->args.len == 0) {
             r->headers_out.location->value = clcf->name;
@@ -3563,6 +3568,7 @@
     clcf->lingering_timeout = NGX_CONF_UNSET_MSEC;
     clcf->resolver_timeout = NGX_CONF_UNSET_MSEC;
     clcf->reset_timedout_connection = NGX_CONF_UNSET;
+    clcf->absolute_redirect = NGX_CONF_UNSET;
     clcf->server_name_in_redirect = NGX_CONF_UNSET;
     clcf->port_in_redirect = NGX_CONF_UNSET;
     clcf->msie_padding = NGX_CONF_UNSET;
@@ -3825,6 +3831,8 @@
 
     ngx_conf_merge_value(conf->reset_timedout_connection,
                               prev->reset_timedout_connection, 0);
+    ngx_conf_merge_value(conf->absolute_redirect,
+                              prev->absolute_redirect, 1);
     ngx_conf_merge_value(conf->server_name_in_redirect,
                               prev->server_name_in_redirect, 0);
     ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 2bb0746..7ae0ce3 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -385,6 +385,7 @@
     ngx_flag_t    tcp_nopush;              /* tcp_nopush */
     ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
     ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
+    ngx_flag_t    absolute_redirect;       /* absolute_redirect */
     ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
     ngx_flag_t    port_in_redirect;        /* port_in_redirect */
     ngx_flag_t    msie_padding;            /* msie_padding */
diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
index 3c8ad7d..a5a9300 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -553,7 +553,7 @@
         return NGX_DECLINED;
     }
 
-    if (h->crc32 != c->crc32 || h->header_start != c->header_start) {
+    if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
                       "cache file \"%s\" has md5 collision", c->file.name.data);
         return NGX_DECLINED;
@@ -1495,8 +1495,8 @@
     if (h.version != NGX_HTTP_CACHE_VERSION
         || h.last_modified != c->last_modified
         || h.crc32 != c->crc32
-        || h.header_start != c->header_start
-        || h.body_start != c->body_start)
+        || (size_t) h.header_start != c->header_start
+        || (size_t) h.body_start != c->body_start)
     {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http file cache \"%s\" content changed",
diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c
index fc4d3e9..4c3f5e2 100644
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -313,7 +313,8 @@
 
     if (r->headers_out.location
         && r->headers_out.location->value.len
-        && r->headers_out.location->value.data[0] == '/')
+        && r->headers_out.location->value.data[0] == '/'
+        && clcf->absolute_redirect)
     {
         r->headers_out.location->hash = 0;
 
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index c2b1658..c1a0b4c 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -356,11 +356,11 @@
 
                 n = sc->source->data[i] - '0';
 
-                if (sc->captures_mask & (1 << n)) {
+                if (sc->captures_mask & ((ngx_uint_t) 1 << n)) {
                     sc->dup_capture = 1;
                 }
 
-                sc->captures_mask |= 1 << n;
+                sc->captures_mask |= (ngx_uint_t) 1 << n;
 
                 if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {
                     return NGX_ERROR;
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
index 46592ab..a5116d7 100644
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -121,16 +121,16 @@
     uintptr_t                   status;
     uintptr_t                   next;
 
-    uintptr_t                   test:1;
-    uintptr_t                   negative_test:1;
-    uintptr_t                   uri:1;
-    uintptr_t                   args:1;
+    unsigned                    test:1;
+    unsigned                    negative_test:1;
+    unsigned                    uri:1;
+    unsigned                    args:1;
 
     /* add the r->args to the new arguments */
-    uintptr_t                   add_args:1;
+    unsigned                    add_args:1;
 
-    uintptr_t                   redirect:1;
-    uintptr_t                   break_cycle:1;
+    unsigned                    redirect:1;
+    unsigned                    break_cycle:1;
 
     ngx_str_t                   name;
 } ngx_http_script_regex_code_t;
@@ -139,13 +139,13 @@
 typedef struct {
     ngx_http_script_code_pt     code;
 
-    uintptr_t                   uri:1;
-    uintptr_t                   args:1;
+    unsigned                    uri:1;
+    unsigned                    args:1;
 
     /* add the r->args to the new arguments */
-    uintptr_t                   add_args:1;
+    unsigned                    add_args:1;
 
-    uintptr_t                   redirect:1;
+    unsigned                    redirect:1;
 } ngx_http_script_regex_end_code_t;
 
 #endif
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 0b7c911..dc58eea 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -4933,8 +4933,8 @@
     }
 
     /*
-     * we do not set r->headers_out.location here to avoid the handling
-     * the local redirects without a host name by ngx_http_header_filter()
+     * we do not set r->headers_out.location here to avoid handling
+     * relative redirects in ngx_http_header_filter()
      */
 
     return NGX_OK;
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 580b918..251ddda 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -366,6 +366,9 @@
     ngx_http_variable("1");
 
 
+static ngx_uint_t  ngx_http_variable_depth = 100;
+
+
 ngx_http_variable_t *
 ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
 {
@@ -517,9 +520,20 @@
 
     v = cmcf->variables.elts;
 
+    if (ngx_http_variable_depth == 0) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "cycle while evaluating variable \"%V\"",
+                      &v[index].name);
+        return NULL;
+    }
+
+    ngx_http_variable_depth--;
+
     if (v[index].get_handler(r, &r->variables[index], v[index].data)
         == NGX_OK)
     {
+        ngx_http_variable_depth++;
+
         if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
             r->variables[index].no_cacheable = 1;
         }
@@ -527,6 +541,8 @@
         return &r->variables[index];
     }
 
+    ngx_http_variable_depth++;
+
     r->variables[index].valid = 0;
     r->variables[index].not_found = 1;
 
@@ -568,17 +584,25 @@
     if (v) {
         if (v->flags & NGX_HTTP_VAR_INDEXED) {
             return ngx_http_get_flushed_variable(r, v->index);
+        }
 
-        } else {
-
-            vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
-
-            if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
-                return vv;
-            }
-
+        if (ngx_http_variable_depth == 0) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "cycle while evaluating variable \"%V\"", name);
             return NULL;
         }
+
+        ngx_http_variable_depth--;
+
+        vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+
+        if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
+            ngx_http_variable_depth++;
+            return vv;
+        }
+
+        ngx_http_variable_depth++;
+        return NULL;
     }
 
     vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index bfe1815..5433481 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -266,7 +266,9 @@
 
     if (r->headers_out.location && r->headers_out.location->value.len) {
 
-        if (r->headers_out.location->value.data[0] == '/') {
+        if (r->headers_out.location->value.data[0] == '/'
+            && clcf->absolute_redirect)
+        {
             if (clcf->server_name_in_redirect) {
                 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
                 host = cscf->server_name;
diff --git a/src/mail/ngx_mail_smtp_module.c b/src/mail/ngx_mail_smtp_module.c
index f03bd90..3b5a2d8 100644
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -280,7 +280,7 @@
 
     p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
 
-    p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+    ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
 
     p = conf->starttls_capability.data
         + (last - conf->capability.data) + 3;
diff --git a/src/os/win32/ngx_win32_config.h b/src/os/win32/ngx_win32_config.h
index f5b5950..4824d05 100644
--- a/src/os/win32/ngx_win32_config.h
+++ b/src/os/win32/ngx_win32_config.h
@@ -51,13 +51,14 @@
 
 /* GCC MinGW's stdio.h includes sys/types.h */
 #define _OFF_T_
+#define __have_typedef_off_t
 
 #endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
-#ifdef __MINGW64_VERSION_MAJOR
+#ifdef __GNUC__
 #include <stdint.h>
 #endif
 #include <ctype.h>
@@ -87,12 +88,21 @@
 /* 'type cast': from data pointer to function pointer */
 #pragma warning(disable:4055)
 
+/* 'function' : different 'const' qualifiers */
+#pragma warning(disable:4090)
+
 /* unreferenced formal parameter */
 #pragma warning(disable:4100)
 
 /* FD_SET() and FD_CLR(): conditional expression is constant */
 #pragma warning(disable:4127)
 
+/* conversion from 'type1' to 'type2', possible loss of data */
+#pragma warning(disable:4244)
+
+/* conversion from 'size_t' to 'type', possible loss of data */
+#pragma warning(disable:4267)
+
 /* array is too small to include a terminating null character */
 #pragma warning(disable:4295)
 
@@ -118,6 +128,9 @@
 /* unreferenced formal parameter */
 #pragma warn -8057
 
+/* suspicious pointer arithmetic */
+#pragma warn -8072
+
 #endif
 
 
@@ -151,7 +164,7 @@
 typedef __int64             int64_t;
 typedef unsigned __int64    uint64_t;
 
-#if !defined(__WATCOMC__) && !defined(__MINGW64_VERSION_MAJOR)
+#if __BORLANDC__
 typedef int                 intptr_t;
 typedef u_int               uintptr_t;
 #endif
@@ -184,9 +197,13 @@
 #endif
 
 
-#ifndef __MINGW64_VERSION_MAJOR
+#ifndef __GNUC__
+#ifdef _WIN64
+typedef __int64             ssize_t;
+#else
 typedef int                 ssize_t;
 #endif
+#endif
 
 
 typedef uint32_t            in_addr_t;
diff --git a/src/stream/ngx_stream_log_module.c b/src/stream/ngx_stream_log_module.c
index 26e6d22..a4b67d0 100644
--- a/src/stream/ngx_stream_log_module.c
+++ b/src/stream/ngx_stream_log_module.c
@@ -106,12 +106,16 @@
 static void ngx_stream_log_flush_handler(ngx_event_t *ev);
 
 static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf,
-    ngx_stream_log_op_t *op, ngx_str_t *value);
+    ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t json);
 static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s,
     uintptr_t data);
 static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
     ngx_stream_log_op_t *op);
 static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size);
+static size_t ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s,
+    uintptr_t data);
+static u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s,
+    u_char *buf, ngx_stream_log_op_t *op);
 
 
 static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf);
@@ -686,7 +690,7 @@
 
 static ngx_int_t
 ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op,
-    ngx_str_t *value)
+    ngx_str_t *value, ngx_uint_t json)
 {
     ngx_int_t  index;
 
@@ -696,8 +700,16 @@
     }
 
     op->len = 0;
-    op->getlen = ngx_stream_log_variable_getlen;
-    op->run = ngx_stream_log_variable;
+
+    if (json) {
+        op->getlen = ngx_stream_log_json_variable_getlen;
+        op->run = ngx_stream_log_json_variable;
+
+    } else {
+        op->getlen = ngx_stream_log_variable_getlen;
+        op->run = ngx_stream_log_variable;
+    }
+
     op->data = index;
 
     return NGX_OK;
@@ -806,6 +818,47 @@
 }
 
 
+static size_t
+ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
+{
+    uintptr_t                     len;
+    ngx_stream_variable_value_t  *value;
+
+    value = ngx_stream_get_indexed_variable(s, data);
+
+    if (value == NULL || value->not_found) {
+        return 0;
+    }
+
+    len = ngx_escape_json(NULL, value->data, value->len);
+
+    value->escape = len ? 1 : 0;
+
+    return value->len + len;
+}
+
+
+static u_char *
+ngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf,
+    ngx_stream_log_op_t *op)
+{
+    ngx_stream_variable_value_t  *value;
+
+    value = ngx_stream_get_indexed_variable(s, op->data);
+
+    if (value == NULL || value->not_found) {
+        return buf;
+    }
+
+    if (value->escape == 0) {
+        return ngx_cpymem(buf, value->data, value->len);
+
+    } else {
+        return (u_char *) ngx_escape_json(buf, value->data, value->len);
+    }
+}
+
+
 static void *
 ngx_stream_log_create_main_conf(ngx_conf_t *cf)
 {
@@ -1220,11 +1273,27 @@
     size_t                 i, len;
     ngx_str_t             *value, var;
     ngx_int_t             *flush;
-    ngx_uint_t             bracket;
+    ngx_uint_t             bracket, json;
     ngx_stream_log_op_t   *op;
 
+    json = 0;
     value = args->elts;
 
+    if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
+        data = value[s].data + 7;
+
+        if (ngx_strcmp(data, "json") == 0) {
+            json = 1;
+
+        } else if (ngx_strcmp(data, "default") != 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unknown log format escaping \"%s\"", data);
+            return NGX_CONF_ERROR;
+        }
+
+        s++;
+    }
+
     for ( /* void */ ; s < args->nelts; s++) {
 
         i = 0;
@@ -1289,7 +1358,9 @@
                     goto invalid;
                 }
 
-                if (ngx_stream_log_variable_compile(cf, op, &var) != NGX_OK) {
+                if (ngx_stream_log_variable_compile(cf, op, &var, json)
+                    != NGX_OK)
+                {
                     return NGX_CONF_ERROR;
                 }
 
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index c03b515..bfd78d5 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -808,6 +808,11 @@
 
     u->state->connect_time = ngx_current_msec - u->state->response_time;
 
+    if (u->peer.notify) {
+        u->peer.notify(&u->peer, u->peer.data,
+                       NGX_STREAM_UPSTREAM_NOTIFY_CONNECT);
+    }
+
     c->log->action = "proxying connection";
 
     if (u->upstream_buf.start == NULL) {
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index 9191641..fb653c5 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -49,6 +49,15 @@
 };
 
 
+static ngx_conf_enum_t  ngx_stream_ssl_verify[] = {
+    { ngx_string("off"), 0 },
+    { ngx_string("on"), 1 },
+    { ngx_string("optional"), 2 },
+    { ngx_string("optional_no_ca"), 3 },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_stream_ssl_commands[] = {
 
     { ngx_string("ssl_handshake_timeout"),
@@ -107,6 +116,34 @@
       offsetof(ngx_stream_ssl_conf_t, ciphers),
       NULL },
 
+    { ngx_string("ssl_verify_client"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, verify),
+      &ngx_stream_ssl_verify },
+
+    { ngx_string("ssl_verify_depth"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, verify_depth),
+      NULL },
+
+    { ngx_string("ssl_client_certificate"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, client_certificate),
+      NULL },
+
+    { ngx_string("ssl_trusted_certificate"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, trusted_certificate),
+      NULL },
+
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -142,6 +179,13 @@
       offsetof(ngx_stream_ssl_conf_t, session_timeout),
       NULL },
 
+    { ngx_string("ssl_crl"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, crl),
+      NULL },
+
       ngx_null_command
 };
 
@@ -197,6 +241,37 @@
     { ngx_string("ssl_server_name"), NULL, ngx_stream_ssl_variable,
       (uintptr_t) ngx_ssl_get_server_name, NGX_STREAM_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_client_cert"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_raw_cert"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_raw_certificate,
+      NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_s_dn"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_subject_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_i_dn"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_serial"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_serial_number, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_fingerprint"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_fingerprint, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_verify"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_verify, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_v_start"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_start, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_v_end"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_end, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
+    { ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -207,6 +282,8 @@
 static ngx_int_t
 ngx_stream_ssl_handler(ngx_stream_session_t *s)
 {
+    long                    rc;
+    X509                   *cert;
     ngx_connection_t       *c;
     ngx_stream_ssl_conf_t  *sslcf;
 
@@ -227,6 +304,37 @@
         return ngx_stream_ssl_init_connection(&sslcf->ssl, c);
     }
 
+    if (sslcf->verify) {
+        rc = SSL_get_verify_result(c->ssl->connection);
+
+        if (rc != X509_V_OK
+            && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
+        {
+            ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                          "client SSL certificate verify error: (%l:%s)",
+                          rc, X509_verify_cert_error_string(rc));
+
+            ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+            return NGX_ERROR;
+        }
+
+        if (sslcf->verify == 1) {
+            cert = SSL_get_peer_certificate(c->ssl->connection);
+
+            if (cert == NULL) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent no required SSL certificate");
+
+                ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+                return NGX_ERROR;
+            }
+
+            X509_free(cert);
+        }
+    }
+
     return NGX_OK;
 }
 
@@ -384,6 +492,9 @@
      *     scf->protocols = 0;
      *     scf->dhparam = { 0, NULL };
      *     scf->ecdh_curve = { 0, NULL };
+     *     scf->client_certificate = { 0, NULL };
+     *     scf->trusted_certificate = { 0, NULL };
+     *     scf->crl = { 0, NULL };
      *     scf->ciphers = { 0, NULL };
      *     scf->shm_zone = NULL;
      */
@@ -393,6 +504,8 @@
     scf->certificate_keys = NGX_CONF_UNSET_PTR;
     scf->passwords = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
+    scf->verify = NGX_CONF_UNSET_UINT;
+    scf->verify_depth = NGX_CONF_UNSET_UINT;
     scf->builtin_session_cache = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
     scf->session_tickets = NGX_CONF_UNSET;
@@ -423,6 +536,9 @@
                          (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
                           |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
 
+    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
     ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
     ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
                          NULL);
@@ -431,6 +547,12 @@
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
 
+    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
+                         "");
+    ngx_conf_merge_str_value(conf->trusted_certificate,
+                         prev->trusted_certificate, "");
+    ngx_conf_merge_str_value(conf->crl, prev->crl, "");
+
     ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
                          NGX_DEFAULT_ECDH_CURVE);
 
@@ -480,6 +602,35 @@
         return NGX_CONF_ERROR;
     }
 
+    if (conf->verify) {
+
+        if (conf->client_certificate.len == 0 && conf->verify != 3) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no ssl_client_certificate for ssl_client_verify");
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_client_certificate(cf, &conf->ssl,
+                                       &conf->client_certificate,
+                                       conf->verify_depth)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+                                        &conf->trusted_certificate,
+                                        conf->verify_depth)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
     if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index 9b1c41a..65f5d45 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -23,6 +23,9 @@
 
     ngx_uint_t       protocols;
 
+    ngx_uint_t       verify;
+    ngx_uint_t       verify_depth;
+
     ssize_t          builtin_session_cache;
 
     time_t           session_timeout;
@@ -32,6 +35,9 @@
 
     ngx_str_t        dhparam;
     ngx_str_t        ecdh_curve;
+    ngx_str_t        client_certificate;
+    ngx_str_t        trusted_certificate;
+    ngx_str_t        crl;
 
     ngx_str_t        ciphers;
 
diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c
index e26c518..2040b4f 100644
--- a/src/stream/ngx_stream_ssl_preread_module.c
+++ b/src/stream/ngx_stream_ssl_preread_module.c
@@ -142,7 +142,7 @@
             return NGX_DECLINED;
         }
 
-        if (p[1] != 3 || p[2] == 0) {
+        if (p[1] != 3) {
             ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
                            "ssl preread: unsupported SSL version");
             return NGX_DECLINED;
diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h
index 7a36775..283ffd8 100644
--- a/src/stream/ngx_stream_upstream.h
+++ b/src/stream/ngx_stream_upstream.h
@@ -24,6 +24,9 @@
 #define NGX_STREAM_UPSTREAM_MAX_CONNS     0x0100
 
 
+#define NGX_STREAM_UPSTREAM_NOTIFY_CONNECT     0x1
+
+
 typedef struct {
     ngx_array_t                        upstreams;
                                            /* ngx_stream_upstream_srv_conf_t */
diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c
index a4d539d..e4a6e76 100644
--- a/src/stream/ngx_stream_upstream_round_robin.c
+++ b/src/stream/ngx_stream_upstream_round_robin.c
@@ -16,6 +16,8 @@
 
 static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer(
     ngx_stream_upstream_rr_peer_data_t *rrp);
+static void ngx_stream_upstream_notify_round_robin_peer(
+    ngx_peer_connection_t *pc, void *data, ngx_uint_t state);
 
 #if (NGX_STREAM_SSL)
 
@@ -288,6 +290,7 @@
 
     s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;
     s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;
+    s->upstream->peer.notify = ngx_stream_upstream_notify_round_robin_peer;
     s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);
 #if (NGX_STREAM_SSL)
     s->upstream->peer.set_session =
@@ -659,6 +662,32 @@
 }
 
 
+static void
+ngx_stream_upstream_notify_round_robin_peer(ngx_peer_connection_t *pc,
+    void *data, ngx_uint_t type)
+{
+    ngx_stream_upstream_rr_peer_data_t  *rrp = data;
+
+    ngx_stream_upstream_rr_peer_t  *peer;
+
+    peer = rrp->current;
+
+    if (type == NGX_STREAM_UPSTREAM_NOTIFY_CONNECT
+        && pc->connection->type == SOCK_STREAM)
+    {
+        ngx_stream_upstream_rr_peers_rlock(rrp->peers);
+        ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);
+
+        if (peer->accessed < peer->checked) {
+            peer->fails = 0;
+        }
+
+        ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
+        ngx_stream_upstream_rr_peers_unlock(rrp->peers);
+    }
+}
+
+
 #if (NGX_STREAM_SSL)
 
 static ngx_int_t
diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c
index aa5361d..9dc93ee 100644
--- a/src/stream/ngx_stream_variables.c
+++ b/src/stream/ngx_stream_variables.c
@@ -119,6 +119,9 @@
     ngx_stream_variable("1");
 
 
+static ngx_uint_t  ngx_stream_variable_depth = 100;
+
+
 ngx_stream_variable_t *
 ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
 {
@@ -270,9 +273,20 @@
 
     v = cmcf->variables.elts;
 
+    if (ngx_stream_variable_depth == 0) {
+        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                      "cycle while evaluating variable \"%V\"",
+                      &v[index].name);
+        return NULL;
+    }
+
+    ngx_stream_variable_depth--;
+
     if (v[index].get_handler(s, &s->variables[index], v[index].data)
         == NGX_OK)
     {
+        ngx_stream_variable_depth++;
+
         if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) {
             s->variables[index].no_cacheable = 1;
         }
@@ -280,6 +294,8 @@
         return &s->variables[index];
     }
 
+    ngx_stream_variable_depth++;
+
     s->variables[index].valid = 0;
     s->variables[index].not_found = 1;
 
@@ -322,18 +338,26 @@
     if (v) {
         if (v->flags & NGX_STREAM_VAR_INDEXED) {
             return ngx_stream_get_flushed_variable(s, v->index);
+        }
 
-        } else {
-
-            vv = ngx_palloc(s->connection->pool,
-                            sizeof(ngx_stream_variable_value_t));
-
-            if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {
-                return vv;
-            }
-
+        if (ngx_stream_variable_depth == 0) {
+            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                          "cycle while evaluating variable \"%V\"", name);
             return NULL;
         }
+
+        ngx_stream_variable_depth--;
+
+        vv = ngx_palloc(s->connection->pool,
+                        sizeof(ngx_stream_variable_value_t));
+
+        if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {
+            ngx_stream_variable_depth++;
+            return vv;
+        }
+
+        ngx_stream_variable_depth++;
+        return NULL;
     }
 
     vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t));