nginx-0.3.45-RELEASE import

    *) 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 and 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.
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index e2191ef..a57fbfb 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -19,6 +19,10 @@
 
 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_command_t  ngx_http_ssl_commands[] = {
 
     { ngx_string("ssl"),
@@ -81,6 +84,27 @@
       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 @@
     { 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 @@
 
 
 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 @@
      *     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_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 @@
                       &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) {
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 4207cdf..8580309 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -22,10 +22,14 @@
 
     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;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 680b3bd..5207fa1 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1172,8 +1172,12 @@
 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 @@
         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")
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index cc40f44..e467dde 100644
--- 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
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index d1072a9..734ad69 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -163,6 +163,26 @@
 ;
 
 
+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 @@
 
 #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 @@
             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 @@
         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_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;
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 0f162a7..83f2adc 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -810,19 +810,12 @@
 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;