changeset 196:8759b346e431 NGINX_0_3_45

nginx 0.3.45 *) Feature: the "ssl_verify_client", "ssl_verify_depth", and "ssl_client_certificate" directives. *) Change: the $request_method variable now returns the main request method. *) Change: the ° symbol codes were changed in koi-win conversion table. *) Feature: the euro п╦ N symbols were added to koi-win conversion table. *) Bugfix: if nginx distributed the requests among several backends and some backend failed, then requests intended for this backend was directed to one live backend only instead of being distributed among the rest.
author Igor Sysoev <http://sysoev.ru>
date Sat, 06 May 2006 00:00:00 +0400
parents b65e20aebc10
children 93658b91fad2
files CHANGES CHANGES.ru conf/koi-win src/core/nginx.h src/event/ngx_event_connect.c 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 src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/http/ngx_http_variables.c
diffstat 13 files changed, 408 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,23 @@
 
+Changes with nginx 0.3.45                                        06 May 2006
+
+    *) Feature: the "ssl_verify_client", "ssl_verify_depth", and 
+       "ssl_client_certificate" directives.
+
+    *) Change: the $request_method variable now returns the main request 
+       method.
+
+    *) Change: the &deg; symbol codes were changed in koi-win conversion 
+       table.
+
+    *) Feature: the euro и N symbols were added to koi-win conversion table.
+
+    *) Bugfix: if nginx distributed the requests among several backends and 
+       some backend failed, then requests intended for this backend was 
+       directed to one live backend only instead of being distributed among 
+       the rest.
+
+
 Changes with nginx 0.3.44                                        04 May 2006
 
     *) Feature: the "wait" parameter in the SSI "include" command.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,24 @@
 
+Изменения в nginx 0.3.45                                          06.05.2006
+
+    *) Добавление: директивы ssl_verify_client, ssl_verify_depth и 
+       ssl_client_certificate.
+
+    *) Изменение: теперь переменная $request_method возвращает метод только 
+       основного запроса.
+
+    *) Изменение: в таблице перекодировки koi-win изменены коды символа 
+       &deg;.
+
+    *) Добавление: в таблицу перекодировки koi-win добавлены символы евро и 
+       номера.
+
+    *) Исправление: если nginx распределял запросы на несколько машин, то 
+       при падении одной из них запросы, предназначенные для этой машины, 
+       перенаправлялись только на одну машину вместо того, чтобы равномерно 
+       распределяться между остальными.
+
+
 Изменения в nginx 0.3.44                                          04.05.2006
 
     *) Добавление: параметр wait в команде SSI inlcude.
--- a/conf/koi-win
+++ b/conf/koi-win
@@ -1,12 +1,12 @@
 
 charset_map  koi8-r  windows-1251 {
 
+    80  88 ; # euro
+
     95  95 ; # bullet
 
     9A  A0 ; # &nbsp;
 
-    9C  B0 ; # &deg;
-
     9E  B7 ; # &middot;
 
     A3  B8 ; # small yo
@@ -18,12 +18,16 @@ charset_map  koi8-r  windows-1251 {
     AD  B4 ; # small Ukrainian soft g
     AE  A2 ; # small Byelorussian short u
 
+    B0  B0 ; # &deg;
+
     B3  A8 ; # capital YO
     B4  AA ; # capital Ukrainian YE
 
     B6  B2 ; # capital Ukrainian I
     B7  AF ; # capital Ukrainian J
 
+    B9  B9 ; # No
+
     BD  A5 ; # capital Ukrainian soft G
     BE  A1 ; # capital Byelorussian short U
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.44"
+#define NGINX_VER          "nginx/0.3.45"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -61,54 +61,75 @@ ngx_event_connect_peer(ngx_peer_connecti
 
             /* it's a first try - get a current peer */
 
-            pc->cur_peer = pc->peers->current;
+            for ( ;; ) {
+                pc->cur_peer = pc->peers->current;
+
+                peer = &pc->peers->peer[pc->cur_peer];
+
+                if (peer->max_fails == 0 || peer->fails <= peer->max_fails) {
+                    break;
+                }
+
+                if (now - peer->accessed > peer->fail_timeout) {
+                    peer->fails = 0;
+                    break;
+                }
+
+                pc->peers->current++;
+
+                if (pc->peers->current >= pc->peers->number) {
+                    pc->peers->current = 0;
+                }
+
+                pc->peers->weight = pc->peers->peer[pc->peers->current].weight;
+
+                pc->tries--;
+
+                if (pc->tries) {
+                    continue;
+                }
+
+                goto failed;
+            }
 
             pc->peers->weight--;
 
             if (pc->peers->weight == 0) {
                 pc->peers->current++;
-            }
 
-            if (pc->peers->current >= pc->peers->number) {
-                pc->peers->current = 0;
-            }
+                if (pc->peers->current >= pc->peers->number) {
+                    pc->peers->current = 0;
+                }
 
-            if (pc->peers->weight == 0) {
                 pc->peers->weight = pc->peers->peer[pc->peers->current].weight;
             }
-        }
-
-        for ( ;; ) {
-            peer = &pc->peers->peer[pc->cur_peer];
-
-            if (peer->max_fails == 0 || peer->fails <= peer->max_fails) {
-                break;
-            }
 
-            if (now - peer->accessed > peer->fail_timeout) {
-                peer->fails = 0;
-                break;
-            }
-
-            pc->cur_peer++;
+        } else {
+            for ( ;; ) {
+                peer = &pc->peers->peer[pc->cur_peer];
 
-            if (pc->cur_peer >= pc->peers->number) {
-                pc->cur_peer = 0;
-            }
-
-            pc->tries--;
+                if (peer->max_fails == 0 || peer->fails <= peer->max_fails) {
+                    break;
+                }
 
-            if (pc->tries == 0) {
-
-                /* all peers failed, mark them as live for quick recovery */
-
-                for (i = 0; i < pc->peers->number; i++) {
-                    pc->peers->peer[i].fails = 0;
+                if (now - peer->accessed > peer->fail_timeout) {
+                    peer->fails = 0;
+                    break;
                 }
 
-                /* ngx_unlock_mutex(pc->peers->mutex); */
+                pc->cur_peer++;
+
+                if (pc->cur_peer >= pc->peers->number) {
+                    pc->cur_peer = 0;
+                }
 
-                return NGX_BUSY;
+                pc->tries--;
+
+                if (pc->tries) {
+                    continue;
+                }
+
+                goto failed;
             }
         }
     }
@@ -319,6 +340,18 @@ ngx_event_connect_peer(ngx_peer_connecti
     wev->ready = 1;
 
     return NGX_OK;
+
+failed:
+
+    /* all peers failed, mark them as live for quick recovery */
+
+    for (i = 0; i < pc->peers->number; i++) {
+        pc->peers->peer[i].fails = 0;
+    }
+
+    /* ngx_unlock_mutex(pc->peers->mutex); */
+
+    return NGX_BUSY;
 }
 
 
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -164,7 +164,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
     }
 
     if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
-                                    SSL_FILETYPE_PEM) == 0)
+                                    SSL_FILETYPE_PEM)
+        == 0)
     {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                       "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
@@ -176,6 +177,26 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
 
 
 ngx_int_t
+ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert)
+{
+    if (ngx_conf_full_name(cf->cycle, cert) == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_load_verify_locations(\"%s\") failed",
+                      cert->data);
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl)
 {
     if (SSL_CTX_need_tmp_RSA(ssl->ctx) == 0) {
@@ -1023,6 +1044,88 @@ ngx_ssl_get_cipher_name(ngx_connection_t
 }
 
 
+ngx_int_t
+ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    char       *p;
+    size_t      len;
+    X509       *cert;
+    X509_NAME  *name;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    name = X509_get_subject_name(cert);
+
+    if (name == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = X509_NAME_oneline(name, NULL, 0);
+
+    for (len = 0; p[len]; len++) { /* void */ }
+
+    s->len = len;
+    s->data = ngx_palloc(pool, len);
+    if (s->data == NULL) {
+        OPENSSL_free(p);
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->data, p, len);
+
+    OPENSSL_free(p);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    char       *p;
+    size_t      len;
+    X509       *cert;
+    X509_NAME  *name;
+
+    s->len = 0;
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+
+    if (cert == NULL) {
+        return NGX_OK;
+    }
+
+    name = X509_get_issuer_name(cert);
+
+    if (name == NULL) {
+        return NGX_ERROR;
+    }
+
+    p = X509_NAME_oneline(name, NULL, 0);
+
+    for (len = 0; p[len]; len++) { /* void */ }
+
+    s->len = len;
+    s->data = ngx_palloc(pool, len);
+    if (s->data == NULL) {
+        OPENSSL_free(p);
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->data, p, len);
+
+    OPENSSL_free(p);
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_openssl_create_conf(ngx_cycle_t *cycle)
 {
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -31,6 +31,7 @@ typedef struct {
 
 typedef struct {
     SSL                        *connection;
+
     ngx_int_t                   last;
     ngx_buf_t                  *buf;
 
@@ -60,10 +61,15 @@ typedef struct {
 #define NGX_SSL_BUFSIZE  16384
 
 
+#define NGX_SSL_VERIFY   SSL_VERIFY_PEER
+
+
 ngx_int_t ngx_ssl_init(ngx_log_t *log);
 ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols);
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_str_t *key);
+ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_str_t *cert);
 ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl);
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);
@@ -75,6 +81,11 @@ ngx_int_t ngx_ssl_set_session(ngx_connec
 
 u_char *ngx_ssl_get_protocol(ngx_connection_t *c);
 u_char *ngx_ssl_get_cipher_name(ngx_connection_t *c);
+ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
+
 
 
 ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -19,6 +19,10 @@ typedef u_char *(*ngx_ssl_variable_handl
 
 static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_ssl_client_s_dn(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_ssl_client_i_dn(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
 static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
@@ -43,7 +47,6 @@ static ngx_conf_bitmask_t  ngx_http_ssl_
 };
 
 
-
 static ngx_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -81,6 +84,27 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, ciphers),
       NULL },
 
+    { ngx_string("ssl_verify_client"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, verify),
+      NULL },
+
+    { ngx_string("ssl_verify_depth"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, verify_depth),
+      NULL },
+
+    { ngx_string("ssl_client_certificate"),
+      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, client_certificate),
+      NULL },
+
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
@@ -142,6 +166,12 @@ static ngx_http_variable_t  ngx_http_ssl
     { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGABLE, 0 },
 
+    { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_client_s_dn,
+      0, NGX_HTTP_VAR_CHANGABLE, 0 },
+
+    { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_client_i_dn,
+      0, NGX_HTTP_VAR_CHANGABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -180,6 +210,58 @@ ngx_http_ssl_variable(ngx_http_request_t
 
 
 static ngx_int_t
+ngx_http_ssl_client_s_dn(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    if (r->connection->ssl) {
+        if (ngx_ssl_get_subject_dn(r->connection, r->pool, (ngx_str_t *) v)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+
+        if (v->len) {
+            v->valid = 1;
+            v->no_cachable = 0;
+            v->not_found = 0;
+
+            return NGX_OK;
+        }
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssl_client_i_dn(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    if (r->connection->ssl) {
+        if (ngx_ssl_get_issuer_dn(r->connection, r->pool, (ngx_str_t *) v)
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+
+        if (v->len) {
+            v->valid = 1;
+            v->no_cachable = 0;
+            v->not_found = 0;
+
+            return NGX_OK;
+        }
+    }
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_ssl_add_variables(ngx_conf_t *cf)
 {
     ngx_http_variable_t  *var, *v;
@@ -217,12 +299,16 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      *     scf->certificate.data = NULL;
      *     scf->certificate_key.len = 0;
      *     scf->certificate_key.data = NULL;
+     *     scf->client_certificate.len = 0;
+     *     scf->client_certificate.data = NULL;
      *     scf->ciphers.len = 0;
      *     scf->ciphers.data = NULL;
      */
 
     scf->enable = NGX_CONF_UNSET;
     scf->session_timeout = NGX_CONF_UNSET;
+    scf->verify = NGX_CONF_UNSET;
+    scf->verify_depth = NGX_CONF_UNSET;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
 
     return scf;
@@ -253,12 +339,18 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
                          (NGX_CONF_BITMASK_SET
                           |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
 
+    ngx_conf_merge_value(conf->verify, prev->verify, 0);
+    ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1);
+
     ngx_conf_merge_str_value(conf->certificate, prev->certificate,
                          NGX_DEFLAUT_CERTIFICATE);
 
     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
                          NGX_DEFLAUT_CERTIFICATE_KEY);
 
+    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
+                         "");
+
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS);
 
 
@@ -291,6 +383,21 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
                       &conf->ciphers);
     }
 
+    if (conf->verify) {
+        SSL_CTX_set_verify(conf->ssl.ctx, NGX_SSL_VERIFY, NULL);
+
+        SSL_CTX_set_verify_depth(conf->ssl.ctx, conf->verify_depth);
+
+        if (conf->client_certificate.len) {
+            if (ngx_ssl_client_certificate(cf, &conf->ssl,
+                                           &conf->client_certificate)
+                != NGX_OK)
+            {
+                return NGX_CONF_ERROR;
+            }
+        }
+    }
+
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
 
     if (conf->prefer_server_ciphers) {
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -22,10 +22,14 @@ typedef struct {
 
     ngx_uint_t      protocols;
 
+    ngx_int_t       verify;
+    ngx_int_t       verify_depth;
+
     time_t          session_timeout;
 
     ngx_str_t       certificate;
     ngx_str_t       certificate_key;
+    ngx_str_t       client_certificate;
 
     ngx_str_t       ciphers;
 } ngx_http_ssl_srv_conf_t;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1172,8 +1172,12 @@ ngx_http_process_cookie(ngx_http_request
 static ngx_int_t
 ngx_http_process_request_header(ngx_http_request_t *r)
 {
-    size_t   len;
-    u_char  *ua, *user_agent, ch;
+    size_t                    len;
+    u_char                   *ua, *user_agent, ch;
+#if (NGX_HTTP_SSL)
+    long                      rc;
+    ngx_http_ssl_srv_conf_t  *sscf;
+#endif
 
     if (r->headers_in.host) {
         for (len = 0; len < r->headers_in.host->value.len; len++) {
@@ -1243,6 +1247,34 @@ ngx_http_process_request_header(ngx_http
         return NGX_ERROR;
     }
 
+#if (NGX_HTTP_SSL)
+
+    if (r->connection->ssl) {
+        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
+
+        if (sscf->verify) {
+            rc = SSL_get_verify_result(r->connection->ssl->connection);
+
+            if (rc != X509_V_OK) {
+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                              "client SSL certificate verify error: %l ", rc);
+                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
+                return NGX_ERROR;
+            }
+
+            if (SSL_get_peer_certificate(r->connection->ssl->connection)
+                == NULL)
+            {
+                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                              "client sent no required SSL certificate");
+                ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
+                return NGX_ERROR;
+            }
+        }
+    }
+
+#endif
+
     if (r->headers_in.connection) {
         if (r->headers_in.connection->value.len == 5
             && ngx_strcasecmp(r->headers_in.connection->value.data, "close")
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -75,7 +75,10 @@
 /* The special code to close connection without any response */
 #define NGX_HTTP_CLOSE                     444
 
-#define NGX_HTTP_OWN_CODES                 NGX_HTTP_TO_HTTPS
+#define NGX_HTTP_OWN_CODES                 495
+
+#define NGX_HTTPS_CERT_ERROR               495
+#define NGX_HTTPS_NO_CERT                  496
 
 /*
  * We use the special code for the plain HTTP requests that are sent to
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -163,6 +163,26 @@ static char error_416_page[] =
 ;
 
 
+static char error_495_page[] =
+"<html>" CRLF
+"<head><title>400 The SSL certificate error</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>The SSL certificate error</center>" CRLF
+;
+
+
+static char error_496_page[] =
+"<html>" CRLF
+"<head><title>400 No required SSL certificate was sent</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>No required SSL certificate was sent</center>" CRLF
+;
+
+
 static char error_497_page[] =
 "<html>" CRLF
 "<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
@@ -254,6 +274,8 @@ static ngx_str_t error_pages[] = {
 
 #define NGX_HTTP_LEVEL_400  17
 
+    ngx_string(error_495_page),  /* 495, https certificate error */
+    ngx_string(error_496_page),  /* 496, https no certificate */
     ngx_string(error_497_page),  /* 497, http to https */
     ngx_string(error_404_page),  /* 498, invalid host name */
     ngx_null_string,             /* 499, client had closed connection */
@@ -296,6 +318,8 @@ ngx_http_special_response_handler(ngx_ht
             case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
             case NGX_HTTP_REQUEST_URI_TOO_LARGE:
             case NGX_HTTP_TO_HTTPS:
+            case NGX_HTTPS_CERT_ERROR:
+            case NGX_HTTPS_NO_CERT:
             case NGX_HTTP_INTERNAL_SERVER_ERROR:
                 r->keepalive = 0;
         }
@@ -305,6 +329,8 @@ ngx_http_special_response_handler(ngx_ht
         switch (error) {
             case NGX_HTTP_BAD_REQUEST:
             case NGX_HTTP_TO_HTTPS:
+            case NGX_HTTPS_CERT_ERROR:
+            case NGX_HTTPS_NO_CERT:
                 r->lingering_close = 0;
         }
     }
@@ -372,6 +398,8 @@ ngx_http_special_response_handler(ngx_ht
                                          + NGX_HTTP_LEVEL_400;
         switch (error) {
             case NGX_HTTP_TO_HTTPS:
+            case NGX_HTTPS_CERT_ERROR:
+            case NGX_HTTPS_NO_CERT:
                 r->headers_out.status = NGX_HTTP_BAD_REQUEST;
                 error = NGX_HTTP_BAD_REQUEST;
                 break;
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -810,19 +810,12 @@ static ngx_int_t
 ngx_http_variable_request_method(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    if (r->method_name.data) {
-        if (r->upstream && r->upstream->method.len) {
-            v->len = r->upstream->method.len;
-            v->data = r->upstream->method.data;
-
-        } else {
-            v->len = r->method_name.len;
-            v->data = r->method_name.data;
-        }
-
+    if (r->main->method_name.data) {
+        v->len = r->main->method_name.len;
         v->valid = 1;
         v->no_cachable = 0;
         v->not_found = 0;
+        v->data = r->main->method_name.data;
 
     } else {
         v->not_found = 1;