nginx-0.1.44-RELEASE import

    *) Feature: the IMAP/POP3 proxy supports SSL.

    *) Feature: the "proxy_timeout" directive of the ngx_imap_proxy_module.

    *) Feature: the "userid_mark" directive.

    *) Feature: the $remote_user variable value is determined independently
       of authorization use.
diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c
index 98c47bd..06d0df5 100644
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -90,8 +90,9 @@
     off_t                            offset;
     ssize_t                          n;
     ngx_fd_t                         fd;
-    ngx_str_t                        auth, encoded, pwd;
-    ngx_uint_t                       i, login, len, left, passwd;
+    ngx_int_t                        rc;
+    ngx_str_t                        pwd;
+    ngx_uint_t                       i, login, left, passwd;
     ngx_file_t                       file;
     ngx_http_auth_basic_ctx_t       *ctx;
     ngx_http_auth_basic_loc_conf_t  *alcf;
@@ -115,57 +116,16 @@
                                                  &alcf->realm);
     }
 
-    if (r->headers_in.authorization == NULL) {
+    rc = ngx_http_auth_basic_user(r);
+
+    if (rc == NGX_DECLINED) {
         return ngx_http_auth_basic_set_realm(r, &alcf->realm);
     }
 
-    encoded = r->headers_in.authorization->value;
-
-    if (encoded.len < sizeof("Basic ") - 1
-        || ngx_strncasecmp(encoded.data, "Basic ", sizeof("Basic ") - 1) != 0)
-    {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    encoded.len -= sizeof("Basic ") - 1;
-    encoded.data += sizeof("Basic ") - 1;
-
-    while (encoded.len && encoded.data[0] == ' ') {
-        encoded.len--;
-        encoded.data++;
-    }
-
-    if (encoded.len == 0) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    auth.len = ngx_base64_decoded_length(encoded.len);
-    auth.data = ngx_palloc(r->pool, auth.len + 1);
-    if (auth.data == NULL) {
+    if (rc == NGX_ERROR) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    auth.data[auth.len] = '\0';
-
-    for (len = 0; len < auth.len; len++) {
-        if (auth.data[len] == ':') {
-            break;
-        }
-    }
-
-    if (len == auth.len) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    r->headers_in.user.len = len;
-    r->headers_in.user.data = auth.data;
-    r->headers_in.passwd.len = auth.len - len - 1;
-    r->headers_in.passwd.data = &auth.data[len + 1];
-
     fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
     if (fd == NGX_INVALID_FILE) {
@@ -208,12 +168,12 @@
                     break;
                 }
 
-                if (buf[i] != auth.data[login]) {
+                if (buf[i] != r->headers_in.user.data[login]) {
                     state = sw_skip;
                     break;
                 }
 
-                if (login == len) {
+                if (login == r->headers_in.user.len) {
                     state = sw_passwd;
                     passwd = i + 1;
                 }
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 8288df1..e8b9c4e 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -424,10 +424,10 @@
 
     escape = 0;
 
-    loc_len = r->valid_location ? u->conf->location->len : 1;
+    loc_len = r->valid_location ? u->conf->location->len - 1 : 0;
 
     if (plcf->upstream.pass_unparsed_uri && r->valid_unparsed_uri) {
-        len += r->unparsed_uri.len - 1;
+        len += r->unparsed_uri.len;
 
     } else {
         if (r->quoted_uri) {
@@ -508,11 +508,11 @@
                              r->method_name.len + 1);
     }
 
-    b->last = ngx_cpymem(b->last, u->conf->uri.data, u->conf->uri.len);
+    b->last = ngx_cpymem(b->last, u->conf->uri.data, u->conf->uri.len - 1);
 
     if (plcf->upstream.pass_unparsed_uri && r->valid_unparsed_uri) {
-        b->last = ngx_cpymem(b->last, r->unparsed_uri.data + 1,
-                             r->unparsed_uri.len - 1);
+        b->last = ngx_cpymem(b->last, r->unparsed_uri.data,
+                             r->unparsed_uri.len);
     } else {
         if (escape) {
             ngx_escape_uri(b->last, r->uri.data + loc_len,
diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c
index af60d80..5ee9712 100644
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -26,18 +26,22 @@
     ngx_str_t   name;
     ngx_str_t   domain;
     ngx_str_t   path;
-    time_t      expires;
     ngx_str_t   p3p;
+
+    time_t      expires;
+
+    u_char      mark;
 } ngx_http_userid_conf_t;
 
 
 typedef struct {
     uint32_t    uid_got[4];
     uint32_t    uid_set[4];
+    ngx_str_t   cookie;
 } ngx_http_userid_ctx_t;
 
 
-static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
+static void ngx_http_userid_get_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
@@ -61,6 +65,9 @@
 static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 
 
 static uint32_t  sequencer_v1 = 1;
@@ -84,7 +91,6 @@
 
 static ngx_conf_post_handler_pt  ngx_http_userid_domain_p =
     ngx_http_userid_domain;
-
 static ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;
 static ngx_conf_post_handler_pt  ngx_http_userid_p3p_p = ngx_http_userid_p3p;
 
@@ -99,28 +105,28 @@
       ngx_http_userid_state },
 
     { ngx_string("userid_service"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, service),
       NULL },
 
     { ngx_string("userid_name"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, name),
       NULL },
 
     { ngx_string("userid_domain"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, domain),
       &ngx_http_userid_domain_p },
 
     { ngx_string("userid_path"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, path),
@@ -140,6 +146,13 @@
       offsetof(ngx_http_userid_conf_t, p3p),
       &ngx_http_userid_p3p_p },
 
+    { ngx_string("userid_mark"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_userid_mark,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
       ngx_null_command
 };
 
@@ -205,15 +218,24 @@
 
     ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
 
+    ngx_http_userid_get_uid(r, ctx, conf);
 
-    rc = ngx_http_userid_get_uid(r, ctx, conf);
-
-    if (rc != NGX_OK) {
-        return rc;
+    if (conf->enable == NGX_HTTP_USERID_LOG) {
+        return ngx_http_next_header_filter(r);
     }
 
-    if (conf->enable == NGX_HTTP_USERID_LOG || ctx->uid_got[3] != 0) {
-        return ngx_http_next_header_filter(r);
+    if (ctx->uid_got[3] != 0) {
+        if (conf->mark == '\0') {
+            return ngx_http_next_header_filter(r);
+
+        } else {
+            if (ctx->cookie.len > 23
+                && ctx->cookie.data[22] == conf->mark
+                && ctx->cookie.data[23] == '=')
+            {
+                return ngx_http_next_header_filter(r);
+            }
+        }
     }
 
     rc = ngx_http_userid_set_uid(r, ctx, conf);
@@ -226,7 +248,7 @@
 }
 
 
-static ngx_int_t
+static void
 ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
     ngx_http_userid_conf_t *conf)
 {
@@ -235,23 +257,29 @@
     ngx_table_elt_t  **cookies;
 
     n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
-                                          &src);
+                                          &ctx->cookie);
     if (n == NGX_DECLINED) {
-        return NGX_OK;
+        return;
     }
 
-    if (src.len < 22) {
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "uid cookie: \"%V\"", &ctx->cookie);
+
+    if (ctx->cookie.len < 22) {
         cookies = r->headers_in.cookies.elts;
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent too short userid cookie \"%V\"",
                       &cookies[n]->value);
-        return NGX_OK;
+        return;
     }
 
+    src = ctx->cookie;
+
     /*
-     * we have to limit encoded string to 22 characters
-     * because there are already the millions cookies with a garbage
-     * instead of the correct base64 trail "=="
+     * we have to limit the encoded string to 22 characters because
+     *  1) cookie may be marked by "userid_mark",
+     *  2) and there are already the millions cookies with a garbage
+     *     instead of the correct base64 trail "=="
      */
 
     src.len = 22;
@@ -263,15 +291,13 @@
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent invalid userid cookie \"%V\"",
                       &cookies[n]->value);
-        return NGX_OK;
+        return;
     }
 
     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "uid: %08XD%08XD%08XD%08XD",
                    ctx->uid_got[0], ctx->uid_got[1],
                    ctx->uid_got[2], ctx->uid_got[3]);
-
-    return NGX_OK;
 }
 
 
@@ -286,47 +312,58 @@
     ngx_str_t            src, dst;
     ngx_table_elt_t     *set_cookie, *p3p;
 
-    /* TODO: mutex for sequencers */
+    /*
+     * TODO: in the threaded mode the sequencers should be in TLS and their
+     * ranges should be divided between threads
+     */
 
-    if (conf->enable == NGX_HTTP_USERID_V1) {
-        if (conf->service == NGX_CONF_UNSET) {
-            ctx->uid_set[0] = 0;
+    if (ctx->uid_got[3] == 0) {
+
+        if (conf->enable == NGX_HTTP_USERID_V1) {
+            if (conf->service == NGX_CONF_UNSET) {
+                ctx->uid_set[0] = 0;
+            } else {
+                ctx->uid_set[0] = conf->service;
+            }
+            ctx->uid_set[1] = ngx_time();
+            ctx->uid_set[2] = ngx_pid;
+            ctx->uid_set[3] = sequencer_v1;
+            sequencer_v1 += 0x100;
+
         } else {
-            ctx->uid_set[0] = htonl(conf->service);
-        }
+            if (conf->service == NGX_CONF_UNSET) {
+                if (r->in_addr == 0) {
+                    slen = sizeof(struct sockaddr_in);
+                    if (getsockname(r->connection->fd,
+                                    (struct sockaddr *) &sin, &slen) == -1)
+                    {
+                        ngx_log_error(NGX_LOG_CRIT, r->connection->log,
+                                      ngx_socket_errno, "getsockname() failed");
+                    }
 
-        ctx->uid_set[1] = ngx_time();
-        ctx->uid_set[2] = ngx_pid;
-        ctx->uid_set[3] = sequencer_v1;
-        sequencer_v1 += 0x100;
-
-    } else {
-        if (conf->service == NGX_CONF_UNSET) {
-            if (r->in_addr == 0) {
-                slen = sizeof(struct sockaddr_in);
-                if (getsockname(r->connection->fd,
-                                (struct sockaddr *) &sin, &slen) == -1)
-                {
-                    ngx_log_error(NGX_LOG_CRIT, r->connection->log,
-                                  ngx_socket_errno, "getsockname() failed");
+                    r->in_addr = sin.sin_addr.s_addr;
                 }
 
-                r->in_addr = sin.sin_addr.s_addr;
+                ctx->uid_set[0] = htonl(r->in_addr);
+
+            } else {
+                ctx->uid_set[0] = htonl(conf->service);
             }
 
-            ctx->uid_set[0] = htonl(r->in_addr);
-
-        } else {
-            ctx->uid_set[0] = htonl(conf->service);
+            ctx->uid_set[1] = htonl(ngx_time());
+            ctx->uid_set[2] = htonl(ngx_pid);
+            ctx->uid_set[3] = htonl(sequencer_v2);
+            sequencer_v2 += 0x100;
+            if (sequencer_v2 < 0x03030302) {
+                sequencer_v2 = 0x03030302;
+            }
         }
 
-        ctx->uid_set[1] = htonl(ngx_time());
-        ctx->uid_set[2] = htonl(ngx_pid);
-        ctx->uid_set[3] = htonl(sequencer_v2);
-        sequencer_v2 += 0x100;
-        if (sequencer_v2 < 0x03030302) {
-            sequencer_v2 = 0x03030302;
-        }
+    } else {
+        ctx->uid_set[0] = ctx->uid_got[0];
+        ctx->uid_set[1] = ctx->uid_got[1];
+        ctx->uid_set[2] = ctx->uid_got[2];
+        ctx->uid_set[3] = ctx->uid_got[3];
     }
 
     len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
@@ -347,13 +384,24 @@
     p = ngx_cpymem(cookie, conf->name.data, conf->name.len);
     *p++ = '=';
 
-    src.len = 16;
-    src.data = (u_char *) ctx->uid_set;
-    dst.data = p;
+    if (ctx->uid_got[3] == 0) {
+        src.len = 16;
+        src.data = (u_char *) ctx->uid_set;
+        dst.data = p;
 
-    ngx_encode_base64(&dst, &src);
+        ngx_encode_base64(&dst, &src);
 
-    p += dst.len;
+        p += dst.len;
+
+        if (conf->mark) {
+            *(p - 2) = conf->mark;
+        }
+
+    } else {
+        p = ngx_cpymem(p, ctx->cookie.data, 22);
+        *p++ = conf->mark;
+        *p++ = '=';
+    }
 
     if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
         p = ngx_cpymem(p, expires, sizeof(expires) - 1);
@@ -545,6 +593,7 @@
     conf->enable = NGX_CONF_UNSET_UINT;
     conf->service = NGX_CONF_UNSET;
     conf->expires = NGX_CONF_UNSET;
+    conf->mark = (u_char) '\xFF';
 
     return conf;
 }   
@@ -567,6 +616,14 @@
     ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);
     ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
 
+    if (conf->mark == (u_char) '\xFF') {
+        if (prev->mark == (u_char) '\xFF') {
+            conf->mark = '\0';
+        } else {
+            conf->mark = prev->mark;
+        }
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -627,7 +684,7 @@
 {
     ngx_http_userid_conf_t *ucf = conf;
 
-    ngx_str_t   *value;
+    ngx_str_t  *value;
 
     if (ucf->expires != NGX_CONF_UNSET) {
         return "is duplicate";
@@ -670,3 +727,36 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_userid_conf_t *ucf = conf;
+
+    ngx_str_t  *value;
+
+    if (ucf->mark != (u_char) '\xFF') {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        ucf->mark = '\0';
+        return NGX_CONF_OK;
+    }
+
+    if (value[1].len != 1
+        || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')
+              || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')
+              || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')
+              || value[1].data[0] == '='))
+    {
+        return "value must be \"off\" or a single letter, digit or \"=\"";
+    }
+
+    ucf->mark = value[1].data[0];
+
+    return NGX_CONF_OK;
+}
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 852a9bb..0b483e0 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -931,6 +931,76 @@
 
 
 ngx_int_t
+ngx_http_auth_basic_user(ngx_http_request_t *r)
+{
+    ngx_str_t   auth, encoded;
+    ngx_uint_t  len;
+
+    if (r->headers_in.user.len == 0 && r->headers_in.user.data != NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_in.authorization == NULL) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    encoded = r->headers_in.authorization->value;
+
+    if (encoded.len < sizeof("Basic ") - 1
+        || ngx_strncasecmp(encoded.data, "Basic ", sizeof("Basic ") - 1) != 0)
+    {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    encoded.len -= sizeof("Basic ") - 1;
+    encoded.data += sizeof("Basic ") - 1;
+
+    while (encoded.len && encoded.data[0] == ' ') {
+        encoded.len--;
+        encoded.data++;
+    }
+
+    if (encoded.len == 0) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+    
+    auth.len = ngx_base64_decoded_length(encoded.len);
+    auth.data = ngx_palloc(r->pool, auth.len + 1); 
+    if (auth.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+    
+    auth.data[auth.len] = '\0';
+    
+    for (len = 0; len < auth.len; len++) { 
+        if (auth.data[len] == ':') {
+            break;
+        }
+    }
+    
+    if (len == auth.len) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    r->headers_in.user.len = len;
+    r->headers_in.user.data = auth.data;
+    r->headers_in.passwd.len = auth.len - len - 1;
+    r->headers_in.passwd.data = &auth.data[len + 1];
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args)
 {
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 1f6b185..59fc32a 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -252,6 +252,7 @@
 
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
 ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
+ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
 
 ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args);
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index b8e97d2..594a272 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -11,9 +11,6 @@
 
 
 static void ngx_http_init_request(ngx_event_t *ev);
-#if (NGX_HTTP_SSL)
-static void ngx_http_ssl_handshake(ngx_event_t *rev);
-#endif
 static void ngx_http_process_request_line(ngx_event_t *rev);
 static void ngx_http_process_request_headers(ngx_event_t *rev);
 static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
@@ -50,6 +47,11 @@
 static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, u_char *buf,
     size_t len);
 
+#if (NGX_HTTP_SSL)
+static void ngx_http_ssl_handshake(ngx_event_t *rev);
+static void ngx_http_ssl_close_handler(ngx_event_t *ev);
+#endif
+
 
 static char *ngx_http_client_errors[] = {
 
@@ -490,6 +492,7 @@
                            "https ssl handshake: 0x%02Xd", buf[0]);
 
             c->recv = ngx_ssl_recv;
+            c->send = ngx_ssl_write;
             c->send_chain = ngx_ssl_send_chain;
 
             rc = ngx_ssl_handshake(c);
@@ -2412,27 +2415,6 @@
 }
 
 
-#if (NGX_HTTP_SSL)
-
-static void
-ngx_http_ssl_close_handler(ngx_event_t *ev)
-{
-    ngx_connection_t  *c;
-
-    c = ev->data;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http ssl close handler");
-
-    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
-        return;
-    }
-
-    ngx_http_close_connection(c);
-}
-
-#endif
-
-
 static void
 ngx_http_close_connection(ngx_connection_t *c)
 {
@@ -2441,7 +2423,7 @@
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "close http connection: %d", c->fd);
 
-#if (NGX_OPENSSL)
+#if (NGX_HTTP_SSL)
 
     if (c->ssl) {
         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
@@ -2465,6 +2447,27 @@
 }
 
 
+#if (NGX_HTTP_SSL)
+
+static void
+ngx_http_ssl_close_handler(ngx_event_t *ev)
+{
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http ssl close handler");
+
+    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+        return;
+    }
+
+    ngx_http_close_connection(c);
+}
+
+#endif
+
+
 static u_char *
 ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)
 {
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index ba3bfcb..4cf8810 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -292,6 +292,8 @@
 
                 r->err_ctx = r->ctx;
 
+                r->method = NGX_HTTP_GET;
+
                 return ngx_http_internal_redirect(r, &err_page[i].uri, NULL);
             }
         }
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 3e3f9f7..0247298 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -32,6 +32,8 @@
     ngx_http_variable_document_root(ngx_http_request_t *r, uintptr_t data);
 static ngx_http_variable_value_t *
     ngx_http_variable_request_filename(ngx_http_request_t *r, uintptr_t data);
+static ngx_http_variable_value_t *
+    ngx_http_variable_remote_user(ngx_http_request_t *r, uintptr_t data);
 
 
 /*
@@ -108,8 +110,7 @@
     { ngx_string("request_method"), ngx_http_variable_request,
       offsetof(ngx_http_request_t, method_name), 0, 0 },
 
-    { ngx_string("remote_user"), ngx_http_variable_request,
-      offsetof(ngx_http_request_t, headers_in.user), 0, 0 },
+    { ngx_string("remote_user"), ngx_http_variable_remote_user, 0, 0, 0 },
 
     { ngx_null_string, NULL, 0, 0, 0 }
 };
@@ -657,6 +658,34 @@
 }
 
 
+static ngx_http_variable_value_t *
+ngx_http_variable_remote_user(ngx_http_request_t *r, uintptr_t data)
+{
+    ngx_int_t                   rc;
+    ngx_http_variable_value_t  *vv;
+
+    rc = ngx_http_auth_basic_user(r);
+
+    if (rc == NGX_DECLINED) {
+        return NGX_HTTP_VAR_NOT_FOUND;
+    }
+
+    if (rc == NGX_ERROR) {
+        return NULL;
+    }
+
+    vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+    if (vv == NULL) {
+        return NULL;
+    }
+
+    vv->value = 0;
+    vv->text = r->headers_in.user;
+
+    return vv;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {