Merge branch 'nginx' (nginx-1.21.2).

Change-Id: Id7b704a389afccf96a9d219955393ac8ca01b360
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index ecf429e..e9aa48d 100644
--- a/.hgtags
+++ b/.hgtags
@@ -462,3 +462,4 @@
 ffcbb9980ee2bad27b4d7b1cd680b14ff47b29aa release-1.19.10
 df34dcc9ac072ffd0945e5a1f3eb7987e8275375 release-1.21.0
 a68ac0677f8553b1f84d357bc9da114731ab5f47 release-1.21.1
+bfbc52374adcbf2f9060afd62de940f6fab3bba5 release-1.21.2
diff --git a/BUILD b/BUILD
index 7fc607b..8ca4e1c 100644
--- a/BUILD
+++ b/BUILD
@@ -1538,5 +1538,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.21.1",
+    version = "1.21.2",
 )
diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl
index f3674b4..7d3f324 100644
--- a/bazel/repositories.bzl
+++ b/bazel/repositories.bzl
@@ -34,9 +34,9 @@
     new_git_repository(
         name = "nginx_pkgoss",
         build_file = "@nginx//bazel/external:nginx_pkgoss.BUILD",
-        commit = "d99ab5743a6d0966f180be66482c7c77afc89306",  # nginx-1.21.1
+        commit = "39dacf6a68cc1af4b1110f3bc46ade6026e4a387",  # nginx-1.21.2
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1625586954 +0300",
+        shallow_since = "1630424319 +0300",
     )
 
     http_archive(
diff --git a/docs/html/50x.html b/docs/html/50x.html
index 9071e0a..a57c2f9 100644
--- a/docs/html/50x.html
+++ b/docs/html/50x.html
@@ -3,11 +3,9 @@
 <head>
 <title>Error</title>
 <style>
-    body {
-        width: 35em;
-        margin: 0 auto;
-        font-family: Tahoma, Verdana, Arial, sans-serif;
-    }
+html { color-scheme: light dark; }
+body { width: 35em; margin: 0 auto;
+font-family: Tahoma, Verdana, Arial, sans-serif; }
 </style>
 </head>
 <body>
diff --git a/docs/html/index.html b/docs/html/index.html
index 2ca3b95..e8f5622 100644
--- a/docs/html/index.html
+++ b/docs/html/index.html
@@ -3,11 +3,9 @@
 <head>
 <title>Welcome to nginx!</title>
 <style>
-    body {
-        width: 35em;
-        margin: 0 auto;
-        font-family: Tahoma, Verdana, Arial, sans-serif;
-    }
+html { color-scheme: light dark; }
+body { width: 35em; margin: 0 auto;
+font-family: Tahoma, Verdana, Arial, sans-serif; }
 </style>
 </head>
 <body>
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 1fb7634..fb64a47 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,113 @@
 <change_log title="nginx">
 
 
+<changes ver="1.21.2" date="2021-08-31">
+
+<change type="change">
+<para lang="ru">
+теперь nginx возвращает ошибку,
+если в запросе по протоколу HTTP/1.0 присутствует
+строка заголовка "Transfer-Encoding".
+</para>
+<para lang="en">
+now nginx rejects HTTP/1.0 requests
+with the "Transfer-Encoding" header line.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+экспортные шифры больше не поддерживаются.
+</para>
+<para lang="en">
+export ciphers are no longer supported.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+совместимость с OpenSSL 3.0.
+</para>
+<para lang="en">
+OpenSSL 3.0 compatibility.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+теперь серверу аутентификации почтового прокси-сервера
+передаются строки заголовка "Auth-SSL-Protocol" и "Auth-SSL-Cipher".<br/>
+Спасибо Rob Mueller.
+</para>
+<para lang="en">
+the "Auth-SSL-Protocol" and "Auth-SSL-Cipher" header lines
+are now passed to the mail proxy authentication server.<br/>
+Thanks to Rob Mueller.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+API для обработки тела запроса
+теперь позволяет буферизировать обрабатываемые данные.
+</para>
+<para lang="en">
+request body filters API
+now permits buffering of the data being processed.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+SSL-соединения к бэкендам в модуле stream
+могли зависать после SSL handshake.
+</para>
+<para lang="en">
+backend SSL connections in the stream module
+might hang after an SSL handshake.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+уровень безопасности, доступный в OpenSSL 1.1.0 и новее,
+не учитывался при загрузке сертификатов сервера,
+если был задан через "@SECLEVEL=N" в директиве ssl_ciphers.
+</para>
+<para lang="en">
+the security level, which is available in OpenSSL 1.1.0 or newer,
+did not affect loading of the server certificates
+when set with "@SECLEVEL=N" in the "ssl_ciphers" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+SSL-соединения с gRPC-бэкендами могли зависать,
+если использовались методы select, poll или /dev/poll.
+</para>
+<para lang="en">
+SSL connections with gRPC backends might hang
+if select, poll, or /dev/poll methods were used.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании HTTP/2
+тело запроса всегда записывалось на диск,
+если в запросе не было строки заголовка "Content-Length".
+</para>
+<para lang="en">
+when using HTTP/2
+client request body was always written to disk
+if the "Content-Length" header line was not present in the request.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.21.1" date="2021-07-06">
 
 <change type="change">
diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index c5fde1f..36dd638 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1k
+OPENSSL =	openssl-1.1.1l
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.44
 
diff --git a/src/core/nginx.h b/src/core/nginx.h
index a7a34de..70b1b87 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1021001
-#define NGINX_VERSION      "1.21.1"
+#define nginx_version      1021002
+#define NGINX_VERSION      "1.21.2"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 72b0da4..602e003 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -299,11 +299,6 @@
     SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
 #endif
 
-#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
-    /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
-    SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
-#endif
-
 #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
     SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
 #endif
@@ -378,6 +373,10 @@
     SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
 #endif
 
+#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
+#endif
+
 #ifdef SSL_MODE_RELEASE_BUFFERS
     SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
 #endif
@@ -859,11 +858,6 @@
         SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
 
-#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER)
-    /* a temporary 512-bit RSA key is required for export versions of MSIE */
-    SSL_CTX_set_tmp_rsa_callback(ssl->ctx, ngx_ssl_rsa512_key_callback);
-#endif
-
     return NGX_OK;
 }
 
@@ -1139,28 +1133,6 @@
 }
 
 
-RSA *
-ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
-    int key_length)
-{
-    static RSA  *key;
-
-    if (key_length != 512) {
-        return NULL;
-    }
-
-#if (OPENSSL_VERSION_NUMBER < 0x10100003L && !defined OPENSSL_NO_DEPRECATED)
-
-    if (key == NULL) {
-        key = RSA_generate_key(512, RSA_F4, NULL, NULL);
-    }
-
-#endif
-
-    return key;
-}
-
-
 ngx_array_t *
 ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
 {
@@ -1373,7 +1345,6 @@
 ngx_int_t
 ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
 {
-    DH   *dh;
     BIO  *bio;
 
     if (file->len == 0) {
@@ -1391,6 +1362,10 @@
         return NGX_ERROR;
     }
 
+#ifdef SSL_CTX_set_tmp_dh
+    {
+    DH  *dh;
+
     dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
     if (dh == NULL) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -1399,9 +1374,42 @@
         return NGX_ERROR;
     }
 
-    SSL_CTX_set_tmp_dh(ssl->ctx, dh);
+    if (SSL_CTX_set_tmp_dh(ssl->ctx, dh) != 1) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_tmp_dh(\"%s\") failed", file->data);
+        DH_free(dh);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
 
     DH_free(dh);
+    }
+#else
+    {
+    EVP_PKEY  *dh;
+
+    /*
+     * PEM_read_bio_DHparams() and SSL_CTX_set_tmp_dh()
+     * are deprecated in OpenSSL 3.0
+     */
+
+    dh = PEM_read_bio_Parameters(bio, NULL);
+    if (dh == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "PEM_read_bio_Parameters(\"%s\") failed", file->data);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
+
+    if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set0_tmp_dh_pkey(\%s\") failed", file->data);
+        BIO_free(bio);
+        return NGX_ERROR;
+    }
+    }
+#endif
+
     BIO_free(bio);
 
     return NGX_OK;
@@ -1763,6 +1771,9 @@
         c->recv_chain = ngx_ssl_recv_chain;
         c->send_chain = ngx_ssl_send_chain;
 
+        c->read->ready = 1;
+        c->write->ready = 1;
+
 #ifndef SSL_OP_NO_RENEGOTIATION
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
@@ -1908,6 +1919,9 @@
         c->recv_chain = ngx_ssl_recv_chain;
         c->send_chain = ngx_ssl_send_chain;
 
+        c->read->ready = 1;
+        c->write->ready = 1;
+
         rc = ngx_ssl_ocsp_validate(c);
 
         if (rc == NGX_ERROR) {
@@ -3260,7 +3274,7 @@
 
         for ( ;; ) {
 
-            n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
+            n = ERR_peek_error_data(&data, &flags);
 
             if (n == 0) {
                 break;
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 7d14647..8ad5361 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -12,6 +12,8 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
+#define OPENSSL_SUPPRESS_DEPRECATED
+
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/bn.h>
@@ -27,7 +29,6 @@
 #include <openssl/ocsp.h>
 #endif
 #include <openssl/rand.h>
-#include <openssl/rsa.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
@@ -64,6 +65,16 @@
 #endif
 
 
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined SSL_get_peer_certificate)
+#define SSL_get_peer_certificate(s)  SSL_get1_peer_certificate(s)
+#endif
+
+
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L && !defined ERR_peek_error_data)
+#define ERR_peek_error_data(d, f)    ERR_peek_error_line_data(NULL, NULL, d, f)
+#endif
+
+
 typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;
 
 
@@ -198,8 +209,6 @@
 ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);
 void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
 ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
-RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
-    int key_length);
 ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
 ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
     ngx_array_t *passwords);
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 65bd1e6..6842b7c 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -4896,6 +4896,12 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = glcf->upstream.ssl;
 
+    if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     if (glcf->upstream.ssl_certificate) {
 
         if (glcf->upstream.ssl_certificate_key == NULL) {
@@ -4927,12 +4933,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0)
-        != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
     if (glcf->upstream.ssl_verify) {
         if (glcf->ssl_trusted_certificate.len == 0) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index bcbb244..a8554a4 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -4975,6 +4975,12 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = plcf->upstream.ssl;
 
+    if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     if (plcf->upstream.ssl_certificate) {
 
         if (plcf->upstream.ssl_certificate_key == NULL) {
@@ -5006,12 +5012,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0)
-        != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
     if (plcf->upstream.ssl_alpn) {
 
         switch (plcf->http_version) {
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 8ec1415..e67dcde 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -810,6 +810,13 @@
                                           ngx_http_ssl_npn_advertised, NULL);
 #endif
 
+    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
+                        conf->prefer_server_ciphers)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
     if (ngx_http_ssl_compile_certificates(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -842,13 +849,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
-                        conf->prefer_server_ciphers)
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
     conf->ssl.buffer_size = conf->buffer_size;
 
     if (conf->verify) {
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 40a06c7..4f9c349 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -2432,6 +2432,12 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = uwcf->upstream.ssl;
 
+    if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
     if (uwcf->upstream.ssl_certificate) {
 
         if (uwcf->upstream.ssl_certificate_key == NULL) {
@@ -2463,12 +2469,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0)
-        != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
     if (uwcf->upstream.ssl_verify) {
         if (uwcf->ssl_trusted_certificate.len == 0) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index de1b96b..9cf371d 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2163,6 +2163,14 @@
     }
 
     if (r->headers_in.transfer_encoding) {
+        if (r->http_version < NGX_HTTP_VERSION_11) {
+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                          "client sent HTTP/1.0 request with "
+                          "\"Transfer-Encoding\" header");
+            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+            return NGX_ERROR;
+        }
+
         if (r->headers_in.transfer_encoding->value.len == 7
             && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
                                (u_char *) "chunked", 7) == 0)
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 4e0ca8a..3ea3490 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -302,6 +302,9 @@
     ngx_chain_t                      *busy;
     ngx_http_chunked_t               *chunked;
     ngx_http_client_body_handler_pt   post_handler;
+    unsigned                          filter_need_buffering:1;
+    unsigned                          last_sent:1;
+    unsigned                          last_saved:1;
 } ngx_http_request_body_t;
 
 
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
index 0cae88f..89a4c74 100644
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -62,11 +62,16 @@
     /*
      * set by ngx_pcalloc():
      *
+     *     rb->temp_file = NULL;
      *     rb->bufs = NULL;
      *     rb->buf = NULL;
      *     rb->free = NULL;
      *     rb->busy = NULL;
      *     rb->chunked = NULL;
+     *     rb->received = 0;
+     *     rb->filter_need_buffering = 0;
+     *     rb->last_sent = 0;
+     *     rb->last_saved = 0;
      */
 
     rb->rest = -1;
@@ -144,7 +149,7 @@
         }
     }
 
-    if (rb->rest == 0) {
+    if (rb->rest == 0 && rb->last_saved) {
         /* the whole request body was pre-read */
         r->request_body_no_buffering = 0;
         post_handler(r);
@@ -172,6 +177,10 @@
             size += preread;
         }
 
+        if (size == 0) {
+            size++;
+        }
+
     } else {
         size = clcf->client_body_buffer_size;
     }
@@ -270,6 +279,7 @@
     size_t                     size;
     ssize_t                    n;
     ngx_int_t                  rc;
+    ngx_uint_t                 flush;
     ngx_chain_t                out;
     ngx_connection_t          *c;
     ngx_http_request_body_t   *rb;
@@ -277,12 +287,17 @@
 
     c = r->connection;
     rb = r->request_body;
+    flush = 1;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http read client request body");
 
     for ( ;; ) {
         for ( ;; ) {
+            if (rb->rest == 0) {
+                break;
+            }
+
             if (rb->buf->last == rb->buf->end) {
 
                 /* update chains */
@@ -306,12 +321,25 @@
                         return NGX_AGAIN;
                     }
 
+                    if (rb->filter_need_buffering) {
+                        clcf = ngx_http_get_module_loc_conf(r,
+                                                         ngx_http_core_module);
+                        ngx_add_timer(c->read, clcf->client_body_timeout);
+
+                        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                        }
+
+                        return NGX_AGAIN;
+                    }
+
                     ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                                   "busy buffers after request body flush");
 
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
+                flush = 0;
                 rb->buf->pos = rb->buf->start;
                 rb->buf->last = rb->buf->start;
             }
@@ -323,6 +351,10 @@
                 size = (size_t) rest;
             }
 
+            if (size == 0) {
+                break;
+            }
+
             n = c->recv(c, rb->buf->last, size);
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
@@ -347,6 +379,7 @@
 
             /* pass buffer to request body filter chain */
 
+            flush = 0;
             out.buf = rb->buf;
             out.next = NULL;
 
@@ -368,11 +401,19 @@
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http client request body rest %O", rb->rest);
 
-        if (rb->rest == 0) {
+        if (flush) {
+            rc = ngx_http_request_body_filter(r, NULL);
+
+            if (rc != NGX_OK) {
+                return rc;
+            }
+        }
+
+        if (rb->rest == 0 && rb->last_saved) {
             break;
         }
 
-        if (!c->read->ready) {
+        if (!c->read->ready || rb->rest == 0) {
 
             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
             ngx_add_timer(c->read, clcf->client_body_timeout);
@@ -939,15 +980,32 @@
 
     rb = r->request_body;
 
+    out = NULL;
+    ll = &out;
+
     if (rb->rest == -1) {
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http request body content length filter");
 
         rb->rest = r->headers_in.content_length_n;
-    }
 
-    out = NULL;
-    ll = &out;
+        if (rb->rest == 0) {
+
+            tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+            if (tl == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            b = tl->buf;
+
+            ngx_memzero(b, sizeof(ngx_buf_t));
+
+            b->last_buf = 1;
+
+            *ll = tl;
+            ll = &tl->next;
+        }
+    }
 
     for (cl = in; cl; cl = cl->next) {
 
@@ -1011,6 +1069,9 @@
 
     rb = r->request_body;
 
+    out = NULL;
+    ll = &out;
+
     if (rb->rest == -1) {
 
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1027,9 +1088,6 @@
         rb->rest = cscf->large_client_header_buffers.size;
     }
 
-    out = NULL;
-    ll = &out;
-
     for (cl = in; cl; cl = cl->next) {
 
         b = NULL;
@@ -1186,15 +1244,16 @@
 ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
     ngx_buf_t                 *b;
-    ngx_chain_t               *cl;
+    ngx_chain_t               *cl, *tl, **ll;
     ngx_http_request_body_t   *rb;
 
     rb = r->request_body;
 
-#if (NGX_DEBUG)
+    ll = &rb->bufs;
+
+    for (cl = rb->bufs; cl; cl = cl->next) {
 
 #if 0
-    for (cl = rb->bufs; cl; cl = cl->next) {
         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                        "http body old buf t:%d f:%d %p, pos %p, size: %z "
                        "file: %O, size: %O",
@@ -1203,10 +1262,13 @@
                        cl->buf->last - cl->buf->pos,
                        cl->buf->file_pos,
                        cl->buf->file_last - cl->buf->file_pos);
-    }
 #endif
 
+        ll = &cl->next;
+    }
+
     for (cl = in; cl; cl = cl->next) {
+
         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                        "http body new buf t:%d f:%d %p, pos %p, size: %z "
                        "file: %O, size: %O",
@@ -1215,15 +1277,31 @@
                        cl->buf->last - cl->buf->pos,
                        cl->buf->file_pos,
                        cl->buf->file_last - cl->buf->file_pos);
+
+        if (cl->buf->last_buf) {
+
+            if (rb->last_saved) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              "duplicate last buf in save filter");
+                *ll = NULL;
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            rb->last_saved = 1;
+        }
+
+        tl = ngx_alloc_chain_link(r->pool);
+        if (tl == NULL) {
+            *ll = NULL;
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        tl->buf = cl->buf;
+        *ll = tl;
+        ll = &tl->next;
     }
 
-#endif
-
-    /* TODO: coalesce neighbouring buffers */
-
-    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
+    *ll = NULL;
 
     if (r->request_body_no_buffering) {
         return NGX_OK;
@@ -1240,10 +1318,18 @@
         return NGX_OK;
     }
 
-    /* rb->rest == 0 */
+    if (!rb->last_saved) {
+        return NGX_OK;
+    }
 
     if (rb->temp_file || r->request_body_in_file_only) {
 
+        if (rb->bufs && rb->bufs->buf->in_file) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "body already in file");
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
         if (ngx_http_write_request_body(r) != NGX_OK) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 8522ffa..2e6a3b1 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2113,6 +2113,10 @@
             c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
         }
 
+        if (c->read->ready) {
+            ngx_post_event(c->read, &ngx_posted_events);
+        }
+
         return;
     }
 
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 82279eb..aa4a725 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -174,7 +174,7 @@
 static void ngx_http_v2_run_request(ngx_http_request_t *r);
 static void ngx_http_v2_run_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
-    u_char *pos, size_t size, ngx_uint_t last);
+    u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush);
 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
 
@@ -1093,7 +1093,7 @@
 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
     u_char *end)
 {
-    size_t                   size;
+    size_t                   size, window;
     ngx_buf_t               *buf;
     ngx_int_t                rc;
     ngx_connection_t        *fc;
@@ -1141,13 +1141,40 @@
     h2c->payload_bytes += size;
 
     if (r->request_body) {
-        rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed);
+        rc = ngx_http_v2_process_request_body(r, pos, size,
+                                              stream->in_closed, 0);
 
-        if (rc != NGX_OK) {
+        if (rc != NGX_OK && rc != NGX_AGAIN) {
             stream->skip_data = 1;
             ngx_http_finalize_request(r, rc);
         }
 
+        if (rc == NGX_AGAIN && !stream->no_flow_control) {
+            buf = r->request_body->buf;
+            window = buf->end - buf->last;
+
+            window -= h2c->state.length - size;
+
+            if (window < stream->recv_window) {
+                ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+                              "http2 negative window update");
+                return ngx_http_v2_connection_error(h2c,
+                                                    NGX_HTTP_V2_INTERNAL_ERROR);
+            }
+
+            if (window > stream->recv_window) {
+                if (ngx_http_v2_send_window_update(h2c, stream->node->id,
+                                                   window - stream->recv_window)
+                    == NGX_ERROR)
+                {
+                    return ngx_http_v2_connection_error(h2c,
+                                                    NGX_HTTP_V2_INTERNAL_ERROR);
+                }
+
+                stream->recv_window = window;
+            }
+        }
+
         ngx_http_run_posted_requests(fc);
 
     } else if (size) {
@@ -4070,16 +4097,30 @@
         return NGX_OK;
     }
 
+    rb->rest = 1;
+
+    /* set rb->filter_need_buffering */
+
+    rc = ngx_http_top_request_body_filter(r, NULL);
+
+    if (rc != NGX_OK) {
+        stream->skip_data = 1;
+        return rc;
+    }
+
     h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     len = r->headers_in.content_length_n;
 
-    if (r->request_body_no_buffering && !stream->in_closed) {
+    if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
+        len = clcf->client_body_buffer_size;
 
-        if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
-            len = clcf->client_body_buffer_size;
-        }
+    } else {
+        len++;
+    }
+
+    if (r->request_body_no_buffering || rb->filter_need_buffering) {
 
         /*
          * We need a room to store data up to the stream's initial window size,
@@ -4093,63 +4134,54 @@
         if (len > NGX_HTTP_V2_MAX_WINDOW) {
             len = NGX_HTTP_V2_MAX_WINDOW;
         }
-
-        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
-
-    } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
-               && !r->request_body_in_file_only)
-    {
-        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
-
-    } else if (len < 0 && stream->in_closed && stream->preread
-               && !r->request_body_in_file_only)
-    {
-        rb->buf = ngx_create_temp_buf(r->pool,
-                                      (size_t) ngx_buf_size(stream->preread));
-
-    } else {
-        rb->buf = ngx_calloc_buf(r->pool);
-
-        if (rb->buf != NULL) {
-            rb->buf->sync = 1;
-        }
     }
 
+    rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
+
     if (rb->buf == NULL) {
         stream->skip_data = 1;
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    rb->rest = 1;
-
     buf = stream->preread;
 
     if (stream->in_closed) {
-        r->request_body_no_buffering = 0;
+        if (!rb->filter_need_buffering) {
+            r->request_body_no_buffering = 0;
+        }
 
         if (buf) {
             rc = ngx_http_v2_process_request_body(r, buf->pos,
-                                                  buf->last - buf->pos, 1);
+                                                  buf->last - buf->pos, 1, 0);
             ngx_pfree(r->pool, buf->start);
+
+        } else {
+            rc = ngx_http_v2_process_request_body(r, NULL, 0, 1, 0);
+        }
+
+        if (rc != NGX_AGAIN) {
             return rc;
         }
 
-        return ngx_http_v2_process_request_body(r, NULL, 0, 1);
+        r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
+        r->write_event_handler = ngx_http_request_empty_handler;
+
+        return NGX_AGAIN;
     }
 
     if (buf) {
         rc = ngx_http_v2_process_request_body(r, buf->pos,
-                                              buf->last - buf->pos, 0);
+                                              buf->last - buf->pos, 0, 0);
 
         ngx_pfree(r->pool, buf->start);
 
-        if (rc != NGX_OK) {
+        if (rc != NGX_OK && rc != NGX_AGAIN) {
             stream->skip_data = 1;
             return rc;
         }
     }
 
-    if (r->request_body_no_buffering) {
+    if (r->request_body_no_buffering || rb->filter_need_buffering) {
         size = (size_t) len - h2scf->preread_size;
 
     } else {
@@ -4191,9 +4223,9 @@
 
 static ngx_int_t
 ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
-    size_t size, ngx_uint_t last)
+    size_t size, ngx_uint_t last, ngx_uint_t flush)
 {
-    ngx_buf_t                 *buf;
+    size_t                     n;
     ngx_int_t                  rc;
     ngx_connection_t          *fc;
     ngx_http_request_body_t   *rb;
@@ -4201,77 +4233,128 @@
 
     fc = r->connection;
     rb = r->request_body;
-    buf = rb->buf;
 
-    if (size) {
-        if (buf->sync) {
-            buf->pos = buf->start = pos;
-            buf->last = buf->end = pos + size;
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+                   "http2 process request body");
 
-            r->request_body_in_file_only = 1;
+    if (size == 0 && !last && !flush) {
+        return NGX_AGAIN;
+    }
 
-        } else {
-            if (size > (size_t) (buf->end - buf->last)) {
-                ngx_log_error(NGX_LOG_INFO, fc->log, 0,
-                              "client intended to send body data "
-                              "larger than declared");
+    for ( ;; ) {
+        for ( ;; ) {
+            if (rb->buf->last == rb->buf->end && size) {
 
-                return NGX_HTTP_BAD_REQUEST;
+                if (r->request_body_no_buffering) {
+
+                    /* should never happen due to flow control */
+
+                    ngx_log_error(NGX_LOG_ALERT, fc->log, 0,
+                                  "no space in http2 body buffer");
+
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                /* update chains */
+
+                ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,
+                              "http2 body update chains");
+
+                rc = ngx_http_v2_filter_request_body(r);
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+
+                if (rb->busy != NULL) {
+                    ngx_log_error(NGX_LOG_ALERT, fc->log, 0,
+                                  "busy buffers after request body flush");
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                rb->buf->pos = rb->buf->start;
+                rb->buf->last = rb->buf->start;
             }
 
-            buf->last = ngx_cpymem(buf->last, pos, size);
+            /* copy body data to the buffer */
+
+            n = rb->buf->end - rb->buf->last;
+
+            if (n > size) {
+                n = size;
+            }
+
+            if (n > 0) {
+                rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);
+            }
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+                           "http2 request body recv %uz", n);
+
+            pos += n;
+            size -= n;
+
+            if (size == 0 && last) {
+                rb->rest = 0;
+            }
+
+            if (r->request_body_no_buffering) {
+                break;
+            }
+
+            /* pass buffer to request body filter chain */
+
+            rc = ngx_http_v2_filter_request_body(r);
+
+            if (rc != NGX_OK) {
+                return rc;
+            }
+
+            if (rb->rest == 0) {
+                break;
+            }
+
+            if (size == 0) {
+                break;
+            }
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+                       "http2 request body rest %O", rb->rest);
+
+        if (rb->rest == 0 && rb->last_saved) {
+            break;
+        }
+
+        if (size == 0) {
+            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+            ngx_add_timer(fc->read, clcf->client_body_timeout);
+
+            if (r->request_body_no_buffering) {
+                ngx_post_event(fc->read, &ngx_posted_events);
+                return NGX_AGAIN;
+            }
+
+            return NGX_AGAIN;
         }
     }
 
-    if (last) {
-        rb->rest = 0;
-
-        if (fc->read->timer_set) {
-            ngx_del_timer(fc->read);
-        }
-
-        if (r->request_body_no_buffering) {
-            ngx_post_event(fc->read, &ngx_posted_events);
-            return NGX_OK;
-        }
-
-        rc = ngx_http_v2_filter_request_body(r);
-
-        if (rc != NGX_OK) {
-            return rc;
-        }
-
-        if (buf->sync) {
-            /* prevent reusing this buffer in the upstream module */
-            rb->buf = NULL;
-        }
-
-        if (r->headers_in.chunked) {
-            r->headers_in.content_length_n = rb->received;
-        }
-
-        r->read_event_handler = ngx_http_block_reading;
-        rb->post_handler(r);
-
-        return NGX_OK;
+    if (fc->read->timer_set) {
+        ngx_del_timer(fc->read);
     }
 
-    if (size == 0) {
-        return NGX_OK;
-    }
-
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    ngx_add_timer(fc->read, clcf->client_body_timeout);
-
     if (r->request_body_no_buffering) {
         ngx_post_event(fc->read, &ngx_posted_events);
         return NGX_OK;
     }
 
-    if (buf->sync) {
-        return ngx_http_v2_filter_request_body(r);
+    if (r->headers_in.chunked) {
+        r->headers_in.content_length_n = rb->received;
     }
 
+    r->read_event_handler = ngx_http_block_reading;
+    rb->post_handler(r);
+
     return NGX_OK;
 }
 
@@ -4288,7 +4371,7 @@
     rb = r->request_body;
     buf = rb->buf;
 
-    if (buf->pos == buf->last && rb->rest) {
+    if (buf->pos == buf->last && (rb->rest || rb->last_sent)) {
         cl = NULL;
         goto update;
     }
@@ -4351,6 +4434,7 @@
         }
 
         b->last_buf = 1;
+        rb->last_sent = 1;
     }
 
     b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body;
@@ -4370,7 +4454,12 @@
 static void
 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)
 {
-    ngx_connection_t  *fc;
+    size_t                     window;
+    ngx_buf_t                 *buf;
+    ngx_int_t                  rc;
+    ngx_connection_t          *fc;
+    ngx_http_v2_stream_t      *stream;
+    ngx_http_v2_connection_t  *h2c;
 
     fc = r->connection;
 
@@ -4396,6 +4485,63 @@
         ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
         return;
     }
+
+    rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1);
+
+    if (rc != NGX_OK && rc != NGX_AGAIN) {
+        r->stream->skip_data = 1;
+        ngx_http_finalize_request(r, rc);
+        return;
+    }
+
+    if (rc == NGX_OK) {
+        return;
+    }
+
+    if (r->request_body->rest == 0) {
+        return;
+    }
+
+    stream = r->stream;
+    h2c = stream->connection;
+
+    buf = r->request_body->buf;
+    window = buf->end - buf->start;
+
+    if (h2c->state.stream == stream) {
+        window -= h2c->state.length;
+    }
+
+    if (window <= stream->recv_window) {
+        if (window < stream->recv_window) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "http2 negative window update");
+
+            stream->skip_data = 1;
+
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        return;
+    }
+
+    if (ngx_http_v2_send_window_update(h2c, stream->node->id,
+                                       window - stream->recv_window)
+        == NGX_ERROR)
+    {
+        stream->skip_data = 1;
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    stream->recv_window = window;
+
+    if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+        stream->skip_data = 1;
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
 }
 
 
@@ -4413,6 +4559,9 @@
     stream = r->stream;
     fc = r->connection;
 
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+                   "http2 read unbuffered request body");
+
     if (fc->read->timedout) {
         if (stream->recv_window) {
             stream->skip_data = 1;
@@ -4436,10 +4585,14 @@
         return rc;
     }
 
-    if (!r->request_body->rest) {
+    if (r->request_body->rest == 0 && r->request_body->last_saved) {
         return NGX_OK;
     }
 
+    if (r->request_body->rest == 0) {
+        return NGX_AGAIN;
+    }
+
     if (r->request_body->busy != NULL) {
         return NGX_AGAIN;
     }
diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c
index 2a198f4..27f64b9 100644
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -1137,8 +1137,8 @@
     ngx_str_t                  login, passwd;
     ngx_connection_t          *c;
 #if (NGX_MAIL_SSL)
-    ngx_str_t                  verify, subject, issuer, serial, fingerprint,
-                               raw_cert, cert;
+    ngx_str_t                  protocol, cipher, verify, subject, issuer,
+                               serial, fingerprint, raw_cert, cert;
     ngx_mail_ssl_conf_t       *sslcf;
 #endif
     ngx_mail_core_srv_conf_t  *cscf;
@@ -1155,6 +1155,25 @@
 
 #if (NGX_MAIL_SSL)
 
+    if (c->ssl) {
+
+        if (ngx_ssl_get_protocol(c, pool, &protocol) != NGX_OK) {
+            return NULL;
+        }
+
+        protocol.len = ngx_strlen(protocol.data);
+
+        if (ngx_ssl_get_cipher_name(c, pool, &cipher) != NGX_OK) {
+            return NULL;
+        }
+
+        cipher.len = ngx_strlen(cipher.data);
+
+    } else {
+        ngx_str_null(&protocol);
+        ngx_str_null(&cipher);
+    }
+
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
     if (c->ssl && sslcf->verify) {
@@ -1252,6 +1271,10 @@
 
     if (c->ssl) {
         len += sizeof("Auth-SSL: on" CRLF) - 1
+               + sizeof("Auth-SSL-Protocol: ") - 1 + protocol.len
+                     + sizeof(CRLF) - 1
+               + sizeof("Auth-SSL-Cipher: ") - 1 + cipher.len
+                     + sizeof(CRLF) - 1
                + sizeof("Auth-SSL-Verify: ") - 1 + verify.len
                      + sizeof(CRLF) - 1
                + sizeof("Auth-SSL-Subject: ") - 1 + subject.len
@@ -1373,6 +1396,20 @@
         b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF,
                              sizeof("Auth-SSL: on" CRLF) - 1);
 
+        if (protocol.len) {
+            b->last = ngx_cpymem(b->last, "Auth-SSL-Protocol: ",
+                                 sizeof("Auth-SSL-Protocol: ") - 1);
+            b->last = ngx_copy(b->last, protocol.data, protocol.len);
+            *b->last++ = CR; *b->last++ = LF;
+        }
+
+        if (cipher.len) {
+            b->last = ngx_cpymem(b->last, "Auth-SSL-Cipher: ",
+                                 sizeof("Auth-SSL-Cipher: ") - 1);
+            b->last = ngx_copy(b->last, cipher.data, cipher.len);
+            *b->last++ = CR; *b->last++ = LF;
+        }
+
         if (verify.len) {
             b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ",
                                  sizeof("Auth-SSL-Verify: ") - 1);
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index 7eae83e..09cc425 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -394,6 +394,13 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
+    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
+                        conf->prefer_server_ciphers)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
     if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
                              conf->certificate_keys, conf->passwords)
         != NGX_OK)
@@ -430,13 +437,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
-                        conf->prefer_server_ciphers)
-        != 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/os/unix/ngx_atomic.h b/src/os/unix/ngx_atomic.h
index 8a86a30..5a3a0c2 100644
--- a/src/os/unix/ngx_atomic.h
+++ b/src/os/unix/ngx_atomic.h
@@ -38,6 +38,39 @@
 #define ngx_cpu_pause()
 
 
+#elif (NGX_HAVE_GCC_ATOMIC)
+
+/* GCC 4.1 builtin atomic operations */
+
+#define NGX_HAVE_ATOMIC_OPS  1
+
+typedef long                        ngx_atomic_int_t;
+typedef unsigned long               ngx_atomic_uint_t;
+
+#if (NGX_PTR_SIZE == 8)
+#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)
+#else
+#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)
+#endif
+
+typedef volatile ngx_atomic_uint_t  ngx_atomic_t;
+
+
+#define ngx_atomic_cmp_set(lock, old, set)                                    \
+    __sync_bool_compare_and_swap(lock, old, set)
+
+#define ngx_atomic_fetch_add(value, add)                                      \
+    __sync_fetch_and_add(value, add)
+
+#define ngx_memory_barrier()        __sync_synchronize()
+
+#if ( __i386__ || __i386 || __amd64__ || __amd64 )
+#define ngx_cpu_pause()             __asm__ ("pause")
+#else
+#define ngx_cpu_pause()
+#endif
+
+
 #elif (NGX_DARWIN_ATOMIC)
 
 /*
@@ -88,39 +121,6 @@
 typedef volatile ngx_atomic_uint_t  ngx_atomic_t;
 
 
-#elif (NGX_HAVE_GCC_ATOMIC)
-
-/* GCC 4.1 builtin atomic operations */
-
-#define NGX_HAVE_ATOMIC_OPS  1
-
-typedef long                        ngx_atomic_int_t;
-typedef unsigned long               ngx_atomic_uint_t;
-
-#if (NGX_PTR_SIZE == 8)
-#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)
-#else
-#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)
-#endif
-
-typedef volatile ngx_atomic_uint_t  ngx_atomic_t;
-
-
-#define ngx_atomic_cmp_set(lock, old, set)                                    \
-    __sync_bool_compare_and_swap(lock, old, set)
-
-#define ngx_atomic_fetch_add(value, add)                                      \
-    __sync_fetch_and_add(value, add)
-
-#define ngx_memory_barrier()        __sync_synchronize()
-
-#if ( __i386__ || __i386 || __amd64__ || __amd64 )
-#define ngx_cpu_pause()             __asm__ ("pause")
-#else
-#define ngx_cpu_pause()
-#endif
-
-
 #elif ( __i386__ || __i386 )
 
 typedef int32_t                     ngx_atomic_int_t;
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index 8c686ab..1275cf2 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -2185,6 +2185,10 @@
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = pscf->ssl;
 
+    if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
     if (pscf->ssl_certificate) {
 
         if (pscf->ssl_certificate_key == NULL) {
@@ -2216,10 +2220,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
     if (pscf->ssl_verify) {
         if (pscf->ssl_trusted_certificate.len == 0) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index d8c0471..b735000 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -720,6 +720,13 @@
                                            ngx_stream_ssl_servername);
 #endif
 
+    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
+                        conf->prefer_server_ciphers)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
     if (ngx_stream_ssl_compile_certificates(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -752,13 +759,6 @@
         }
     }
 
-    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
-                        conf->prefer_server_ciphers)
-        != NGX_OK)
-    {
-        return NGX_CONF_ERROR;
-    }
-
     if (conf->verify) {
 
         if (conf->client_certificate.len == 0 && conf->verify != 3) {