changeset 8411:7995cd199b52 quic

Merged with the default branch.
author Sergey Kandaurov <pluknet@nginx.com>
date Tue, 26 May 2020 20:26:44 +0300
parents c7d1b500bd0a (current diff) d78400b2fa93 (diff)
children 4ea302a47d28
files src/event/ngx_event_openssl.c src/event/ngx_event_openssl.h src/http/modules/ngx_http_ssl_module.c src/http/modules/ngx_http_ssl_module.h src/http/ngx_http_request.c
diffstat 14 files changed, 1372 insertions(+), 206 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -449,3 +449,4 @@ e56295fe0ea76bf53b06bffa77a2d3a9a335cb8c
 fdacd273711ddf20f778c1fb91529ab53979a454 release-1.17.8
 5e8d52bca714d4b85284ddb649d1ba4a3ca978a8 release-1.17.9
 c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10
+cbe6ba650211541310618849168631ce0b788f35 release-1.19.0
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,51 @@
 <change_log title="nginx">
 
 
+<changes ver="1.19.0" date="2020-05-26">
+
+<change type="feature">
+<para lang="ru">
+проверка клиентских сертификатов с помощью OCSP.
+</para>
+<para lang="en">
+client certificate validation with OCSP.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при работе с gRPC-бэкендами
+могли возникать ошибки "upstream sent frame for closed stream".
+</para>
+<para lang="en">
+"upstream sent frame for closed stream" errors might occur
+when working with gRPC backends.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+OCSP stapling мог не работать,
+если не была указана директива resolver.
+</para>
+<para lang="en">
+OCSP stapling might not work
+if the "resolver" directive was not specified.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+соединения с некорректным HTTP/2 preface не логгировались.
+</para>
+<para lang="en">
+connections with incorrect HTTP/2 preface were not logged.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.17.10" date="2020-04-14">
 
 <change type="feature">
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@ TEMP =		tmp
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1f
+OPENSSL =	openssl-1.1.1g
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.44
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1017010
-#define NGINX_VERSION      "1.17.10"
+#define nginx_version      1019000
+#define NGINX_VERSION      "1.19.0"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -130,6 +130,7 @@ int  ngx_ssl_connection_index;
 int  ngx_ssl_server_conf_index;
 int  ngx_ssl_session_cache_index;
 int  ngx_ssl_session_ticket_keys_index;
+int  ngx_ssl_ocsp_index;
 int  ngx_ssl_certificate_index;
 int  ngx_ssl_next_certificate_index;
 int  ngx_ssl_certificate_name_index;
@@ -213,6 +214,13 @@ ngx_ssl_init(ngx_log_t *log)
         return NGX_ERROR;
     }
 
+    ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+    if (ngx_ssl_ocsp_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
     ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
                                                          NULL);
     if (ngx_ssl_certificate_index == -1) {
@@ -1594,6 +1602,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
 {
     int        n, sslerr;
     ngx_err_t  err;
+    ngx_int_t  rc;
 
 #ifdef SSL_READ_EARLY_DATA_SUCCESS
     if (c->ssl->try_early_data) {
@@ -1601,6 +1610,10 @@ ngx_ssl_handshake(ngx_connection_t *c)
     }
 #endif
 
+    if (c->ssl->in_ocsp) {
+        return ngx_ssl_ocsp_validate(c);
+    }
+
     ngx_ssl_clear_error(c->log);
 
     n = SSL_do_handshake(c->ssl->connection);
@@ -1621,8 +1634,6 @@ ngx_ssl_handshake(ngx_connection_t *c)
         ngx_ssl_handshake_log(c);
 #endif
 
-        c->ssl->handshaked = 1;
-
         c->recv = ngx_ssl_recv;
         c->send = ngx_ssl_write;
         c->recv_chain = ngx_ssl_recv_chain;
@@ -1641,6 +1652,20 @@ ngx_ssl_handshake(ngx_connection_t *c)
 #endif
 #endif
 
+        rc = ngx_ssl_ocsp_validate(c);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_AGAIN) {
+            c->read->handler = ngx_ssl_handshake_handler;
+            c->write->handler = ngx_ssl_handshake_handler;
+            return NGX_AGAIN;
+        }
+
+        c->ssl->handshaked = 1;
+
         return NGX_OK;
     }
 
@@ -1710,6 +1735,7 @@ ngx_ssl_try_early_data(ngx_connection_t 
     u_char     buf;
     size_t     readbytes;
     ngx_err_t  err;
+    ngx_int_t  rc;
 
     ngx_ssl_clear_error(c->log);
 
@@ -1744,7 +1770,6 @@ ngx_ssl_try_early_data(ngx_connection_t 
         c->ssl->early_buf = buf;
         c->ssl->early_preread = 1;
 
-        c->ssl->handshaked = 1;
         c->ssl->in_early = 1;
 
         c->recv = ngx_ssl_recv;
@@ -1752,6 +1777,20 @@ ngx_ssl_try_early_data(ngx_connection_t 
         c->recv_chain = ngx_ssl_recv_chain;
         c->send_chain = ngx_ssl_send_chain;
 
+        rc = ngx_ssl_ocsp_validate(c);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_AGAIN) {
+            c->read->handler = ngx_ssl_handshake_handler;
+            c->write->handler = ngx_ssl_handshake_handler;
+            return NGX_AGAIN;
+        }
+
+        c->ssl->handshaked = 1;
+
         return NGX_OK;
     }
 
@@ -2740,6 +2779,8 @@ ngx_ssl_shutdown(ngx_connection_t *c)
         return NGX_OK;
     }
 
+    ngx_ssl_ocsp_cleanup(c);
+
     if (SSL_in_init(c->ssl->connection)) {
         /*
          * OpenSSL 1.0.2f complains if SSL_shutdown() is called during
@@ -4899,11 +4940,14 @@ ngx_ssl_get_client_verify(ngx_connection
     rc = SSL_get_verify_result(c->ssl->connection);
 
     if (rc == X509_V_OK) {
-        ngx_str_set(s, "SUCCESS");
-        return NGX_OK;
-    }
-
-    str = X509_verify_cert_error_string(rc);
+        if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) {
+            ngx_str_set(s, "SUCCESS");
+            return NGX_OK;
+        }
+
+    } else {
+        str = X509_verify_cert_error_string(rc);
+    }
 
     s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str));
     if (s->data == NULL) {
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -71,6 +71,9 @@
 #endif
 
 
+typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;
+
+
 struct ngx_ssl_s {
     SSL_CTX                    *ctx;
     ngx_log_t                  *log;
@@ -94,6 +97,8 @@ struct ngx_ssl_connection_s {
     ngx_event_handler_pt        saved_read_handler;
     ngx_event_handler_pt        saved_write_handler;
 
+    ngx_ssl_ocsp_t             *ocsp;
+
     u_char                      early_buf;
 
     unsigned                    handshaked:1;
@@ -104,6 +109,7 @@ struct ngx_ssl_connection_s {
     unsigned                    handshake_buffer_set:1;
     unsigned                    try_early_data:1;
     unsigned                    in_early:1;
+    unsigned                    in_ocsp:1;
     unsigned                    early_preread:1;
     unsigned                    write_blocked:1;
 };
@@ -187,6 +193,14 @@ ngx_int_t ngx_ssl_stapling(ngx_conf_t *c
     ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
 ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
+ngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
+    ngx_uint_t depth, ngx_shm_zone_t *shm_zone);
+ngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
+ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c);
+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);
@@ -288,6 +302,7 @@ extern int  ngx_ssl_connection_index;
 extern int  ngx_ssl_server_conf_index;
 extern int  ngx_ssl_session_cache_index;
 extern int  ngx_ssl_session_ticket_keys_index;
+extern int  ngx_ssl_ocsp_index;
 extern int  ngx_ssl_certificate_index;
 extern int  ngx_ssl_next_certificate_index;
 extern int  ngx_ssl_certificate_name_index;
--- a/src/event/ngx_event_openssl_stapling.c
+++ b/src/event/ngx_event_openssl_stapling.c
@@ -22,6 +22,7 @@ typedef struct {
     ngx_msec_t                   resolver_timeout;
 
     ngx_addr_t                  *addrs;
+    ngx_uint_t                   naddrs;
     ngx_str_t                    host;
     ngx_str_t                    uri;
     in_port_t                    port;
@@ -30,6 +31,7 @@ typedef struct {
 
     X509                        *cert;
     X509                        *issuer;
+    STACK_OF(X509)              *chain;
 
     u_char                      *name;
 
@@ -41,15 +43,66 @@ typedef struct {
 } ngx_ssl_stapling_t;
 
 
+typedef struct {
+    ngx_addr_t                  *addrs;
+    ngx_uint_t                   naddrs;
+
+    ngx_str_t                    host;
+    ngx_str_t                    uri;
+    in_port_t                    port;
+    ngx_uint_t                   depth;
+
+    ngx_shm_zone_t              *shm_zone;
+
+    ngx_resolver_t              *resolver;
+    ngx_msec_t                   resolver_timeout;
+} ngx_ssl_ocsp_conf_t;
+
+
+typedef struct {
+    ngx_rbtree_t                 rbtree;
+    ngx_rbtree_node_t            sentinel;
+    ngx_queue_t                  expire_queue;
+} ngx_ssl_ocsp_cache_t;
+
+
+typedef struct {
+    ngx_str_node_t               node;
+    ngx_queue_t                  queue;
+    int                          status;
+    time_t                       valid;
+} ngx_ssl_ocsp_cache_node_t;
+
+
 typedef struct ngx_ssl_ocsp_ctx_s  ngx_ssl_ocsp_ctx_t;
 
+
+struct ngx_ssl_ocsp_s {
+    STACK_OF(X509)              *certs;
+    ngx_uint_t                   ncert;
+
+    int                          cert_status;
+    ngx_int_t                    status;
+
+    ngx_ssl_ocsp_conf_t         *conf;
+    ngx_ssl_ocsp_ctx_t          *ctx;
+};
+
+
 struct ngx_ssl_ocsp_ctx_s {
+    SSL_CTX                     *ssl_ctx;
+
     X509                        *cert;
     X509                        *issuer;
+    STACK_OF(X509)              *chain;
+
+    int                          status;
+    time_t                       valid;
 
     u_char                      *name;
 
     ngx_uint_t                   naddrs;
+    ngx_uint_t                   naddr;
 
     ngx_addr_t                  *addrs;
     ngx_str_t                    host;
@@ -64,17 +117,20 @@ struct ngx_ssl_ocsp_ctx_s {
     void                       (*handler)(ngx_ssl_ocsp_ctx_t *ctx);
     void                        *data;
 
+    ngx_str_t                    key;
     ngx_buf_t                   *request;
     ngx_buf_t                   *response;
     ngx_peer_connection_t        peer;
 
+    ngx_shm_zone_t              *shm_zone;
+
     ngx_int_t                  (*process)(ngx_ssl_ocsp_ctx_t *ctx);
 
     ngx_uint_t                   state;
 
     ngx_uint_t                   code;
     ngx_uint_t                   count;
-
+    ngx_uint_t                   flags;
     ngx_uint_t                   done;
 
     u_char                      *header_name_start;
@@ -105,8 +161,14 @@ static time_t ngx_ssl_stapling_time(ASN1
 
 static void ngx_ssl_stapling_cleanup(void *data);
 
-static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
+static void ngx_ssl_ocsp_validate_next(ngx_connection_t *c);
+static void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c,
+    ngx_ssl_ocsp_ctx_t *ctx);
+
+static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log);
 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx);
 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
@@ -120,6 +182,11 @@ static ngx_int_t ngx_ssl_ocsp_parse_stat
 static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
 static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
 static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx);
+
+static ngx_int_t ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx);
 
 static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
 
@@ -173,6 +240,18 @@ ngx_ssl_stapling_certificate(ngx_conf_t 
         return NGX_ERROR;
     }
 
+#ifdef SSL_CTRL_SELECT_CURRENT_CERT
+    /* OpenSSL 1.0.2+ */
+    SSL_CTX_select_current_cert(ssl->ctx, cert);
+#endif
+
+#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
+    /* OpenSSL 1.0.1+ */
+    SSL_CTX_get_extra_chain_certs(ssl->ctx, &staple->chain);
+#else
+    staple->chain = ssl->ctx->extra_certs;
+#endif
+
     staple->ssl_ctx = ssl->ctx;
     staple->timeout = 60000;
     staple->verify = verify;
@@ -289,29 +368,16 @@ ngx_ssl_stapling_issuer(ngx_conf_t *cf, 
     X509            *cert, *issuer;
     X509_STORE      *store;
     X509_STORE_CTX  *store_ctx;
-    STACK_OF(X509)  *chain;
 
     cert = staple->cert;
 
-#ifdef SSL_CTRL_SELECT_CURRENT_CERT
-    /* OpenSSL 1.0.2+ */
-    SSL_CTX_select_current_cert(ssl->ctx, cert);
-#endif
-
-#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
-    /* OpenSSL 1.0.1+ */
-    SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
-#else
-    chain = ssl->ctx->extra_certs;
-#endif
-
-    n = sk_X509_num(chain);
+    n = sk_X509_num(staple->chain);
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
                    "SSL get issuer: %d extra certs", n);
 
     for (i = 0; i < n; i++) {
-        issuer = sk_X509_value(chain, i);
+        issuer = sk_X509_value(staple->chain, i);
         if (X509_check_issued(issuer, cert) == X509_V_OK) {
 #if OPENSSL_VERSION_NUMBER >= 0x10100001L
             X509_up_ref(issuer);
@@ -462,6 +528,7 @@ ngx_ssl_stapling_responder(ngx_conf_t *c
     }
 
     staple->addrs = u.addrs;
+    staple->naddrs = u.naddrs;
     staple->host = u.host;
     staple->uri = u.uri;
     staple->port = u.port;
@@ -559,16 +626,20 @@ ngx_ssl_stapling_update(ngx_ssl_stapling
 
     staple->loading = 1;
 
-    ctx = ngx_ssl_ocsp_start();
+    ctx = ngx_ssl_ocsp_start(ngx_cycle->log);
     if (ctx == NULL) {
         return;
     }
 
+    ctx->ssl_ctx = staple->ssl_ctx;
     ctx->cert = staple->cert;
     ctx->issuer = staple->issuer;
+    ctx->chain = staple->chain;
     ctx->name = staple->name;
+    ctx->flags = (staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY);
 
     ctx->addrs = staple->addrs;
+    ctx->naddrs = staple->naddrs;
     ctx->host = staple->host;
     ctx->uri = staple->uri;
     ctx->port = staple->port;
@@ -589,137 +660,27 @@ ngx_ssl_stapling_update(ngx_ssl_stapling
 static void
 ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
 {
-    int                    n;
-    size_t                 len;
-    time_t                 now, valid;
-    ngx_str_t              response;
-    X509_STORE            *store;
-    const u_char          *p;
-    STACK_OF(X509)        *chain;
-    OCSP_CERTID           *id;
-    OCSP_RESPONSE         *ocsp;
-    OCSP_BASICRESP        *basic;
-    ngx_ssl_stapling_t    *staple;
-    ASN1_GENERALIZEDTIME  *thisupdate, *nextupdate;
+    time_t               now;
+    ngx_str_t            response;
+    ngx_ssl_stapling_t  *staple;
 
     staple = ctx->data;
     now = ngx_time();
-    ocsp = NULL;
-    basic = NULL;
-    id = NULL;
-
-    if (ctx->code != 200) {
-        goto error;
-    }
-
-    /* check the response */
-
-    len = ctx->response->last - ctx->response->pos;
-    p = ctx->response->pos;
-
-    ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
-    if (ocsp == NULL) {
-        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
-                      "d2i_OCSP_RESPONSE() failed");
-        goto error;
-    }
-
-    n = OCSP_response_status(ocsp);
-
-    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
-        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
-                      "OCSP response not successful (%d: %s)",
-                      n, OCSP_response_status_str(n));
-        goto error;
-    }
-
-    basic = OCSP_response_get1_basic(ocsp);
-    if (basic == NULL) {
-        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
-                      "OCSP_response_get1_basic() failed");
-        goto error;
-    }
-
-    store = SSL_CTX_get_cert_store(staple->ssl_ctx);
-    if (store == NULL) {
-        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
-                      "SSL_CTX_get_cert_store() failed");
+
+    if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) {
         goto error;
     }
 
-#ifdef SSL_CTRL_SELECT_CURRENT_CERT
-    /* OpenSSL 1.0.2+ */
-    SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert);
-#endif
-
-#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
-    /* OpenSSL 1.0.1+ */
-    SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
-#else
-    chain = staple->ssl_ctx->extra_certs;
-#endif
-
-    if (OCSP_basic_verify(basic, chain, store,
-                          staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
-        != 1)
-    {
-        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
-                      "OCSP_basic_verify() failed");
-        goto error;
-    }
-
-    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
-    if (id == NULL) {
-        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
-                      "OCSP_cert_to_id() failed");
-        goto error;
-    }
-
-    if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
-                              &thisupdate, &nextupdate)
-        != 1)
-    {
+    if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
         ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
-                      "certificate status not found in the OCSP response");
+                      "certificate status \"%s\" in the OCSP response",
+                      OCSP_cert_status_str(ctx->status));
         goto error;
     }
 
-    if (n != V_OCSP_CERTSTATUS_GOOD) {
-        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
-                      "certificate status \"%s\" in the OCSP response",
-                      OCSP_cert_status_str(n));
-        goto error;
-    }
-
-    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
-        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
-                      "OCSP_check_validity() failed");
-        goto error;
-    }
-
-    if (nextupdate) {
-        valid = ngx_ssl_stapling_time(nextupdate);
-        if (valid == (time_t) NGX_ERROR) {
-            ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
-                          "invalid nextUpdate time in certificate status");
-            goto error;
-        }
-
-    } else {
-        valid = NGX_MAX_TIME_T_VALUE;
-    }
-
-    OCSP_CERTID_free(id);
-    OCSP_BASICRESP_free(basic);
-    OCSP_RESPONSE_free(ocsp);
-
-    id = NULL;
-    basic = NULL;
-    ocsp = NULL;
-
     /* copy the response to memory not in ctx->pool */
 
-    response.len = len;
+    response.len = ctx->response->last - ctx->response->pos;
     response.data = ngx_alloc(response.len, ctx->log);
 
     if (response.data == NULL) {
@@ -728,16 +689,12 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc
 
     ngx_memcpy(response.data, ctx->response->pos, response.len);
 
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
-                   "ssl ocsp response, %s, %uz",
-                   OCSP_cert_status_str(n), response.len);
-
     if (staple->staple.data) {
         ngx_free(staple->staple.data);
     }
 
     staple->staple = response;
-    staple->valid = valid;
+    staple->valid = ctx->valid;
 
     /*
      * refresh before the response expires,
@@ -745,7 +702,7 @@ ngx_ssl_stapling_ocsp_handler(ngx_ssl_oc
      */
 
     staple->loading = 0;
-    staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300);
+    staple->refresh = ngx_max(ngx_min(ctx->valid - 300, now + 3600), now + 300);
 
     ngx_ssl_ocsp_done(ctx);
     return;
@@ -755,18 +712,6 @@ error:
     staple->loading = 0;
     staple->refresh = now + 300;
 
-    if (id) {
-        OCSP_CERTID_free(id);
-    }
-
-    if (basic) {
-        OCSP_BASICRESP_free(basic);
-    }
-
-    if (ocsp) {
-        OCSP_RESPONSE_free(ocsp);
-    }
-
     ngx_ssl_ocsp_done(ctx);
 }
 
@@ -820,14 +765,499 @@ ngx_ssl_stapling_cleanup(void *data)
 }
 
 
+ngx_int_t
+ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
+    ngx_uint_t depth, ngx_shm_zone_t *shm_zone)
+{
+    ngx_url_t             u;
+    ngx_ssl_ocsp_conf_t  *ocf;
+
+    ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t));
+    if (ocf == NULL) {
+        return NGX_ERROR;
+    }
+
+    ocf->depth = depth;
+    ocf->shm_zone = shm_zone;
+
+    if (responder->len) {
+        ngx_memzero(&u, sizeof(ngx_url_t));
+
+        u.url = *responder;
+        u.default_port = 80;
+        u.uri_part = 1;
+
+        if (u.url.len > 7
+            && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+        {
+            u.url.len -= 7;
+            u.url.data += 7;
+
+        } else {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "invalid URL prefix in OCSP responder \"%V\" "
+                          "in \"ssl_ocsp_responder\"", &u.url);
+            return NGX_ERROR;
+        }
+
+        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+            if (u.err) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "%s in OCSP responder \"%V\" "
+                              "in \"ssl_ocsp_responder\"", u.err, &u.url);
+            }
+
+            return NGX_ERROR;
+        }
+
+        ocf->addrs = u.addrs;
+        ocf->naddrs = u.naddrs;
+        ocf->host = u.host;
+        ocf->uri = u.uri;
+        ocf->port = u.port;
+    }
+
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+    ngx_ssl_ocsp_conf_t  *ocf;
+
+    ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index);
+    ocf->resolver = resolver;
+    ocf->resolver_timeout = resolver_timeout;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_validate(ngx_connection_t *c)
+{
+    X509                 *cert;
+    SSL_CTX              *ssl_ctx;
+    ngx_int_t             rc;
+    X509_STORE           *store;
+    X509_STORE_CTX       *store_ctx;
+    STACK_OF(X509)       *chain;
+    ngx_ssl_ocsp_t       *ocsp;
+    ngx_ssl_ocsp_conf_t  *ocf;
+
+    if (c->ssl->in_ocsp) {
+        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;
+    }
+
+    ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);
+
+    ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index);
+    if (ocf == NULL) {
+        return NGX_OK;
+    }
+
+    if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
+        return NGX_OK;
+    }
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t));
+    if (ocsp == NULL) {
+        return NGX_ERROR;
+    }
+
+    c->ssl->ocsp = ocsp;
+
+    ocsp->status = NGX_AGAIN;
+    ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD;
+    ocsp->conf = ocf;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER)
+
+    ocsp->certs = SSL_get0_verified_chain(c->ssl->connection);
+
+    if (ocsp->certs) {
+        ocsp->certs = X509_chain_up_ref(ocsp->certs);
+        if (ocsp->certs == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+#endif
+
+    if (ocsp->certs == NULL) {
+        store = SSL_CTX_get_cert_store(ssl_ctx);
+        if (store == NULL) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "SSL_CTX_get_cert_store() failed");
+            return NGX_ERROR;
+        }
+
+        store_ctx = X509_STORE_CTX_new();
+        if (store_ctx == NULL) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "X509_STORE_CTX_new() failed");
+            return NGX_ERROR;
+        }
+
+        chain = SSL_get_peer_cert_chain(c->ssl->connection);
+
+        if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "X509_STORE_CTX_init() failed");
+            X509_STORE_CTX_free(store_ctx);
+            return NGX_ERROR;
+        }
+
+        rc = X509_verify_cert(store_ctx);
+        if (rc <= 0) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed");
+            X509_STORE_CTX_free(store_ctx);
+            return NGX_ERROR;
+        }
+
+        ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx);
+        if (ocsp->certs == NULL) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
+                          "X509_STORE_CTX_get1_chain() failed");
+            X509_STORE_CTX_free(store_ctx);
+            return NGX_ERROR;
+        }
+
+        X509_STORE_CTX_free(store_ctx);
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "ssl ocsp validate, certs:%d", sk_X509_num(ocsp->certs));
+
+    ngx_ssl_ocsp_validate_next(c);
+
+    if (ocsp->status == NGX_AGAIN) {
+        c->ssl->in_ocsp = 1;
+        return NGX_AGAIN;
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
+{
+    ngx_int_t             rc;
+    ngx_uint_t            n;
+    ngx_ssl_ocsp_t       *ocsp;
+    ngx_ssl_ocsp_ctx_t   *ctx;
+    ngx_ssl_ocsp_conf_t  *ocf;
+
+    ocsp = c->ssl->ocsp;
+    ocf = ocsp->conf;
+
+    n = sk_X509_num(ocsp->certs);
+
+    for ( ;; ) {
+
+        if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) {
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "ssl ocsp validated, certs:%ui", ocsp->ncert);
+            goto done;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "ssl ocsp validate cert:%ui", ocsp->ncert);
+
+        ctx = ngx_ssl_ocsp_start(c->log);
+        if (ctx == NULL) {
+            goto failed;
+        }
+
+        ocsp->ctx = ctx;
+
+        ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);
+        ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert);
+        ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1);
+        ctx->chain = ocsp->certs;
+
+        ctx->resolver = ocf->resolver;
+        ctx->resolver_timeout = ocf->resolver_timeout;
+
+        ctx->handler = ngx_ssl_ocsp_handler;
+        ctx->data = c;
+
+        ctx->shm_zone = ocf->shm_zone;
+
+        ctx->addrs = ocf->addrs;
+        ctx->naddrs = ocf->naddrs;
+        ctx->host = ocf->host;
+        ctx->uri = ocf->uri;
+        ctx->port = ocf->port;
+
+        if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) {
+            goto failed;
+        }
+
+        if (ctx->uri.len == 0) {
+            ngx_str_set(&ctx->uri, "/");
+        }
+
+        ocsp->ncert++;
+
+        rc = ngx_ssl_ocsp_cache_lookup(ctx);
+
+        if (rc == NGX_ERROR) {
+            goto failed;
+        }
+
+        if (rc == NGX_DECLINED) {
+            break;
+        }
+
+        /* rc == NGX_OK */
+
+        if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                           "ssl ocsp cached status \"%s\"",
+                           OCSP_cert_status_str(ctx->status));
+            ocsp->cert_status = ctx->status;
+            goto done;
+        }
+
+        ocsp->ctx = NULL;
+        ngx_ssl_ocsp_done(ctx);
+    }
+
+    ngx_ssl_ocsp_request(ctx);
+    return;
+
+done:
+
+    ocsp->status = NGX_OK;
+    return;
+
+failed:
+
+    ocsp->status = NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    ngx_int_t          rc;
+    ngx_ssl_ocsp_t    *ocsp;
+    ngx_connection_t  *c;
+
+    c = ctx->data;
+    ocsp = c->ssl->ocsp;
+    ocsp->ctx = NULL;
+
+    rc = ngx_ssl_ocsp_verify(ctx);
+    if (rc != NGX_OK) {
+        ocsp->status = rc;
+        ngx_ssl_ocsp_done(ctx);
+        goto done;
+    }
+
+    rc = ngx_ssl_ocsp_cache_store(ctx);
+    if (rc != NGX_OK) {
+        ocsp->status = rc;
+        ngx_ssl_ocsp_done(ctx);
+        goto done;
+    }
+
+    if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
+        ocsp->cert_status = ctx->status;
+        ocsp->status = NGX_OK;
+        ngx_ssl_ocsp_done(ctx);
+        goto done;
+    }
+
+    ngx_ssl_ocsp_done(ctx);
+
+    ngx_ssl_ocsp_validate_next(c);
+
+done:
+
+    if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) {
+        return;
+    }
+
+    c->ssl->handshaked = 1;
+
+    c->ssl->handler(c);
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx)
+{
+    char                      *s;
+    ngx_str_t                  responder;
+    ngx_url_t                  u;
+    STACK_OF(OPENSSL_STRING)  *aia;
+
+    if (ctx->host.len) {
+        return NGX_OK;
+    }
+
+    /* extract OCSP responder URL from certificate */
+
+    aia = X509_get1_ocsp(ctx->cert);
+    if (aia == NULL) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "no OCSP responder URL in certificate");
+        return NGX_ERROR;
+    }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+    s = sk_OPENSSL_STRING_value(aia, 0);
+#else
+    s = sk_value(aia, 0);
+#endif
+    if (s == NULL) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "no OCSP responder URL in certificate");
+        X509_email_free(aia);
+        return NGX_ERROR;
+    }
+
+    responder.len = ngx_strlen(s);
+    responder.data = ngx_palloc(ctx->pool, responder.len);
+    if (responder.data == NULL) {
+        X509_email_free(aia);
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(responder.data, s, responder.len);
+    X509_email_free(aia);
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    u.url = responder;
+    u.default_port = 80;
+    u.uri_part = 1;
+    u.no_resolve = 1;
+
+    if (u.url.len > 7
+        && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+    {
+        u.url.len -= 7;
+        u.url.data += 7;
+
+    } else {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "invalid URL prefix in OCSP responder \"%V\" "
+                      "in certificate", &u.url);
+        return NGX_ERROR;
+    }
+
+    if (ngx_parse_url(ctx->pool, &u) != NGX_OK) {
+        if (u.err) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "%s in OCSP responder \"%V\" in certificate",
+                          u.err, &u.url);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (u.host.len == 0) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "empty host in OCSP responder in certificate");
+        return NGX_ERROR;
+    }
+
+    ctx->addrs = u.addrs;
+    ctx->naddrs = u.naddrs;
+    ctx->host = u.host;
+    ctx->uri = u.uri;
+    ctx->port = u.port;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)
+{
+    ngx_ssl_ocsp_t  *ocsp;
+
+    ocsp = c->ssl->ocsp;
+    if (ocsp == NULL) {
+        return NGX_OK;
+    }
+
+    if (ocsp->status == NGX_ERROR) {
+        *s = "certificate status request failed";
+        return NGX_DECLINED;
+    }
+
+    switch (ocsp->cert_status) {
+
+    case V_OCSP_CERTSTATUS_GOOD:
+        return NGX_OK;
+
+    case V_OCSP_CERTSTATUS_REVOKED:
+        *s = "certificate revoked";
+        break;
+
+    default: /* V_OCSP_CERTSTATUS_UNKNOWN */
+        *s = "certificate status unknown";
+    }
+
+    return NGX_DECLINED;
+}
+
+
+void
+ngx_ssl_ocsp_cleanup(ngx_connection_t *c)
+{
+    ngx_ssl_ocsp_t  *ocsp;
+
+    ocsp = c->ssl->ocsp;
+    if (ocsp == NULL) {
+        return;
+    }
+
+    if (ocsp->ctx) {
+        ngx_ssl_ocsp_done(ocsp->ctx);
+        ocsp->ctx = NULL;
+    }
+
+    if (ocsp->certs) {
+        sk_X509_pop_free(ocsp->certs, X509_free);
+        ocsp->certs = NULL;
+    }
+}
+
+
 static ngx_ssl_ocsp_ctx_t *
-ngx_ssl_ocsp_start(void)
+ngx_ssl_ocsp_start(ngx_log_t *log)
 {
-    ngx_log_t           *log;
     ngx_pool_t          *pool;
     ngx_ssl_ocsp_ctx_t  *ctx;
 
-    pool = ngx_create_pool(2048, ngx_cycle->log);
+    pool = ngx_create_pool(2048, log);
     if (pool == NULL) {
         return NULL;
     }
@@ -885,6 +1315,36 @@ ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *c
 
 
 static void
+ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                   "ssl ocsp next");
+
+    if (++ctx->naddr >= ctx->naddrs) {
+        ngx_ssl_ocsp_error(ctx);
+        return;
+    }
+
+    ctx->request->pos = ctx->request->start;
+
+    if (ctx->response) {
+        ctx->response->last = ctx->response->pos;
+    }
+
+    if (ctx->peer.connection) {
+        ngx_close_connection(ctx->peer.connection);
+        ctx->peer.connection = NULL;
+    }
+
+    ctx->state = 0;
+    ctx->count = 0;
+    ctx->done = 0;
+
+    ngx_ssl_ocsp_connect(ctx);
+}
+
+
+static void
 ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
 {
     ngx_resolver_ctx_t  *resolve, temp;
@@ -909,6 +1369,14 @@ ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t 
         }
 
         if (resolve == NGX_NO_RESOLVER) {
+            if (ctx->naddrs == 0) {
+                ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+                              "no resolver defined to resolve %V", &ctx->host);
+
+                ngx_ssl_ocsp_error(ctx);
+                return;
+            }
+
             ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
                           "no resolver defined to resolve %V", &ctx->host);
             goto connect;
@@ -1022,16 +1490,17 @@ failed:
 static void
 ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
 {
-    ngx_int_t  rc;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
-                   "ssl ocsp connect");
-
-    /* TODO: use all ip addresses */
-
-    ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
-    ctx->peer.socklen = ctx->addrs[0].socklen;
-    ctx->peer.name = &ctx->addrs[0].name;
+    ngx_int_t    rc;
+    ngx_addr_t  *addr;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                   "ssl ocsp connect %ui/%ui", ctx->naddr, ctx->naddrs);
+
+    addr = &ctx->addrs[ctx->naddr];
+
+    ctx->peer.sockaddr = addr->sockaddr;
+    ctx->peer.socklen = addr->socklen;
+    ctx->peer.name = &addr->name;
     ctx->peer.get = ngx_event_get_peer;
     ctx->peer.log = ctx->log;
     ctx->peer.log_error = NGX_ERROR_ERR;
@@ -1041,11 +1510,16 @@ ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t 
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                    "ssl ocsp connect peer done");
 
-    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+    if (rc == NGX_ERROR) {
         ngx_ssl_ocsp_error(ctx);
         return;
     }
 
+    if (rc == NGX_BUSY || rc == NGX_DECLINED) {
+        ngx_ssl_ocsp_next(ctx);
+        return;
+    }
+
     ctx->peer.connection->data = ctx;
     ctx->peer.connection->pool = ctx->pool;
 
@@ -1054,8 +1528,10 @@ ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t 
 
     ctx->process = ngx_ssl_ocsp_process_status_line;
 
-    ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
-    ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
+    if (ctx->timeout) {
+        ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
+        ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
+    }
 
     if (rc == NGX_OK) {
         ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
@@ -1080,7 +1556,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *
     if (wev->timedout) {
         ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
                       "OCSP responder timed out");
-        ngx_ssl_ocsp_error(ctx);
+        ngx_ssl_ocsp_next(ctx);
         return;
     }
 
@@ -1089,7 +1565,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *
     n = ngx_send(c, ctx->request->pos, size);
 
     if (n == NGX_ERROR) {
-        ngx_ssl_ocsp_error(ctx);
+        ngx_ssl_ocsp_next(ctx);
         return;
     }
 
@@ -1111,7 +1587,7 @@ ngx_ssl_ocsp_write_handler(ngx_event_t *
         }
     }
 
-    if (!wev->timer_set) {
+    if (!wev->timer_set && ctx->timeout) {
         ngx_add_timer(wev, ctx->timeout);
     }
 }
@@ -1134,7 +1610,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *r
     if (rev->timedout) {
         ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                       "OCSP responder timed out");
-        ngx_ssl_ocsp_error(ctx);
+        ngx_ssl_ocsp_next(ctx);
         return;
     }
 
@@ -1158,7 +1634,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *r
             rc = ctx->process(ctx);
 
             if (rc == NGX_ERROR) {
-                ngx_ssl_ocsp_error(ctx);
+                ngx_ssl_ocsp_next(ctx);
                 return;
             }
 
@@ -1189,7 +1665,7 @@ ngx_ssl_ocsp_read_handler(ngx_event_t *r
     ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                   "OCSP responder prematurely closed connection");
 
-    ngx_ssl_ocsp_error(ctx);
+    ngx_ssl_ocsp_next(ctx);
 }
 
 
@@ -1831,6 +2307,368 @@ ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_c
 }
 
 
+static ngx_int_t
+ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    int                    n;
+    size_t                 len;
+    X509_STORE            *store;
+    const u_char          *p;
+    OCSP_CERTID           *id;
+    OCSP_RESPONSE         *ocsp;
+    OCSP_BASICRESP        *basic;
+    ASN1_GENERALIZEDTIME  *thisupdate, *nextupdate;
+
+    ocsp = NULL;
+    basic = NULL;
+    id = NULL;
+
+    if (ctx->code != 200) {
+        goto error;
+    }
+
+    /* check the response */
+
+    len = ctx->response->last - ctx->response->pos;
+    p = ctx->response->pos;
+
+    ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+    if (ocsp == NULL) {
+        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+                      "d2i_OCSP_RESPONSE() failed");
+        goto error;
+    }
+
+    n = OCSP_response_status(ocsp);
+
+    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+                      "OCSP response not successful (%d: %s)",
+                      n, OCSP_response_status_str(n));
+        goto error;
+    }
+
+    basic = OCSP_response_get1_basic(ocsp);
+    if (basic == NULL) {
+        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+                      "OCSP_response_get1_basic() failed");
+        goto error;
+    }
+
+    store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
+    if (store == NULL) {
+        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+                      "SSL_CTX_get_cert_store() failed");
+        goto error;
+    }
+
+    if (OCSP_basic_verify(basic, ctx->chain, store, ctx->flags) != 1) {
+        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+                      "OCSP_basic_verify() failed");
+        goto error;
+    }
+
+    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+    if (id == NULL) {
+        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+                      "OCSP_cert_to_id() failed");
+        goto error;
+    }
+
+    if (OCSP_resp_find_status(basic, id, &ctx->status, NULL, NULL,
+                              &thisupdate, &nextupdate)
+        != 1)
+    {
+        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+                      "certificate status not found in the OCSP response");
+        goto error;
+    }
+
+    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
+        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+                      "OCSP_check_validity() failed");
+        goto error;
+    }
+
+    if (nextupdate) {
+        ctx->valid = ngx_ssl_stapling_time(nextupdate);
+        if (ctx->valid == (time_t) NGX_ERROR) {
+            ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+                          "invalid nextUpdate time in certificate status");
+            goto error;
+        }
+
+    } else {
+        ctx->valid = NGX_MAX_TIME_T_VALUE;
+    }
+
+    OCSP_CERTID_free(id);
+    OCSP_BASICRESP_free(basic);
+    OCSP_RESPONSE_free(ocsp);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                   "ssl ocsp response, %s, %uz",
+                   OCSP_cert_status_str(ctx->status), len);
+
+    return NGX_OK;
+
+error:
+
+    if (id) {
+        OCSP_CERTID_free(id);
+    }
+
+    if (basic) {
+        OCSP_BASICRESP_free(basic);
+    }
+
+    if (ocsp) {
+        OCSP_RESPONSE_free(ocsp);
+    }
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+    size_t                 len;
+    ngx_slab_pool_t       *shpool;
+    ngx_ssl_ocsp_cache_t  *cache;
+
+    if (data) {
+        shm_zone->data = data;
+        return NGX_OK;
+    }
+
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    if (shm_zone->shm.exists) {
+        shm_zone->data = shpool->data;
+        return NGX_OK;
+    }
+
+    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_ocsp_cache_t));
+    if (cache == NULL) {
+        return NGX_ERROR;
+    }
+
+    shpool->data = cache;
+    shm_zone->data = cache;
+
+    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+                    ngx_str_rbtree_insert_value);
+
+    ngx_queue_init(&cache->expire_queue);
+
+    len = sizeof(" in OCSP cache \"\"") + shm_zone->shm.name.len;
+
+    shpool->log_ctx = ngx_slab_alloc(shpool, len);
+    if (shpool->log_ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_sprintf(shpool->log_ctx, " in OCSP cache \"%V\"%Z",
+                &shm_zone->shm.name);
+
+    shpool->log_nomem = 0;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    uint32_t                    hash;
+    ngx_shm_zone_t             *shm_zone;
+    ngx_slab_pool_t            *shpool;
+    ngx_ssl_ocsp_cache_t       *cache;
+    ngx_ssl_ocsp_cache_node_t  *node;
+
+    shm_zone = ctx->shm_zone;
+
+    if (shm_zone == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (ngx_ssl_ocsp_create_key(ctx) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache lookup");
+
+    cache = shm_zone->data;
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+    hash = ngx_hash_key(ctx->key.data, ctx->key.len);
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    node = (ngx_ssl_ocsp_cache_node_t *)
+               ngx_str_rbtree_lookup(&cache->rbtree, &ctx->key, hash);
+
+    if (node) {
+        if (node->valid > ngx_time()) {
+            ctx->status = node->status;
+            ngx_shmtx_unlock(&shpool->mutex);
+
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                           "ssl ocsp cache hit, %s",
+                           OCSP_cert_status_str(ctx->status));
+
+            return NGX_OK;
+        }
+
+        ngx_queue_remove(&node->queue);
+        ngx_rbtree_delete(&cache->rbtree, &node->node.node);
+        ngx_slab_free_locked(shpool, node);
+
+        ngx_shmtx_unlock(&shpool->mutex);
+
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                       "ssl ocsp cache expired");
+
+        return NGX_DECLINED;
+    }
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache miss");
+
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    time_t                      now, valid;
+    uint32_t                    hash;
+    ngx_queue_t                *q;
+    ngx_shm_zone_t             *shm_zone;
+    ngx_slab_pool_t            *shpool;
+    ngx_ssl_ocsp_cache_t       *cache;
+    ngx_ssl_ocsp_cache_node_t  *node;
+
+    shm_zone = ctx->shm_zone;
+
+    if (shm_zone == NULL) {
+        return NGX_OK;
+    }
+
+    valid = ctx->valid;
+
+    now = ngx_time();
+
+    if (valid < now) {
+        return NGX_OK;
+    }
+
+    if (valid == NGX_MAX_TIME_T_VALUE) {
+        valid = now + 3600;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                   "ssl ocsp cache store, valid:%T", valid - now);
+
+    cache = shm_zone->data;
+    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+    hash = ngx_hash_key(ctx->key.data, ctx->key.len);
+
+    ngx_shmtx_lock(&shpool->mutex);
+
+    node = ngx_slab_calloc_locked(shpool,
+                             sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);
+    if (node == NULL) {
+
+        if (!ngx_queue_empty(&cache->expire_queue)) {
+            q = ngx_queue_last(&cache->expire_queue);
+            node = ngx_queue_data(q, ngx_ssl_ocsp_cache_node_t, queue);
+
+            ngx_rbtree_delete(&cache->rbtree, &node->node.node);
+            ngx_queue_remove(q);
+            ngx_slab_free_locked(shpool, node);
+
+            node = ngx_slab_alloc_locked(shpool,
+                             sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);
+        }
+
+        if (node == NULL) {
+            ngx_shmtx_unlock(&shpool->mutex);
+            ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
+                          "could not allocate new entry%s", shpool->log_ctx);
+            return NGX_ERROR;
+        }
+    }
+
+    node->node.str.len = ctx->key.len;
+    node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t);
+    ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len);
+    node->node.node.key = hash;
+    node->status = ctx->status;
+    node->valid = valid;
+
+    ngx_rbtree_insert(&cache->rbtree, &node->node.node);
+    ngx_queue_insert_head(&cache->expire_queue, &node->queue);
+
+    ngx_shmtx_unlock(&shpool->mutex);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx)
+{
+    u_char        *p;
+    X509_NAME     *name;
+    ASN1_INTEGER  *serial;
+
+    p = ngx_pnalloc(ctx->pool, 60);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    ctx->key.data = p;
+    ctx->key.len = 60;
+
+    name = X509_get_subject_name(ctx->issuer);
+    if (X509_NAME_digest(name, EVP_sha1(), p, NULL) == 0) {
+        return NGX_ERROR;
+    }
+
+    p += 20;
+
+    if (X509_pubkey_digest(ctx->issuer, EVP_sha1(), p, NULL) == 0) {
+        return NGX_ERROR;
+    }
+
+    p += 20;
+
+    serial = X509_get_serialNumber(ctx->cert);
+    if (serial->length > 20) {
+        return NGX_ERROR;
+    }
+
+    p = ngx_cpymem(p, serial->data, serial->length);
+    ngx_memzero(p, 20 - serial->length);
+
+#if (NGX_DEBUG)
+    {
+        u_char  buf[120];
+
+        ngx_hex_dump(buf, ctx->key.data, ctx->key.len);
+
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+                       "ssl ocsp key %*s", sizeof(buf), buf);
+    }
+#endif
+
+    return NGX_OK;
+}
+
+
 static u_char *
 ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
 {
@@ -1891,4 +2729,50 @@ ngx_ssl_stapling_resolver(ngx_conf_t *cf
 }
 
 
+ngx_int_t
+ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
+    ngx_uint_t depth, ngx_shm_zone_t *shm_zone)
+{
+    ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,
+                  "\"ssl_ocsp\" is not supported on this platform");
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_validate(ngx_connection_t *c)
+{
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)
+{
+    return NGX_OK;
+}
+
+
+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)
+{
+    return NGX_OK;
+}
+
+
 #endif
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -120,6 +120,7 @@ typedef struct {
     unsigned                   end_stream:1;
     unsigned                   done:1;
     unsigned                   status:1;
+    unsigned                   rst:1;
 
     ngx_http_request_t        *request;
 
@@ -1205,6 +1206,7 @@ ngx_http_grpc_reinit_request(ngx_http_re
     ctx->end_stream = 0;
     ctx->done = 0;
     ctx->status = 0;
+    ctx->rst = 0;
     ctx->connection = NULL;
 
     return NGX_OK;
@@ -2088,7 +2090,10 @@ ngx_http_grpc_filter(void *data, ssize_t
                 return NGX_ERROR;
             }
 
-            if (ctx->stream_id && ctx->done) {
+            if (ctx->stream_id && ctx->done
+                && ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME
+                && ctx->type != NGX_HTTP_V2_WINDOW_UPDATE_FRAME)
+            {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "upstream sent frame for closed stream %ui",
                               ctx->stream_id);
@@ -2131,11 +2136,21 @@ ngx_http_grpc_filter(void *data, ssize_t
                 return NGX_ERROR;
             }
 
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "upstream rejected request with error %ui",
-                          ctx->error);
-
-            return NGX_ERROR;
+            if (ctx->error || !ctx->done) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream rejected request with error %ui",
+                              ctx->error);
+                return NGX_ERROR;
+            }
+
+            if (ctx->rst) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent frame for closed stream %ui",
+                              ctx->stream_id);
+                return NGX_ERROR;
+            }
+
+            ctx->rst = 1;
         }
 
         if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -50,6 +50,8 @@ static char *ngx_http_ssl_password_file(
     void *conf);
 static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
 
@@ -74,6 +76,14 @@ static ngx_conf_enum_t  ngx_http_ssl_ver
 };
 
 
+static ngx_conf_enum_t  ngx_http_ssl_ocsp[] = {
+    { ngx_string("off"), 0 },
+    { ngx_string("on"), 1 },
+    { ngx_string("leaf"), 2 },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_conf_deprecated_t  ngx_http_ssl_deprecated = {
     ngx_conf_deprecated, "ssl", "listen ... ssl"
 };
@@ -214,6 +224,27 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, crl),
       NULL },
 
+    { ngx_string("ssl_ocsp"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, ocsp),
+      &ngx_http_ssl_ocsp },
+
+    { ngx_string("ssl_ocsp_responder"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, ocsp_responder),
+      NULL },
+
+    { ngx_string("ssl_ocsp_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_http_ssl_ocsp_cache,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("ssl_stapling"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -569,6 +600,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      *     sscf->crl = { 0, NULL };
      *     sscf->ciphers = { 0, NULL };
      *     sscf->shm_zone = NULL;
+     *     sscf->ocsp_responder = { 0, NULL };
      *     sscf->stapling_file = { 0, NULL };
      *     sscf->stapling_responder = { 0, NULL };
      */
@@ -586,6 +618,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
     sscf->session_timeout = NGX_CONF_UNSET;
     sscf->session_tickets = NGX_CONF_UNSET;
     sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+    sscf->ocsp = NGX_CONF_UNSET_UINT;
+    sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;
     sscf->stapling = NGX_CONF_UNSET;
     sscf->stapling_verify = NGX_CONF_UNSET;
 
@@ -649,6 +683,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
+    ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0);
+    ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, "");
+    ngx_conf_merge_ptr_value(conf->ocsp_cache_zone,
+                         prev->ocsp_cache_zone, NULL);
+
     ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
     ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
     ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
@@ -810,6 +849,23 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
         return NGX_CONF_ERROR;
     }
 
+    if (conf->ocsp) {
+
+        if (conf->verify == 3) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "\"ssl_ocsp\" is incompatible with "
+                          "\"ssl_verify_client optional_no_ca\"");
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_ssl_ocsp(cf, &conf->ssl, &conf->ocsp_responder, conf->ocsp,
+                         conf->ocsp_cache_zone)
+            != NGX_OK)
+        {
+            return NGX_CONF_ERROR;
+        }
+    }
+
     if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -1108,6 +1164,85 @@ invalid:
 }
 
 
+static char *
+ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_ssl_srv_conf_t *sscf = conf;
+
+    size_t       len;
+    ngx_int_t    n;
+    ngx_str_t   *value, name, size;
+    ngx_uint_t   j;
+
+    if (sscf->ocsp_cache_zone != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        sscf->ocsp_cache_zone = NULL;
+        return NGX_CONF_OK;
+    }
+
+    if (value[1].len <= sizeof("shared:") - 1
+        || ngx_strncmp(value[1].data, "shared:", sizeof("shared:") - 1) != 0)
+    {
+        goto invalid;
+    }
+
+    len = 0;
+
+    for (j = sizeof("shared:") - 1; j < value[1].len; j++) {
+        if (value[1].data[j] == ':') {
+            break;
+        }
+
+        len++;
+    }
+
+    if (len == 0) {
+        goto invalid;
+    }
+
+    name.len = len;
+    name.data = value[1].data + sizeof("shared:") - 1;
+
+    size.len = value[1].len - j - 1;
+    size.data = name.data + len + 1;
+
+    n = ngx_parse_size(&size);
+
+    if (n == NGX_ERROR) {
+        goto invalid;
+    }
+
+    if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "OCSP cache \"%V\" is too small", &value[1]);
+
+        return NGX_CONF_ERROR;
+    }
+
+    sscf->ocsp_cache_zone = ngx_shared_memory_add(cf, &name, n,
+                                                  &ngx_http_ssl_module_ctx);
+    if (sscf->ocsp_cache_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    sscf->ocsp_cache_zone->init = ngx_ssl_ocsp_cache_init;
+
+    return NGX_CONF_OK;
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid OCSP cache \"%V\"", &value[1]);
+
+    return NGX_CONF_ERROR;
+}
+
+
 static ngx_int_t
 ngx_http_ssl_init(ngx_conf_t *cf)
 {
@@ -1126,17 +1261,28 @@ ngx_http_ssl_init(ngx_conf_t *cf)
 
         sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
 
-        if (sscf->ssl.ctx == NULL || !sscf->stapling) {
+        if (sscf->ssl.ctx == NULL) {
             continue;
         }
 
         clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
 
-        if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
+        if (sscf->stapling) {
+            if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
+                                          clcf->resolver_timeout)
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
+        }
+
+        if (sscf->ocsp) {
+            if (ngx_ssl_ocsp_resolver(cf, &sscf->ssl, clcf->resolver,
                                       clcf->resolver_timeout)
-            != NGX_OK)
-        {
-            return NGX_ERROR;
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
         }
     }
 
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -54,6 +54,10 @@ typedef struct {
     ngx_flag_t                      session_tickets;
     ngx_array_t                    *session_ticket_keys;
 
+    ngx_uint_t                      ocsp;
+    ngx_str_t                       ocsp_responder;
+    ngx_shm_zone_t                 *ocsp_cache_zone;
+
     ngx_flag_t                      stapling;
     ngx_flag_t                      stapling_verify;
     ngx_str_t                       stapling_file;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2127,6 +2127,7 @@ ngx_http_process_request(ngx_http_reques
     if (r->http_connection->ssl) {
         long                      rc;
         X509                     *cert;
+        const char               *s;
         ngx_http_ssl_srv_conf_t  *sscf;
 
         if (c->ssl == NULL) {
@@ -2171,6 +2172,17 @@ ngx_http_process_request(ngx_http_reques
 
                 X509_free(cert);
             }
+
+            if (ngx_ssl_ocsp_get_status(c, &s) != NGX_OK) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client SSL certificate verify error: %s", s);
+
+                ngx_ssl_remove_cached_session(c->ssl->session_ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+
+                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
+                return;
+            }
         }
     }
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2502,6 +2502,8 @@ ngx_http_upstream_test_next(ngx_http_req
         }
 
 #endif
+
+        break;
     }
 
 #if (NGX_HTTP_CACHE)
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -1075,7 +1075,7 @@ ngx_http_variable_argument(ngx_http_requ
     len = name->len - (sizeof("arg_") - 1);
     arg = name->data + sizeof("arg_") - 1;
 
-    if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
+    if (len == 0 || ngx_http_arg(r, arg, len, &value) != NGX_OK) {
         v->not_found = 1;
         return NGX_OK;
     }
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -731,9 +731,8 @@ ngx_http_v2_state_preface(ngx_http_v2_co
     }
 
     if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
-                       "invalid http2 connection preface \"%*s\"",
-                       sizeof(preface) - 1, pos);
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "invalid connection preface");
 
         return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
     }
@@ -754,9 +753,8 @@ ngx_http_v2_state_preface_end(ngx_http_v
     }
 
     if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
-                       "invalid http2 connection preface \"%*s\"",
-                       sizeof(preface) - 1, pos);
+        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
+                      "invalid connection preface");
 
         return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
     }