Merge branch 'nginx' (nginx-1.15.4).

Change-Id: I70e09ea945468bfc6aab18f4331369d377ff03c7
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index a4f1a21..6b97c0f 100644
--- a/.hgtags
+++ b/.hgtags
@@ -429,3 +429,4 @@
 4189160cb946bb38d0bc0a452b5eb4cdd8979fb5 release-1.15.1
 b234199c7ed8a156a6bb98f7ff58302c857c954f release-1.15.2
 28b3e17ca7eba1e6a0891afde0e4bc5bcc99c861 release-1.15.3
+49d49835653857daa418e68d6cbfed4958c78fca release-1.15.4
diff --git a/BUILD b/BUILD
index 9253e79..8026b48 100644
--- a/BUILD
+++ b/BUILD
@@ -1535,5 +1535,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.15.3",
+    version = "1.15.4",
 )
diff --git a/build.bzl b/build.bzl
index 79f36a8..4906869 100644
--- a/build.bzl
+++ b/build.bzl
@@ -663,7 +663,7 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "7f82ec785633480de508d4e61f47f4b62e93760c",  # nginx-1.15.3
+        commit = "6dad8e159e768fd3b0940fe089cc09c6ac135f19",  # nginx-1.15.4
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
     )
 
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index d1e37e8..02c4b3c 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,96 @@
 <change_log title="nginx">
 
 
+<changes ver="1.15.4" date="2018-09-25">
+
+<change type="feature">
+<para lang="ru">
+теперь директиву ssl_early_data можно использовать с OpenSSL.
+</para>
+<para lang="en">
+now the "ssl_early_data" directive can be used with OpenSSL.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в модуле ngx_http_uwsgi_module.<br/>
+Спасибо Chris Caputo.
+</para>
+<para lang="en">
+in the ngx_http_uwsgi_module.<br/>
+Thanks to Chris Caputo.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+соединения к некоторым gRPC-бэкендам могли не кэшироваться
+при использовании директивы keepalive.
+</para>
+<para lang="en">
+connections with some gRPC backends might not be cached
+when using the "keepalive" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании директивы error_page для перенаправления ошибок,
+возникающих на ранних этапах обработки запроса,
+в частности ошибок с кодом 400,
+могла происходить утечка сокетов.
+</para>
+<para lang="en">
+a socket leak might occur
+when using the "error_page" directive
+to redirect early request processing errors,
+notably errors with code 400.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+директива return при возврате ошибок не изменяла код ответа,
+если запрос был перенаправлен с помощью директивы error_page.
+</para>
+<para lang="en">
+the "return" directive did not change the response code when returning errors
+if the request was redirected by the "error_page" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+стандартные сообщения об ошибках и ответы модуля ngx_http_autoindex_module
+содержали атрибут bgcolor, что могло приводить к их некорректному отображению
+при использовании пользовательских настроек цветов в браузерах.<br/>
+Спасибо Nova DasSarma.
+</para>
+<para lang="en">
+standard error pages and responses of the ngx_http_autoindex_module module
+used the "bgcolor" attribute, and might be displayed incorrectly when using
+custom color settings in browsers.<br/>
+Thanks to Nova DasSarma.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+уровень логгирования ошибок SSL "no suitable key share" и
+"no suitable signature algorithm"
+понижен с уровня crit до info.
+</para>
+<para lang="en">
+the logging level of the "no suitable key share" and
+"no suitable signature algorithm" SSL errors
+has been lowered from "crit" to "info".
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.15.3" date="2018-08-28">
 
 <change type="feature">
diff --git a/src/core/nginx.h b/src/core/nginx.h
index dadcda6..3294ab5 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1015003
-#define NGINX_VERSION      "1.15.3"
+#define nginx_version      1015004
+#define NGINX_VERSION      "1.15.4"
 #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 9525c36..6b63601 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -26,9 +26,23 @@
 static void ngx_ssl_passwords_cleanup(void *data);
 static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn,
     ngx_ssl_session_t *sess);
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+static ngx_int_t ngx_ssl_try_early_data(ngx_connection_t *c);
+#endif
+#if (NGX_DEBUG)
+static void ngx_ssl_handshake_log(ngx_connection_t *c);
+#endif
 static void ngx_ssl_handshake_handler(ngx_event_t *ev);
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+static ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf,
+    size_t size);
+#endif
 static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
 static void ngx_ssl_write_handler(ngx_event_t *wev);
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+static ssize_t ngx_ssl_write_early(ngx_connection_t *c, u_char *data,
+    size_t size);
+#endif
 static void ngx_ssl_read_handler(ngx_event_t *rev);
 static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
 static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
@@ -340,6 +354,10 @@
     SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
 #endif
 
+#ifdef SSL_OP_NO_ANTI_REPLAY
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY);
+#endif
+
 #ifdef SSL_MODE_RELEASE_BUFFERS
     SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
 #endif
@@ -866,6 +884,8 @@
     BIO               *rbio, *wbio;
     ngx_connection_t  *c;
 
+#ifndef SSL_OP_NO_RENEGOTIATION
+
     if ((where & SSL_CB_HANDSHAKE_START)
         && SSL_is_server((ngx_ssl_conn_t *) ssl_conn))
     {
@@ -877,6 +897,8 @@
         }
     }
 
+#endif
+
     if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {
         c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
 
@@ -1204,6 +1226,12 @@
 
     SSL_CTX_set_early_data_enabled(ssl->ctx, 1);
 
+#elif defined SSL_READ_EARLY_DATA_SUCCESS
+
+    /* OpenSSL */
+
+    SSL_CTX_set_max_early_data(ssl->ctx, NGX_SSL_BUFSIZE);
+
 #else
     ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
                   "\"ssl_early_data\" is not supported on this platform, "
@@ -1265,6 +1293,12 @@
 
     sc->session_ctx = ssl->ctx;
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (SSL_CTX_get_max_early_data(ssl->ctx)) {
+        sc->try_early_data = 1;
+    }
+#endif
+
     sc->connection = SSL_new(ssl->ctx);
 
     if (sc->connection == NULL) {
@@ -1344,6 +1378,12 @@
     int        n, sslerr;
     ngx_err_t  err;
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (c->ssl->try_early_data) {
+        return ngx_ssl_try_early_data(c);
+    }
+#endif
+
     ngx_ssl_clear_error(c->log);
 
     n = SSL_do_handshake(c->ssl->connection);
@@ -1361,50 +1401,7 @@
         }
 
 #if (NGX_DEBUG)
-        {
-        char         buf[129], *s, *d;
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-        const
-#endif
-        SSL_CIPHER  *cipher;
-
-        cipher = SSL_get_current_cipher(c->ssl->connection);
-
-        if (cipher) {
-            SSL_CIPHER_description(cipher, &buf[1], 128);
-
-            for (s = &buf[1], d = buf; *s; s++) {
-                if (*s == ' ' && *d == ' ') {
-                    continue;
-                }
-
-                if (*s == LF || *s == CR) {
-                    continue;
-                }
-
-                *++d = *s;
-            }
-
-            if (*d != ' ') {
-                d++;
-            }
-
-            *d = '\0';
-
-            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                           "SSL: %s, cipher: \"%s\"",
-                           SSL_get_version(c->ssl->connection), &buf[1]);
-
-            if (SSL_session_reused(c->ssl->connection)) {
-                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                               "SSL reused session");
-            }
-
-        } else {
-            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                           "SSL no shared ciphers");
-        }
-        }
+        ngx_ssl_handshake_log(c);
 #endif
 
         c->ssl->handshaked = 1;
@@ -1414,6 +1411,7 @@
         c->recv_chain = ngx_ssl_recv_chain;
         c->send_chain = ngx_ssl_send_chain;
 
+#ifndef SSL_OP_NO_RENEGOTIATION
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
 
@@ -1424,6 +1422,7 @@
 
 #endif
 #endif
+#endif
 
         return NGX_OK;
     }
@@ -1485,6 +1484,173 @@
 }
 
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+
+static ngx_int_t
+ngx_ssl_try_early_data(ngx_connection_t *c)
+{
+    int        n, sslerr;
+    u_char     buf;
+    size_t     readbytes;
+    ngx_err_t  err;
+
+    ngx_ssl_clear_error(c->log);
+
+    readbytes = 0;
+
+    n = SSL_read_early_data(c->ssl->connection, &buf, 1, &readbytes);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "SSL_read_early_data: %d, %uz", n, readbytes);
+
+    if (n == SSL_READ_EARLY_DATA_FINISH) {
+        c->ssl->try_early_data = 0;
+        return ngx_ssl_handshake(c);
+    }
+
+    if (n == SSL_READ_EARLY_DATA_SUCCESS) {
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+#if (NGX_DEBUG)
+        ngx_ssl_handshake_log(c);
+#endif
+
+        c->ssl->try_early_data = 0;
+
+        c->ssl->early_buf = buf;
+        c->ssl->early_preread = 1;
+
+        c->ssl->handshaked = 1;
+        c->ssl->in_early = 1;
+
+        c->recv = ngx_ssl_recv;
+        c->send = ngx_ssl_write;
+        c->recv_chain = ngx_ssl_recv_chain;
+        c->send_chain = ngx_ssl_send_chain;
+
+        return NGX_OK;
+    }
+
+    /* SSL_READ_EARLY_DATA_ERROR */
+
+    sslerr = SSL_get_error(c->ssl->connection, n);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+    if (sslerr == SSL_ERROR_WANT_READ) {
+        c->read->ready = 0;
+        c->read->handler = ngx_ssl_handshake_handler;
+        c->write->handler = ngx_ssl_handshake_handler;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    if (sslerr == SSL_ERROR_WANT_WRITE) {
+        c->write->ready = 0;
+        c->read->handler = ngx_ssl_handshake_handler;
+        c->write->handler = ngx_ssl_handshake_handler;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+    c->ssl->no_wait_shutdown = 1;
+    c->ssl->no_send_shutdown = 1;
+    c->read->eof = 1;
+
+    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
+        ngx_connection_error(c, err,
+                             "peer closed connection in SSL handshake");
+
+        return NGX_ERROR;
+    }
+
+    c->read->error = 1;
+
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_read_early_data() failed");
+
+    return NGX_ERROR;
+}
+
+#endif
+
+
+#if (NGX_DEBUG)
+
+static void
+ngx_ssl_handshake_log(ngx_connection_t *c)
+{
+    char         buf[129], *s, *d;
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+    const
+#endif
+    SSL_CIPHER  *cipher;
+
+    cipher = SSL_get_current_cipher(c->ssl->connection);
+
+    if (cipher) {
+        SSL_CIPHER_description(cipher, &buf[1], 128);
+
+        for (s = &buf[1], d = buf; *s; s++) {
+            if (*s == ' ' && *d == ' ') {
+                continue;
+            }
+
+            if (*s == LF || *s == CR) {
+                continue;
+            }
+
+            *++d = *s;
+        }
+
+        if (*d != ' ') {
+            d++;
+        }
+
+        *d = '\0';
+
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL: %s, cipher: \"%s\"",
+                       SSL_get_version(c->ssl->connection), &buf[1]);
+
+        if (SSL_session_reused(c->ssl->connection)) {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "SSL reused session");
+        }
+
+    } else {
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL no shared ciphers");
+    }
+}
+
+#endif
+
+
 static void
 ngx_ssl_handshake_handler(ngx_event_t *ev)
 {
@@ -1572,6 +1738,12 @@
 {
     int  n, bytes;
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (c->ssl->in_early) {
+        return ngx_ssl_recv_early(c, buf, size);
+    }
+#endif
+
     if (c->ssl->last == NGX_ERROR) {
         c->read->error = 1;
         return NGX_ERROR;
@@ -1645,12 +1817,131 @@
 }
 
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+
+static ssize_t
+ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, size_t size)
+{
+    int        n, bytes;
+    size_t     readbytes;
+
+    if (c->ssl->last == NGX_ERROR) {
+        c->read->error = 1;
+        return NGX_ERROR;
+    }
+
+    if (c->ssl->last == NGX_DONE) {
+        c->read->ready = 0;
+        c->read->eof = 1;
+        return 0;
+    }
+
+    bytes = 0;
+
+    ngx_ssl_clear_error(c->log);
+
+    if (c->ssl->early_preread) {
+
+        if (size == 0) {
+            c->read->ready = 0;
+            c->read->eof = 1;
+            return 0;
+        }
+
+        *buf = c->ssl->early_buf;
+
+        c->ssl->early_preread = 0;
+
+        bytes = 1;
+        size -= 1;
+        buf += 1;
+    }
+
+    /*
+     * SSL_read_early_data() may return data in parts, so try to read
+     * until SSL_read_early_data() would return no data
+     */
+
+    for ( ;; ) {
+
+        readbytes = 0;
+
+        n = SSL_read_early_data(c->ssl->connection, buf, size, &readbytes);
+
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL_read_early_data: %d, %uz", n, readbytes);
+
+        if (n == SSL_READ_EARLY_DATA_SUCCESS) {
+
+            c->ssl->last = ngx_ssl_handle_recv(c, 1);
+
+            bytes += readbytes;
+            size -= readbytes;
+
+            if (size == 0) {
+                c->read->ready = 1;
+                return bytes;
+            }
+
+            buf += readbytes;
+
+            continue;
+        }
+
+        if (n == SSL_READ_EARLY_DATA_FINISH) {
+
+            c->ssl->last = ngx_ssl_handle_recv(c, 1);
+            c->ssl->in_early = 0;
+
+            if (bytes) {
+                c->read->ready = 1;
+                return bytes;
+            }
+
+            return ngx_ssl_recv(c, buf, size);
+        }
+
+        /* SSL_READ_EARLY_DATA_ERROR */
+
+        c->ssl->last = ngx_ssl_handle_recv(c, 0);
+
+        if (bytes) {
+            if (c->ssl->last != NGX_AGAIN) {
+                c->read->ready = 1;
+            }
+
+            return bytes;
+        }
+
+        switch (c->ssl->last) {
+
+        case NGX_DONE:
+            c->read->ready = 0;
+            c->read->eof = 1;
+            return 0;
+
+        case NGX_ERROR:
+            c->read->error = 1;
+
+            /* fall through */
+
+        case NGX_AGAIN:
+            return c->ssl->last;
+        }
+    }
+}
+
+#endif
+
+
 static ngx_int_t
 ngx_ssl_handle_recv(ngx_connection_t *c, int n)
 {
     int        sslerr;
     ngx_err_t  err;
 
+#ifndef SSL_OP_NO_RENEGOTIATION
+
     if (c->ssl->renegotiation) {
         /*
          * disable renegotiation (CVE-2009-3555):
@@ -1673,6 +1964,8 @@
         return NGX_ERROR;
     }
 
+#endif
+
     if (n > 0) {
 
         if (c->ssl->saved_write_handler) {
@@ -1698,14 +1991,28 @@
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
 
     if (sslerr == SSL_ERROR_WANT_READ) {
+
+        if (c->ssl->saved_write_handler) {
+
+            c->write->handler = c->ssl->saved_write_handler;
+            c->ssl->saved_write_handler = NULL;
+            c->write->ready = 1;
+
+            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ngx_post_event(c->write, &ngx_posted_events);
+        }
+
         c->read->ready = 0;
         return NGX_AGAIN;
     }
 
     if (sslerr == SSL_ERROR_WANT_WRITE) {
 
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "peer started SSL renegotiation");
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL_read: want write");
 
         c->write->ready = 0;
 
@@ -1747,6 +2054,8 @@
 
     c = wev->data;
 
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL write handler");
+
     c->read->handler(c->read);
 }
 
@@ -1920,6 +2229,12 @@
     int        n, sslerr;
     ngx_err_t  err;
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (c->ssl->in_early) {
+        return ngx_ssl_write_early(c, data, size);
+    }
+#endif
+
     ngx_ssl_clear_error(c->log);
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %uz", size);
@@ -1955,14 +2270,28 @@
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
 
     if (sslerr == SSL_ERROR_WANT_WRITE) {
+
+        if (c->ssl->saved_read_handler) {
+
+            c->read->handler = c->ssl->saved_read_handler;
+            c->ssl->saved_read_handler = NULL;
+            c->read->ready = 1;
+
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ngx_post_event(c->read, &ngx_posted_events);
+        }
+
         c->write->ready = 0;
         return NGX_AGAIN;
     }
 
     if (sslerr == SSL_ERROR_WANT_READ) {
 
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "peer started SSL renegotiation");
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL_write: want read");
 
         c->read->ready = 0;
 
@@ -1993,6 +2322,107 @@
 }
 
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+
+ssize_t
+ngx_ssl_write_early(ngx_connection_t *c, u_char *data, size_t size)
+{
+    int        n, sslerr;
+    size_t     written;
+    ngx_err_t  err;
+
+    ngx_ssl_clear_error(c->log);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %uz", size);
+
+    written = 0;
+
+    n = SSL_write_early_data(c->ssl->connection, data, size, &written);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "SSL_write_early_data: %d, %uz", n, written);
+
+    if (n > 0) {
+
+        if (c->ssl->saved_read_handler) {
+
+            c->read->handler = c->ssl->saved_read_handler;
+            c->ssl->saved_read_handler = NULL;
+            c->read->ready = 1;
+
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ngx_post_event(c->read, &ngx_posted_events);
+        }
+
+        c->sent += written;
+
+        return written;
+    }
+
+    sslerr = SSL_get_error(c->ssl->connection, n);
+
+    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+    if (sslerr == SSL_ERROR_WANT_WRITE) {
+
+        if (c->ssl->saved_read_handler) {
+
+            c->read->handler = c->ssl->saved_read_handler;
+            c->ssl->saved_read_handler = NULL;
+            c->read->ready = 1;
+
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
+            ngx_post_event(c->read, &ngx_posted_events);
+        }
+
+        c->write->ready = 0;
+        return NGX_AGAIN;
+    }
+
+    if (sslerr == SSL_ERROR_WANT_READ) {
+
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "SSL_write_early_data: want read");
+
+        c->read->ready = 0;
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        /*
+         * we do not set the timer because there is already
+         * the write event timer
+         */
+
+        if (c->ssl->saved_read_handler == NULL) {
+            c->ssl->saved_read_handler = c->read->handler;
+            c->read->handler = ngx_ssl_read_handler;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    c->ssl->no_wait_shutdown = 1;
+    c->ssl->no_send_shutdown = 1;
+    c->write->error = 1;
+
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_write_early_data() failed");
+
+    return NGX_ERROR;
+}
+
+#endif
+
+
 static void
 ngx_ssl_read_handler(ngx_event_t *rev)
 {
@@ -2000,6 +2430,8 @@
 
     c = rev->data;
 
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL read handler");
+
     c->write->handler(c->write);
 }
 
@@ -2176,6 +2608,12 @@
 
             /* handshake failures */
         if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC                        /*  103 */
+#ifdef SSL_R_NO_SUITABLE_KEY_SHARE
+            || n == SSL_R_NO_SUITABLE_KEY_SHARE                      /*  101 */
+#endif
+#ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM
+            || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM            /*  118 */
+#endif
             || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG                  /*  129 */
             || n == SSL_R_DIGEST_CHECK_FAILED                        /*  149 */
             || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              /*  151 */
@@ -2197,7 +2635,13 @@
             || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */
             || n == SSL_R_UNKNOWN_ALERT_TYPE                         /*  246 */
             || n == SSL_R_UNKNOWN_PROTOCOL                           /*  252 */
+#ifdef SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS
+            || n == SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS             /*  253 */
+#endif
             || n == SSL_R_UNSUPPORTED_PROTOCOL                       /*  258 */
+#ifdef SSL_R_NO_SHARED_GROUP
+            || n == SSL_R_NO_SHARED_GROUP                            /*  266 */
+#endif
             || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */
             || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */
 #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
@@ -3678,9 +4122,21 @@
     s->len = 0;
 
 #ifdef SSL_ERROR_EARLY_DATA_REJECTED
+
+    /* BoringSSL */
+
     if (SSL_in_early_data(c->ssl->connection)) {
         ngx_str_set(s, "1");
     }
+
+#elif defined SSL_READ_EARLY_DATA_SUCCESS
+
+    /* OpenSSL */
+
+    if (!SSL_is_init_finished(c->ssl->connection)) {
+        ngx_str_set(s, "1");
+    }
+
 #endif
 
     return NGX_OK;
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index c9524ad..3d87c4d 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -87,12 +87,17 @@
     ngx_event_handler_pt        saved_read_handler;
     ngx_event_handler_pt        saved_write_handler;
 
+    u_char                      early_buf;
+
     unsigned                    handshaked:1;
     unsigned                    renegotiation:1;
     unsigned                    buffer:1;
     unsigned                    no_wait_shutdown:1;
     unsigned                    no_send_shutdown:1;
     unsigned                    handshake_buffer_set:1;
+    unsigned                    try_early_data:1;
+    unsigned                    in_early:1;
+    unsigned                    early_preread:1;
 };
 
 
diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index 94b91db..d59fba2 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -452,7 +452,7 @@
 
     static u_char  header[] =
         "</title></head>" CRLF
-        "<body bgcolor=\"white\">" CRLF
+        "<body>" CRLF
         "<h1>Index of "
     ;
 
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 7fbf736..17b0a26 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -109,8 +109,10 @@
 
     unsigned                   header_sent:1;
     unsigned                   output_closed:1;
+    unsigned                   output_blocked:1;
     unsigned                   parsing_headers:1;
     unsigned                   end_stream:1;
+    unsigned                   done:1;
     unsigned                   status:1;
 
     ngx_http_request_t        *request;
@@ -1072,8 +1074,10 @@
     ctx->state = 0;
     ctx->header_sent = 0;
     ctx->output_closed = 0;
+    ctx->output_blocked = 0;
     ctx->parsing_headers = 0;
     ctx->end_stream = 0;
+    ctx->done = 0;
     ctx->status = 0;
     ctx->connection = NULL;
 
@@ -1093,6 +1097,7 @@
     ngx_int_t               rc;
     ngx_uint_t              next, last;
     ngx_chain_t            *cl, *out, **ll;
+    ngx_http_upstream_t    *u;
     ngx_http_grpc_ctx_t    *ctx;
     ngx_http_grpc_frame_t  *f;
 
@@ -1407,6 +1412,36 @@
         rc = NGX_AGAIN;
     }
 
+    if (rc == NGX_AGAIN) {
+        ctx->output_blocked = 1;
+
+    } else {
+        ctx->output_blocked = 0;
+    }
+
+    if (ctx->done) {
+
+        /*
+         * We have already got the response and were sending some additional
+         * control frames.  Even if there is still something unsent, stop
+         * here anyway.
+         */
+
+        u = r->upstream;
+        u->length = 0;
+
+        if (ctx->in == NULL
+            && ctx->out == NULL
+            && ctx->output_closed
+            && !ctx->output_blocked
+            && ctx->state == ngx_http_grpc_st_start)
+        {
+            u->keepalive = 1;
+        }
+
+        ngx_post_event(u->peer.connection->read, &ngx_posted_events);
+    }
+
     return rc;
 }
 
@@ -1749,6 +1784,7 @@
                     if (ctx->in == NULL
                         && ctx->out == NULL
                         && ctx->output_closed
+                        && !ctx->output_blocked
                         && b->last == b->pos)
                     {
                         u->keepalive = 1;
@@ -1832,6 +1868,34 @@
             rc = ngx_http_grpc_parse_frame(r, ctx, b);
 
             if (rc == NGX_AGAIN) {
+
+                if (ctx->done) {
+
+                    /*
+                     * We have finished parsing the response and the
+                     * remaining control frames.  If there are unsent
+                     * control frames, post a write event to send them.
+                     */
+
+                    if (ctx->out) {
+                        ngx_post_event(u->peer.connection->write,
+                                       &ngx_posted_events);
+                        return NGX_AGAIN;
+                    }
+
+                    u->length = 0;
+
+                    if (ctx->in == NULL
+                        && ctx->output_closed
+                        && !ctx->output_blocked
+                        && ctx->state == ngx_http_grpc_st_start)
+                    {
+                        u->keepalive = 1;
+                    }
+
+                    break;
+                }
+
                 return NGX_AGAIN;
             }
 
@@ -1898,6 +1962,13 @@
                 return NGX_ERROR;
             }
 
+            if (ctx->stream_id && ctx->done) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent frame for closed stream %ui",
+                              ctx->stream_id);
+                return NGX_ERROR;
+            }
+
             ctx->padding = 0;
         }
 
@@ -1914,17 +1985,7 @@
             ctx->state = ngx_http_grpc_st_start;
 
             if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
-                u->length = 0;
-
-                if (ctx->in == NULL
-                    && ctx->out == NULL
-                    && ctx->output_closed
-                    && b->last == b->pos)
-                {
-                    u->keepalive = 1;
-                }
-
-                break;
+                ctx->done = 1;
             }
 
             continue;
@@ -2094,17 +2155,8 @@
                                    "grpc trailer done");
 
                     if (ctx->end_stream) {
-                        u->length = 0;
-
-                        if (ctx->in == NULL
-                            && ctx->out == NULL
-                            && ctx->output_closed
-                            && b->last == b->pos)
-                        {
-                            u->keepalive = 1;
-                        }
-
-                        return NGX_OK;
+                        ctx->done = 1;
+                        break;
                     }
 
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -2121,6 +2173,10 @@
                 return NGX_ERROR;
             }
 
+            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+                continue;
+            }
+
             /* rc == NGX_AGAIN */
 
             if (ctx->rest == 0) {
@@ -2237,17 +2293,7 @@
         ctx->state = ngx_http_grpc_st_start;
 
         if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
-            u->length = 0;
-
-            if (ctx->in == NULL
-                && ctx->out == NULL
-                && ctx->output_closed
-                && b->last == b->pos)
-            {
-                u->keepalive = 1;
-            }
-
-            break;
+            ctx->done = 1;
         }
     }
 
diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c
index a6f1fc8..3b49c45 100644
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -180,15 +180,7 @@
         code(e);
     }
 
-    if (e->status < NGX_HTTP_BAD_REQUEST) {
-        return e->status;
-    }
-
-    if (r->err_status == 0) {
-        return e->status;
-    }
-
-    return r->err_status;
+    return e->status;
 }
 
 
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index 0959133..d0adbdb 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -954,12 +954,18 @@
 #if 0
     /* allow custom uwsgi packet */
     if (len > 0 && len < 2) {
-        ngx_log_error (NGX_LOG_ALERT, r->connection->log, 0,
-                       "uwsgi request is too little: %uz", len);
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "uwsgi request is too little: %uz", len);
         return NGX_ERROR;
     }
 #endif
 
+    if (len > 65535) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "uwsgi request is too big: %uz", len);
+        return NGX_ERROR;
+    }
+
     b = ngx_create_temp_buf(r->pool, len + 4);
     if (b == NULL) {
         return NGX_ERROR;
diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
index 56866fa..330833d 100644
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -2669,7 +2669,7 @@
         } else {
 
             status = ngx_atoi(value[i].data, value[i].len);
-            if (status < 100) {
+            if (status < 100 || status > 599) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid status \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 02a5075..d7da788 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1114,7 +1114,7 @@
             n = ngx_http_read_request_header(r);
 
             if (n == NGX_AGAIN || n == NGX_ERROR) {
-                return;
+                break;
             }
         }
 
@@ -1139,7 +1139,7 @@
             }
 
             if (ngx_http_process_request_uri(r) != NGX_OK) {
-                return;
+                break;
             }
 
             if (r->schema_end) {
@@ -1158,16 +1158,16 @@
                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                   "client sent invalid host in request line");
                     ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
-                    return;
+                    break;
                 }
 
                 if (rc == NGX_ERROR) {
                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return;
+                    break;
                 }
 
                 if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
-                    return;
+                    break;
                 }
 
                 r->headers_in.server = host;
@@ -1179,11 +1179,11 @@
                     && ngx_http_set_virtual_server(r, &r->headers_in.server)
                        == NGX_ERROR)
                 {
-                    return;
+                    break;
                 }
 
                 ngx_http_process_request(r);
-                return;
+                break;
             }
 
 
@@ -1192,7 +1192,7 @@
                 != NGX_OK)
             {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
+                break;
             }
 
             c->log->action = "reading client request headers";
@@ -1200,7 +1200,7 @@
             rev->handler = ngx_http_process_request_headers;
             ngx_http_process_request_headers(rev);
 
-            return;
+            break;
         }
 
         if (rc != NGX_AGAIN) {
@@ -1217,7 +1217,7 @@
                 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
             }
 
-            return;
+            break;
         }
 
         /* NGX_AGAIN: a request line parsing is still incomplete */
@@ -1228,7 +1228,7 @@
 
             if (rv == NGX_ERROR) {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
+                break;
             }
 
             if (rv == NGX_DECLINED) {
@@ -1238,10 +1238,12 @@
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
                               "client sent too long URI");
                 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
-                return;
+                break;
             }
         }
     }
+
+    ngx_http_run_posted_requests(c);
 }
 
 
@@ -1403,7 +1405,7 @@
 
                 if (rv == NGX_ERROR) {
                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                    return;
+                    break;
                 }
 
                 if (rv == NGX_DECLINED) {
@@ -1416,7 +1418,7 @@
                                       "client sent too large request");
                         ngx_http_finalize_request(r,
                                             NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
-                        return;
+                        break;
                     }
 
                     len = r->header_in->end - p;
@@ -1431,14 +1433,14 @@
 
                     ngx_http_finalize_request(r,
                                             NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
-                    return;
+                    break;
                 }
             }
 
             n = ngx_http_read_request_header(r);
 
             if (n == NGX_AGAIN || n == NGX_ERROR) {
-                return;
+                break;
             }
         }
 
@@ -1468,7 +1470,7 @@
             h = ngx_list_push(&r->headers_in.headers);
             if (h == NULL) {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
+                break;
             }
 
             h->hash = r->header_hash;
@@ -1484,7 +1486,7 @@
             h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
             if (h->lowcase_key == NULL) {
                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
+                break;
             }
 
             if (h->key.len == r->lowcase_index) {
@@ -1498,7 +1500,7 @@
                                h->lowcase_key, h->key.len);
 
             if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
-                return;
+                break;
             }
 
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1522,12 +1524,12 @@
             rc = ngx_http_process_request_header(r);
 
             if (rc != NGX_OK) {
-                return;
+                break;
             }
 
             ngx_http_process_request(r);
 
-            return;
+            break;
         }
 
         if (rc == NGX_AGAIN) {
@@ -1543,8 +1545,10 @@
                       "client sent invalid header line");
 
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
-        return;
+        break;
     }
+
+    ngx_http_run_posted_requests(c);
 }
 
 
@@ -2115,8 +2119,6 @@
     r->read_event_handler = ngx_http_block_reading;
 
     ngx_http_handler(r);
-
-    ngx_http_run_posted_requests(c);
 }
 
 
@@ -3482,6 +3484,10 @@
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n);
 
+        if (n == NGX_AGAIN) {
+            break;
+        }
+
         if (n == NGX_ERROR || n == 0) {
             ngx_http_close_request(r, 0);
             return;
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index a19318b..17511c2 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -60,7 +60,7 @@
 static char ngx_http_error_301_page[] =
 "<html>" CRLF
 "<head><title>301 Moved Permanently</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>301 Moved Permanently</h1></center>" CRLF
 ;
 
@@ -68,7 +68,7 @@
 static char ngx_http_error_302_page[] =
 "<html>" CRLF
 "<head><title>302 Found</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>302 Found</h1></center>" CRLF
 ;
 
@@ -76,7 +76,7 @@
 static char ngx_http_error_303_page[] =
 "<html>" CRLF
 "<head><title>303 See Other</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>303 See Other</h1></center>" CRLF
 ;
 
@@ -84,7 +84,7 @@
 static char ngx_http_error_307_page[] =
 "<html>" CRLF
 "<head><title>307 Temporary Redirect</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>307 Temporary Redirect</h1></center>" CRLF
 ;
 
@@ -92,7 +92,7 @@
 static char ngx_http_error_308_page[] =
 "<html>" CRLF
 "<head><title>308 Permanent Redirect</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>308 Permanent Redirect</h1></center>" CRLF
 ;
 
@@ -100,7 +100,7 @@
 static char ngx_http_error_400_page[] =
 "<html>" CRLF
 "<head><title>400 Bad Request</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>400 Bad Request</h1></center>" CRLF
 ;
 
@@ -108,7 +108,7 @@
 static char ngx_http_error_401_page[] =
 "<html>" CRLF
 "<head><title>401 Authorization Required</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>401 Authorization Required</h1></center>" CRLF
 ;
 
@@ -116,7 +116,7 @@
 static char ngx_http_error_402_page[] =
 "<html>" CRLF
 "<head><title>402 Payment Required</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>402 Payment Required</h1></center>" CRLF
 ;
 
@@ -124,7 +124,7 @@
 static char ngx_http_error_403_page[] =
 "<html>" CRLF
 "<head><title>403 Forbidden</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>403 Forbidden</h1></center>" CRLF
 ;
 
@@ -132,7 +132,7 @@
 static char ngx_http_error_404_page[] =
 "<html>" CRLF
 "<head><title>404 Not Found</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>404 Not Found</h1></center>" CRLF
 ;
 
@@ -140,7 +140,7 @@
 static char ngx_http_error_405_page[] =
 "<html>" CRLF
 "<head><title>405 Not Allowed</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>405 Not Allowed</h1></center>" CRLF
 ;
 
@@ -148,7 +148,7 @@
 static char ngx_http_error_406_page[] =
 "<html>" CRLF
 "<head><title>406 Not Acceptable</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>406 Not Acceptable</h1></center>" CRLF
 ;
 
@@ -156,7 +156,7 @@
 static char ngx_http_error_408_page[] =
 "<html>" CRLF
 "<head><title>408 Request Time-out</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>408 Request Time-out</h1></center>" CRLF
 ;
 
@@ -164,7 +164,7 @@
 static char ngx_http_error_409_page[] =
 "<html>" CRLF
 "<head><title>409 Conflict</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>409 Conflict</h1></center>" CRLF
 ;
 
@@ -172,7 +172,7 @@
 static char ngx_http_error_410_page[] =
 "<html>" CRLF
 "<head><title>410 Gone</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>410 Gone</h1></center>" CRLF
 ;
 
@@ -180,7 +180,7 @@
 static char ngx_http_error_411_page[] =
 "<html>" CRLF
 "<head><title>411 Length Required</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>411 Length Required</h1></center>" CRLF
 ;
 
@@ -188,7 +188,7 @@
 static char ngx_http_error_412_page[] =
 "<html>" CRLF
 "<head><title>412 Precondition Failed</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>412 Precondition Failed</h1></center>" CRLF
 ;
 
@@ -196,7 +196,7 @@
 static char ngx_http_error_413_page[] =
 "<html>" CRLF
 "<head><title>413 Request Entity Too Large</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>413 Request Entity Too Large</h1></center>" CRLF
 ;
 
@@ -204,7 +204,7 @@
 static char ngx_http_error_414_page[] =
 "<html>" CRLF
 "<head><title>414 Request-URI Too Large</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>414 Request-URI Too Large</h1></center>" CRLF
 ;
 
@@ -212,7 +212,7 @@
 static char ngx_http_error_415_page[] =
 "<html>" CRLF
 "<head><title>415 Unsupported Media Type</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>415 Unsupported Media Type</h1></center>" CRLF
 ;
 
@@ -220,7 +220,7 @@
 static char ngx_http_error_416_page[] =
 "<html>" CRLF
 "<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
 ;
 
@@ -228,7 +228,7 @@
 static char ngx_http_error_421_page[] =
 "<html>" CRLF
 "<head><title>421 Misdirected Request</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>421 Misdirected Request</h1></center>" CRLF
 ;
 
@@ -236,7 +236,7 @@
 static char ngx_http_error_429_page[] =
 "<html>" CRLF
 "<head><title>429 Too Many Requests</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>429 Too Many Requests</h1></center>" CRLF
 ;
 
@@ -245,7 +245,7 @@
 "<html>" CRLF
 "<head><title>400 Request Header Or Cookie Too Large</title></head>"
 CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>400 Bad Request</h1></center>" CRLF
 "<center>Request Header Or Cookie Too Large</center>" CRLF
 ;
@@ -255,7 +255,7 @@
 "<html>" CRLF
 "<head><title>400 The SSL certificate error</title></head>"
 CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>400 Bad Request</h1></center>" CRLF
 "<center>The SSL certificate error</center>" CRLF
 ;
@@ -265,7 +265,7 @@
 "<html>" CRLF
 "<head><title>400 No required SSL certificate was sent</title></head>"
 CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>400 Bad Request</h1></center>" CRLF
 "<center>No required SSL certificate was sent</center>" CRLF
 ;
@@ -275,7 +275,7 @@
 "<html>" CRLF
 "<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
 CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>400 Bad Request</h1></center>" CRLF
 "<center>The plain HTTP request was sent to HTTPS port</center>" CRLF
 ;
@@ -284,7 +284,7 @@
 static char ngx_http_error_500_page[] =
 "<html>" CRLF
 "<head><title>500 Internal Server Error</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>500 Internal Server Error</h1></center>" CRLF
 ;
 
@@ -292,7 +292,7 @@
 static char ngx_http_error_501_page[] =
 "<html>" CRLF
 "<head><title>501 Not Implemented</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>501 Not Implemented</h1></center>" CRLF
 ;
 
@@ -300,7 +300,7 @@
 static char ngx_http_error_502_page[] =
 "<html>" CRLF
 "<head><title>502 Bad Gateway</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>502 Bad Gateway</h1></center>" CRLF
 ;
 
@@ -308,7 +308,7 @@
 static char ngx_http_error_503_page[] =
 "<html>" CRLF
 "<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF
 ;
 
@@ -316,7 +316,7 @@
 static char ngx_http_error_504_page[] =
 "<html>" CRLF
 "<head><title>504 Gateway Time-out</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>504 Gateway Time-out</h1></center>" CRLF
 ;
 
@@ -324,7 +324,7 @@
 static char ngx_http_error_505_page[] =
 "<html>" CRLF
 "<head><title>505 HTTP Version Not Supported</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>505 HTTP Version Not Supported</h1></center>" CRLF
 ;
 
@@ -332,7 +332,7 @@
 static char ngx_http_error_507_page[] =
 "<html>" CRLF
 "<head><title>507 Insufficient Storage</title></head>" CRLF
-"<body bgcolor=\"white\">" CRLF
+"<body>" CRLF
 "<center><h1>507 Insufficient Storage</h1></center>" CRLF
 ;
 
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 7b0b6e1..80520ee 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -2703,11 +2703,13 @@
 
     if (rc == NGX_ABORT) {
         /* header handler has already finalized request */
+        ngx_http_run_posted_requests(fc);
         return NULL;
     }
 
     if (rc == NGX_DECLINED) {
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        ngx_http_run_posted_requests(fc);
         return NULL;
     }
 
@@ -3785,18 +3787,22 @@
 static void
 ngx_http_v2_run_request(ngx_http_request_t *r)
 {
+    ngx_connection_t  *fc;
+
+    fc = r->connection;
+
     if (ngx_http_v2_construct_request_line(r) != NGX_OK) {
-        return;
+        goto failed;
     }
 
     if (ngx_http_v2_construct_cookie_header(r) != NGX_OK) {
-        return;
+        goto failed;
     }
 
     r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
 
     if (ngx_http_process_request_header(r) != NGX_OK) {
-        return;
+        goto failed;
     }
 
     if (r->headers_in.content_length_n > 0 && r->stream->in_closed) {
@@ -3806,7 +3812,7 @@
         r->stream->skip_data = 1;
 
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
-        return;
+        goto failed;
     }
 
     if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {
@@ -3814,6 +3820,10 @@
     }
 
     ngx_http_process_request(r);
+
+failed:
+
+    ngx_http_run_posted_requests(fc);
 }
 
 
diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
index 96e7c9a..3c4027b 100644
--- a/src/stream/ngx_stream_core_module.c
+++ b/src/stream/ngx_stream_core_module.c
@@ -249,34 +249,40 @@
         }
 
         if (!c->read->ready) {
-            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
-                rc = NGX_ERROR;
-                break;
-            }
-
-            if (!c->read->timer_set) {
-                ngx_add_timer(c->read, cscf->preread_timeout);
-            }
-
-            c->read->handler = ngx_stream_session_handler;
-
-            return NGX_OK;
+            break;
         }
 
         n = c->recv(c, c->buffer->last, size);
 
-        if (n == NGX_ERROR) {
+        if (n == NGX_ERROR || n == 0) {
             rc = NGX_STREAM_OK;
             break;
         }
 
-        if (n > 0) {
-            c->buffer->last += n;
+        if (n == NGX_AGAIN) {
+            break;
         }
 
+        c->buffer->last += n;
+
         rc = ph->handler(s);
     }
 
+    if (rc == NGX_AGAIN) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+            return NGX_OK;
+        }
+
+        if (!c->read->timer_set) {
+            ngx_add_timer(c->read, cscf->preread_timeout);
+        }
+
+        c->read->handler = ngx_stream_session_handler;
+
+        return NGX_OK;
+    }
+
     if (c->read->timer_set) {
         ngx_del_timer(c->read);
     }