changeset 140:55a211e5eeb7 NGINX_0_3_17

nginx 0.3.17 *) Change: now on Linux configure checks the presence of epoll and sendfile64() in kernel. *) Feature: the "map" directive supports domain names in the ".domain.tld" form. *) Bugfix: the timeouts were not used in SSL handshake; bug appeared in 0.2.4. *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive. *) Bugfix: when the HTTPS protocol was used in the "proxy_pass" directive the port 80 was used by default.
author Igor Sysoev <http://sysoev.ru>
date Sun, 18 Dec 2005 00:00:00 +0300
parents 9cee8bc94578
children 249e67502bf3
files CHANGES CHANGES.ru auto/os/linux auto/sources src/core/nginx.h src/core/ngx_hash.c src/event/ngx_event_openssl.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_proxy_module.c src/http/ngx_http_config.c src/http/ngx_http_config.h src/http/ngx_http_upstream.c
diffstat 12 files changed, 451 insertions(+), 249 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,19 @@
+
+Changes with nginx 0.3.17                                        18 Dec 2005
+
+    *) Change: now on Linux configure checks the presence of epoll and 
+       sendfile64() in kernel.
+
+    *) Feature: the "map" directive supports domain names in the 
+       ".domain.tld" form.
+
+    *) Bugfix: the timeouts were not used in SSL handshake.
+
+    *) Bugfix: in the HTTP protocol in the "proxy_pass" directive.
+
+    *) Bugfix: when the HTTP protocol was used in the "proxy_pass" 
+       directive the port 80 was used by default.
+
 
 Changes with nginx 0.3.16                                        16 Dec 2005
 
@@ -113,7 +129,7 @@ Changes with nginx 0.3.10               
 
     *) Feature: the "if" directive supports the "=" and "!=" operations.
 
-    *) Feature: the "proxy_set_body" directive supports the HTTPS protocol.
+    *) Feature: the "proxy_pass" directive supports the HTTPS protocol.
 
     *) Feature: the "proxy_set_body" directive.
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,3 +1,19 @@
+
+Изменения в nginx 0.3.17                                          18.12.2005
+
+    *) Изменение: на Linux configure теперь проверяет наличие epoll и 
+       sendfile64() в ядре.
+
+    *) Добавление: директива map поддерживает доменные имена в формате 
+       ".domain.tld".
+
+    *) Исправление: во время SSL handshake не иcпользовались таймауты.
+
+    *) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
+
+    *) Исправление: при использовании протокола HTTPS в директиве 
+       proxy_pass по умолчанию использовался порт 80.
+
 
 Изменения в nginx 0.3.16                                          16.12.2005
 
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -38,7 +38,7 @@ fi
 
 ngx_feature="epoll"
 ngx_feature_name="NGX_HAVE_EPOLL"
-ngx_feature_run=no
+ngx_feature_run=yes
 ngx_feature_incs="#include <sys/epoll.h>"
 ngx_feature_libs=
 ngx_feature_test="int efd = 0, fd = 1, n;
@@ -62,12 +62,14 @@ fi
 CC_AUX_FLAGS="-D_GNU_SOURCE"
 ngx_feature="sendfile()"
 ngx_feature_name="NGX_HAVE_SENDFILE"
-ngx_feature_run=no
-ngx_feature_incs="#include <sys/sendfile.h>"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/sendfile.h>
+#include <errno.h>"
 ngx_feature_libs=
 ngx_feature_test="int s = 0, fd = 1;
                   ssize_t n; off_t off = 0;
-                  n = sendfile(s, fd, &off, 1)"
+                  n = sendfile(s, fd, &off, 1);
+                  if (n == -1 && errno == ENOSYS) return 1"
 . auto/feature
 
 if [ $ngx_found = yes ]; then
@@ -80,12 +82,14 @@ fi
 CC_AUX_FLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 ngx_feature="sendfile64()"
 ngx_feature_name="NGX_HAVE_SENDFILE64"
-ngx_feature_run=no
-ngx_feature_incs="#include <sys/sendfile.h>"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/sendfile.h>
+#include <errno.h>"
 ngx_feature_libs=
 ngx_feature_test="int s = 0, fd = 1;
                   ssize_t n; off_t off = 0;
-                  n = sendfile(s, fd, &off, 1)"
+                  n = sendfile(s, fd, &off, 1);
+                  if (n == -1 && errno == ENOSYS) return 1"
 . auto/feature
 
 
--- a/auto/sources
+++ b/auto/sources
@@ -266,6 +266,7 @@ HTTP_DEPS="src/http/ngx_http.h \
 
 HTTP_SRCS="src/http/ngx_http.c \
            src/http/ngx_http_core_module.c \
+           src/http/ngx_http_config.c \
            src/http/ngx_http_special_response.c \
            src/http/ngx_http_request.c \
            src/http/ngx_http_parse.c \
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.16"
+#define NGINX_VER          "nginx/0.3.17"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -76,10 +76,6 @@ ngx_hash_find_wildcard(ngx_hash_wildcard
         n--;
     }
 
-    if (n == 0) {
-        return NULL;
-    }
-
     key = 0;
 
     for (i = n; i < len; i++) {
@@ -93,8 +89,28 @@ ngx_hash_find_wildcard(ngx_hash_wildcard
     value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);
 
     if (value) {
+
+        /*
+         * the 2 low bits of value have the special meaning:
+         *     00 - value is data pointer,
+         *     01 - value is pointer to wildcard hash allowing
+         *          "*.example.com" only,
+         *     11 - value is pointer to wildcard hash allowing
+         *          both "example.com" and "*.example.com".
+         */
+
         if ((uintptr_t) value & 1) {
-            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~1);
+
+            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
+
+            if (n == 0) {
+                if ((uintptr_t) value & 2) {
+                    return hwc->value;
+
+                } else {
+                    return NULL;
+                }
+            }
 
             value = ngx_hash_find_wildcard(hwc, name, n - 1);
 
@@ -332,7 +348,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
     ngx_uint_t nelts)
 {
     size_t                len;
-    ngx_uint_t            i, n;
+    ngx_uint_t            i, n, dot;
     ngx_array_t           curr_names, next_names;
     ngx_hash_key_t       *name, *next_name;
     ngx_hash_init_t       h;
@@ -359,9 +375,11 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
                       "wc0: \"%V\"", &names[n].key);
 #endif
 
+        dot = 0;
+
         for (len = 0; len < names[n].key.len; len++) {
             if (names[n].key.data[len] == '.') {
-                len++;
+                dot = 1;
                 break;
             }
         }
@@ -371,7 +389,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
             return NGX_ERROR;
         }
 
-        name->key.len = len - 1;
+        name->key.len = len;
         name->key.data = names[n].key.data;
         name->key_hash = hinit->key(name->key.data, name->key.len);
         name->value = names[n].value;
@@ -381,6 +399,10 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
                       "wc1: \"%V\"", &name->key);
 #endif
 
+        if (dot) {
+            len++;
+        }
+
         next_names.nelts = 0;
 
         if (names[n].key.len != len) {
@@ -417,7 +439,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
 
 #if 0
             ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
-                          "wc2: \"%V\"", &next_name->key);
+                          "wc3: \"%V\"", &next_name->key);
 #endif
         }
 
@@ -442,7 +464,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
 #endif
             }
 
-            name->value = (void *) ((uintptr_t) wdc | 1);
+            name->value = (void *) ((uintptr_t) wdc | (dot ? 1 : 3));
         }
     }
 
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -334,6 +334,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
     if (sslerr == SSL_ERROR_WANT_READ) {
         c->read->ready = 0;
         c->read->handler = ngx_ssl_handshake_handler;
+        c->write->handler = ngx_ssl_handshake_handler;
 
         if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
             return NGX_ERROR;
@@ -344,6 +345,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     if (sslerr == SSL_ERROR_WANT_WRITE) {
         c->write->ready = 0;
+        c->read->handler = ngx_ssl_handshake_handler;
         c->write->handler = ngx_ssl_handshake_handler;
 
         if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
@@ -357,6 +359,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     c->ssl->no_wait_shutdown = 1;
     c->ssl->no_send_shutdown = 1;
+    c->read->eof = 1;
 
     if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
         ngx_log_error(NGX_LOG_INFO, c->log, err,
@@ -365,6 +368,8 @@ ngx_ssl_handshake(ngx_connection_t *c)
         return NGX_ERROR;
     }
 
+    c->read->error = 1;
+
     ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
 
     return NGX_ERROR;
@@ -381,6 +386,11 @@ ngx_ssl_handshake_handler(ngx_event_t *e
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "SSL handshake handler: %d", ev->write);
 
+    if (ev->timedout) {
+        c->ssl->handler(c);
+        return;
+    }
+
     if (ngx_ssl_handshake(c) == NGX_AGAIN) {
         return;
     }
@@ -548,6 +558,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
         return NGX_DONE;
     }
 
+    c->read->error = 1;
     ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
 
     return NGX_ERROR;
@@ -773,6 +784,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
     c->ssl->no_wait_shutdown = 1;
     c->ssl->no_send_shutdown = 1;
+    c->write->error = 1;
 
     ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");
 
@@ -795,6 +807,7 @@ ngx_int_t
 ngx_ssl_shutdown(ngx_connection_t *c)
 {
     int         n, sslerr, mode;
+    ngx_err_t   err;
     ngx_uint_t  again;
 
     if (c->timedout) {
@@ -866,7 +879,9 @@ ngx_ssl_shutdown(ngx_connection_t *c)
         return NGX_AGAIN;
     }
 
-    ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_shutdown() failed");
+    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+    ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
 
     SSL_free(c->ssl->connection);
     c->ssl = NULL;
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -9,8 +9,6 @@
 #include <ngx_http.h>
 
 
-#define NGX_HTTP_MAP_HASH       10007
-
 typedef struct {
     ngx_uint_t                  hash_max_size;
     ngx_uint_t                  hash_bucket_size;
@@ -18,13 +16,7 @@ typedef struct {
 
 
 typedef struct {
-    ngx_pool_t                 *pool;
-
-    ngx_array_t                 keys;
-    ngx_array_t                *keys_hash;
-
-    ngx_array_t                 dns_wildcards;
-    ngx_array_t                *dns_hash;
+    ngx_http_hash_conf_t        hash;
 
     ngx_array_t                *values_hash;
 
@@ -253,40 +245,44 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_array_init(&ctx.keys, pool, 16384, sizeof(ngx_hash_key_t))
+    if (ngx_array_init(&ctx.hash.keys, pool, 16384, sizeof(ngx_hash_key_t))
         != NGX_OK)
     {
         ngx_destroy_pool(pool);
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_array_init(&ctx.dns_wildcards, pool, 16384, sizeof(ngx_hash_key_t))
+    if (ngx_array_init(&ctx.hash.dns_wildcards, pool, 16384,
+                       sizeof(ngx_hash_key_t))
         != NGX_OK)
     {
         ngx_destroy_pool(pool);
         return NGX_CONF_ERROR;
     }
 
-    ctx.keys_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH);
-    if (ctx.keys_hash == NULL) {
+    ctx.hash.keys_hash = ngx_pcalloc(pool,
+                                    sizeof(ngx_array_t) * NGX_HTTP_CONFIG_HASH);
+    if (ctx.hash.keys_hash == NULL) {
         ngx_destroy_pool(pool);
         return NGX_CONF_ERROR;
     }
 
-    ctx.dns_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH);
-    if (ctx.dns_hash == NULL) {
+    ctx.hash.dns_hash = ngx_pcalloc(pool,
+                                    sizeof(ngx_array_t) * NGX_HTTP_CONFIG_HASH);
+    if (ctx.hash.dns_hash == NULL) {
         ngx_destroy_pool(pool);
         return NGX_CONF_ERROR;
     }
 
     ctx.values_hash = ngx_pcalloc(pool,
-                                  sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH);
+                                  sizeof(ngx_array_t) * NGX_HTTP_CONFIG_HASH);
     if (ctx.values_hash == NULL) {
         ngx_destroy_pool(pool);
         return NGX_CONF_ERROR;
     }
 
-    ctx.pool = cf->pool;
+    ctx.hash.pool = cf->pool;
+    ctx.hash.temp_pool = pool;
     ctx.default_value = NULL;
     ctx.hostnames = 0;
 
@@ -312,11 +308,13 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
     hash.name = "map_hash";
     hash.pool = cf->pool;
 
-    if (ctx.keys.nelts) {
+    if (ctx.hash.keys.nelts) {
         hash.hash = &map->hash;
         hash.temp_pool = NULL;
 
-        if (ngx_hash_init(&hash, ctx.keys.elts, ctx.keys.nelts) != NGX_OK) {
+        if (ngx_hash_init(&hash, ctx.hash.keys.elts, ctx.hash.keys.nelts)
+            != NGX_OK)
+        {
             return NGX_CONF_ERROR;
         }
     }
@@ -324,16 +322,17 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
     map->default_value = ctx.default_value ? ctx.default_value:
                                              &ngx_http_variable_null_value;
 
-    if (ctx.dns_wildcards.nelts) {
+    if (ctx.hash.dns_wildcards.nelts) {
 
-        ngx_qsort(ctx.dns_wildcards.elts, (size_t) ctx.dns_wildcards.nelts,
+        ngx_qsort(ctx.hash.dns_wildcards.elts,
+                  (size_t) ctx.hash.dns_wildcards.nelts,
                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
 
         hash.hash = NULL;
         hash.temp_pool = pool;
 
-        if (ngx_hash_wildcard_init(&hash, ctx.dns_wildcards.elts,
-                                   ctx.dns_wildcards.nelts)
+        if (ngx_hash_wildcard_init(&hash, ctx.hash.dns_wildcards.elts,
+                                   ctx.hash.dns_wildcards.nelts)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -363,13 +362,12 @@ ngx_http_map_cmp_dns_wildcards(const voi
 static char *
 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
-    size_t                      len;
-    ngx_str_t                  *value, file, *name;
-    ngx_uint_t                  i, n, key;
-    ngx_hash_key_t             *m;
+    u_char                      ch;
+    ngx_int_t                   rc;
+    ngx_str_t                  *value, file;
+    ngx_uint_t                  i, key, flags;
     ngx_http_map_conf_ctx_t    *ctx;
-    ngx_http_variable_value_t  *var, *old, **vp;
-    u_char                      buf[2048];
+    ngx_http_variable_value_t  *var, **vp;
 
     ctx = cf->ctx;
 
@@ -410,7 +408,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
         key = ngx_hash(key, value[1].data[i]);
     }
 
-    key %= NGX_HTTP_MAP_HASH;
+    key %= NGX_HTTP_CONFIG_HASH;
 
     vp = ctx->values_hash[key].elts;
 
@@ -435,13 +433,13 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
         }
     }
 
-    var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
+    var = ngx_palloc(ctx->hash.pool, sizeof(ngx_http_variable_value_t));
     if (var == NULL) {
         return NGX_CONF_ERROR;
     }
 
     var->len = value[1].len;
-    var->data = ngx_pstrdup(ctx->pool, &value[1]);
+    var->data = ngx_pstrdup(ctx->hash.pool, &value[1]);
     if (var->data == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -459,179 +457,54 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
 
 found:
 
-    if (value[0].data[0] != '*' || ctx->hostnames == 0) {
-
-        if (ngx_strcmp(value[0].data, "default") != 0) {
-
-            if (value[0].len && value[0].data[0] == '!') {
-                value[0].len--;
-                value[0].data++;
-            }
-
-            key = 0;
+    ch = value[0].data[0];
 
-            for (i = 0; i < value[0].len; i++) {
-                value[0].data[i] = ngx_tolower(value[0].data[i]);
-                key = ngx_hash(key, value[0].data[i]);
-            }
-
-            key %= NGX_HTTP_MAP_HASH;
-
-            name = ctx->keys_hash[key].elts;
-
-            if (name) {
-                for (i = 0; i < ctx->keys_hash[key].nelts; i++) {
-                    if (value[0].len != name[i].len) {
-                        continue;
-                    }
+    if ((ch != '*' && ch != '.') || ctx->hostnames == 0) {
 
-                    if (ngx_strncmp(value[0].data, name[i].data, value[0].len)
-                        == 0)
-                    {
-                        m = ctx->keys.elts;
-                        for (i = 0; i < ctx->keys.nelts; i++) {
-                            if (ngx_strcmp(value[0].data, m[i].key.data) == 0) {
-                                old = m[i].value;
-                                m[i].value = var;
+        if (ngx_strcmp(value[0].data, "default") == 0) {
 
-                                goto duplicate;
-                            }
-                        }
-                    }
-                }
-
-            } else {
-                if (ngx_array_init(&ctx->keys_hash[key], cf->pool, 4,
-                                   sizeof(ngx_str_t))
-                    != NGX_OK)
-                {
-                    return NGX_CONF_ERROR;
-                }
-            }
-
-            name = ngx_array_push(&ctx->keys_hash[key]);
-            if (name == NULL) {
+            if (ctx->default_value) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate default map parameter");
                 return NGX_CONF_ERROR;
             }
 
-            *name = value[0];
+            ctx->default_value = var;
 
-            m = ngx_array_push(&ctx->keys);
-            if (m == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            m->key = value[0];
-            m->key_hash = ngx_hash_key(value[0].data, value[0].len);
-            m->value = var;
+            return NGX_CONF_OK;
+        }
 
-        } else {
-            if (ctx->default_value) {
-                old = ctx->default_value;
-                ctx->default_value = var;
+        if (value[0].len && ch == '!') {
+            value[0].len--;
+            value[0].data++;
+        }
 
-                goto duplicate;
-            }
-
-            ctx->default_value = var;
-        }
+        flags = 0;
 
     } else {
 
-        if (value[0].len < 3 || value[0].data[1] != '.') {
+        if ((ch == '*' && (value[0].len < 3 || value[0].data[1] != '.'))
+            || (ch == '.' && value[0].len < 2))
+        {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "invalid DNS wildcard \"%V\"", &value[0]);
+
             return NGX_CONF_ERROR;
         }
 
-        key = 0;
-
-        for (i = 2; i < value[0].len; i++) {
-            value[0].data[i] = ngx_tolower(value[0].data[i]);
-            key = ngx_hash(key, value[0].data[i]);
-        }
-
-        key %= NGX_HTTP_MAP_HASH;
-
-        /* convert "*.example.com" into "com.example.\0" */
-
-        len = 0;
-        n = 0;
-
-        for (i = value[0].len - 1; i; i--) {
-            if (value[0].data[i] == '.') {
-                ngx_memcpy(&buf[n], &value[0].data[i + 1], len);
-                n += len;
-                buf[n++] = '.';
-                len = 0;
-                continue;
-            }
-
-            len++;
-        }
-
-        buf[n] = '\0';
-
-        name = ctx->dns_hash[key].elts;
-
-        if (name) {
-            for (i = 0; i < ctx->dns_hash[key].nelts; i++) {
-                if (value[0].len != name[i].len) {
-                    continue;
-                }
-
-                if (ngx_strncmp(value[0].data, name[i].data, value[0].len)
-                    == 0)
-                {
-                    m = ctx->dns_wildcards.elts;
-                    for (i = 0; i < ctx->dns_wildcards.nelts; i++) {
-                        if (ngx_strcmp(buf, m[i].key.data) == 0) {
-                            old = m[i].value;
-                            m[i].value = var;
-
-                            goto duplicate;
-                        }
-                    }
-                }
-            }
-
-        } else {
-            if (ngx_array_init(&ctx->dns_hash[key], cf->pool, 4,
-                               sizeof(ngx_str_t))
-                != NGX_OK)
-            {
-                return NGX_CONF_ERROR;
-            }
-        }
-
-        name = ngx_array_push(&ctx->dns_hash[key]);
-        if (name == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        *name = value[0];
-
-        ngx_memcpy(value[0].data, buf, value[0].len);
-        value[0].len--;
-
-        m = ngx_array_push(&ctx->dns_wildcards);
-        if (m == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        m->key = value[0];
-        m->key_hash = 0;
-        m->value = var;
+        flags = NGX_HTTP_WILDCARD_HASH;
     }
 
-    return NGX_CONF_OK;
+    rc = ngx_http_config_add_hash(&ctx->hash, &value[0], var, flags);
 
-duplicate:
+    if (rc == NGX_OK) {
+        return NGX_CONF_OK;
+    }
 
-    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                       "duplicate parameter \"%V\", value: \"%V\", "
-                       "old value: \"%V\"",
-                       &value[0], var, old);
+    if (rc == NGX_BUSY) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "conflicting parameter \"%V\"", &value[0]);
+    }
 
-    return NGX_CONF_OK;
+    return NGX_CONF_ERROR;
 }
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1926,6 +1926,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
     size_t                       add;
     ngx_str_t                   *value, *url;
+    ngx_uint_t                   port;
     ngx_inet_upstream_t          inet_upstream;
     ngx_http_core_loc_conf_t    *clcf;
 #if (NGX_HTTP_SSL)
@@ -1945,12 +1946,14 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
     if (ngx_strncasecmp(url->data, "http://", 7) == 0) {
         add = 7;
+        port = 80;
 
     } else if (ngx_strncasecmp(url->data, "https://", 8) == 0) {
 
 #if (NGX_HTTP_SSL)
 
         add = 8;
+        port = 443;
 
         plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
         if (plcf->upstream.ssl == NULL) {
@@ -2019,7 +2022,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         inet_upstream.name = *url;
         inet_upstream.url.len = url->len - add;
         inet_upstream.url.data = url->data + add;
-        inet_upstream.default_port_value = 80;
+        inet_upstream.default_port_value = port;
         inet_upstream.uri_part = 1;
 
         plcf->peers = ngx_inet_upstream_parse(cf, &inet_upstream);
@@ -2032,8 +2035,8 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         plcf->upstream.uri = inet_upstream.uri;
     }
 
-    plcf->upstream.schema.len = sizeof("http://") - 1;
-    plcf->upstream.schema.data = (u_char *) "http://";
+    plcf->upstream.schema.len = add;
+    plcf->upstream.schema.data = url->data;
 
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
 
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_config.c
@@ -0,0 +1,214 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+ngx_int_t
+ngx_http_config_add_hash(ngx_http_hash_conf_t *h, ngx_str_t *key, void *value,
+    ngx_uint_t flags)
+{
+    size_t           len;
+    ngx_str_t       *name;
+    ngx_uint_t       i, k, n, skip;
+    ngx_hash_key_t  *hk;
+    u_char           buf[2048];
+
+    if (!(flags & NGX_HTTP_WILDCARD_HASH)) {
+
+        /* exact hash */
+
+        k = 0;
+
+        for (i = 0; i < key->len; i++) {
+            key->data[i] = ngx_tolower(key->data[i]);
+            k = ngx_hash(k, key->data[i]);
+        }
+
+        k %= NGX_HTTP_CONFIG_HASH;
+
+        /* check conflicts in exact hash */
+
+        name = h->keys_hash[k].elts;
+
+        if (name) {
+            for (i = 0; i < h->keys_hash[k].nelts; i++) {
+                if (key->len != name[i].len) {
+                    continue;
+                }
+
+                if (ngx_strncmp(key->data, name[i].data, key->len) == 0) {
+                    return NGX_BUSY;
+                }
+            }
+
+        } else {
+            if (ngx_array_init(&h->keys_hash[k], h->temp_pool, 4,
+                               sizeof(ngx_str_t))
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
+        }
+
+        name = ngx_array_push(&h->keys_hash[k]);
+        if (name == NULL) {
+            return NGX_ERROR;
+        }
+
+        *name = *key;
+
+        hk = ngx_array_push(&h->keys);
+        if (hk == NULL) {
+            return NGX_ERROR;
+        }
+
+        hk->key = *key;
+        hk->key_hash = ngx_hash_key(key->data, key->len);
+        hk->value = value;
+
+    } else {
+
+        /* wildcard hash */
+
+        skip = (key->data[0] == '*') ? 2 : 1;
+        k = 0;
+
+        for (i = skip; i < key->len; i++) {
+            key->data[i] = ngx_tolower(key->data[i]);
+            k = ngx_hash(k, key->data[i]);
+        }
+
+        k %= NGX_HTTP_CONFIG_HASH;
+
+        if (skip == 1) {
+
+            /* check conflicts in exact hash for ".example.com" */
+
+            name = h->keys_hash[k].elts;
+
+            if (name) {
+                len = key->len - skip;
+
+                for (i = 0; i < h->keys_hash[k].nelts; i++) {
+                    if (len != name[i].len) {
+                        continue;
+                    }
+
+                    if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
+                        return NGX_BUSY;
+                    }
+                }
+
+            } else {
+                if (ngx_array_init(&h->keys_hash[k], h->temp_pool, 4,
+                                   sizeof(ngx_str_t))
+                    != NGX_OK)
+                {
+                    return NGX_ERROR;
+                }
+            }
+
+            name = ngx_array_push(&h->keys_hash[k]);
+            if (name == NULL) {
+                return NGX_ERROR;
+            }
+
+            name->len = key->len - 1;
+            name->data = ngx_palloc(h->temp_pool, name->len);
+            if (name->data == NULL) {
+                return NGX_ERROR;
+            }
+
+            ngx_memcpy(name->data, &key->data[1], name->len);
+        }
+
+
+        /*
+         * convert "*.example.com" to "com.example.\0"
+         *      and ".example.com" to "com.example\0"
+         */
+
+        len = 0;
+        n = 0;
+
+        for (i = key->len - 1; i; i--) {
+            if (key->data[i] == '.') {
+                ngx_memcpy(&buf[n], &key->data[i + 1], len);
+                n += len;
+                buf[n++] = '.';
+                len = 0;
+                continue;
+            }
+
+            len++;
+        }
+
+        if (len) {
+            ngx_memcpy(&buf[n], &key->data[1], len);
+            n += len;
+        }
+
+        buf[n] = '\0';
+
+
+        /* check conflicts in wildcard hash */
+
+        name = h->dns_hash[k].elts;
+
+        if (name) {
+            len = key->len - skip;
+
+            for (i = 0; i < h->dns_hash[k].nelts; i++) {
+                if (len != name[i].len) {
+                    continue;
+                }
+
+                if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
+                    return NGX_BUSY;
+                }
+            }
+
+        } else {
+            if (ngx_array_init(&h->dns_hash[k], h->temp_pool, 4,
+                               sizeof(ngx_str_t))
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
+        }
+
+        name = ngx_array_push(&h->dns_hash[k]);
+        if (name == NULL) {
+            return NGX_ERROR;
+        }
+
+        name->len = key->len - skip;
+        name->data = ngx_palloc(h->temp_pool, name->len);
+        if (name->data == NULL) {
+            return NGX_ERROR;
+        }
+        ngx_memcpy(name->data, key->data + skip, name->len);
+
+
+        ngx_memcpy(key->data, buf, key->len);
+        key->len--;
+
+        hk = ngx_array_push(&h->dns_wildcards);
+        if (hk == NULL) {
+            return NGX_ERROR;
+        }
+
+        hk->key = *key;
+        hk->key_hash = 0;
+        hk->value = value;
+    }
+
+    return NGX_OK;
+}
--- a/src/http/ngx_http_config.h
+++ b/src/http/ngx_http_config.h
@@ -14,9 +14,9 @@
 
 
 typedef struct {
-    void   **main_conf;
-    void   **srv_conf;
-    void   **loc_conf;
+    void        **main_conf;
+    void        **srv_conf;
+    void        **loc_conf;
 } ngx_http_conf_ctx_t;
 
 
@@ -72,5 +72,25 @@ typedef struct {
          cycle->conf_ctx[ngx_http_module.index])->main_conf[module.ctx_index]
 
 
+#define NGX_HTTP_CONFIG_HASH    10007
+
+#define NGX_HTTP_WILDCARD_HASH  1
+
+typedef struct {
+    ngx_pool_t   *pool;
+    ngx_pool_t   *temp_pool;
+
+    ngx_array_t   keys;
+    ngx_array_t  *keys_hash;
+
+    ngx_array_t   dns_wildcards;
+    ngx_array_t  *dns_hash;
+} ngx_http_hash_conf_t;
+
+
+ngx_int_t ngx_http_config_add_hash(ngx_http_hash_conf_t *h, ngx_str_t *key,
+    void *value, ngx_uint_t flags);
+
+
 
 #endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -90,6 +90,8 @@ static void *ngx_http_upstream_create_ma
 static char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf);
 
 #if (NGX_HTTP_SSL)
+static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,
+    ngx_http_upstream_t *u, ngx_connection_t *c);
 static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);
 static void ngx_http_upstream_ssl_shutdown(ngx_connection_t *c,
     ngx_peer_t *peer);
@@ -498,9 +500,8 @@ ngx_http_upstream_check_broken_connectio
 static void
 ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    ngx_int_t            rc;
-    ngx_peer_t          *peer;
-    ngx_connection_t    *c;
+    ngx_int_t          rc;
+    ngx_connection_t  *c;
 
     r->connection->log->action = "connecting to upstream";
 
@@ -517,8 +518,7 @@ ngx_http_upstream_connect(ngx_http_reque
         return;
     }
 
-    peer = &u->peer.peers->peer[u->peer.cur_peer];
-    u->state->peer = &peer->name;
+    u->state->peer = &u->peer.peers->peer[u->peer.cur_peer].name;
 
     if (rc == NGX_BUSY) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
@@ -534,6 +534,7 @@ ngx_http_upstream_connect(ngx_http_reque
     c = u->peer.connection;
 
     c->data = r;
+
     c->write->handler = ngx_http_upstream_send_request_handler;
     c->read->handler = ngx_http_upstream_process_header;
 
@@ -587,40 +588,10 @@ ngx_http_upstream_connect(ngx_http_reque
         return;
     }
 
-    /* rc == NGX_OK */
-
 #if (NGX_HTTP_SSL)
 
-    if (u->conf->ssl) {
-        if (c->ssl == NULL) {
-
-            if (ngx_ssl_create_connection(u->conf->ssl, c,
-                                          NGX_SSL_BUFFER|NGX_SSL_CLIENT)
-                == NGX_ERROR)
-            {
-                ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-                return;
-            }
-
-            c->sendfile = 0;
-        }
-
-        if (ngx_ssl_set_session(c, peer->ssl_session) != NGX_OK) {
-            ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-
-        rc = ngx_ssl_handshake(c);
-
-        if (rc == NGX_AGAIN) {
-            c->ssl->handler = ngx_http_upstream_ssl_handshake;
-            return;
-        }
-
-        ngx_http_upstream_ssl_handshake(c);
-
+    if (u->conf->ssl && c->ssl == NULL) {
+        ngx_http_upstream_ssl_init_connection(r, u, c);
         return;
     }
 
@@ -633,6 +604,43 @@ ngx_http_upstream_connect(ngx_http_reque
 #if (NGX_HTTP_SSL)
 
 static void
+ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
+    ngx_http_upstream_t *u, ngx_connection_t *c)
+{
+    ngx_int_t    rc;
+    ngx_peer_t  *peer;
+
+    if (ngx_ssl_create_connection(u->conf->ssl, c,
+                                  NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+        == NGX_ERROR)
+    {
+        ngx_http_upstream_finalize_request(r, u,
+                                           NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    c->sendfile = 0;
+
+    peer = &u->peer.peers->peer[u->peer.cur_peer];
+
+    if (ngx_ssl_set_session(c, peer->ssl_session) != NGX_OK) {
+        ngx_http_upstream_finalize_request(r, u,
+                                           NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    rc = ngx_ssl_handshake(c);
+
+    if (rc == NGX_AGAIN) {
+        c->ssl->handler = ngx_http_upstream_ssl_handshake;
+        return;
+    }
+
+    ngx_http_upstream_ssl_handshake(c);
+}
+
+
+static void
 ngx_http_upstream_ssl_handshake(ngx_connection_t *c)
 {
     ngx_http_request_t   *r;
@@ -671,7 +679,8 @@ ngx_http_upstream_reinit(ngx_http_reques
                 sizeof(ngx_http_upstream_headers_in_t));
 
     if (ngx_list_init(&r->upstream->headers_in.headers, r->pool, 8,
-                      sizeof(ngx_table_elt_t)) != NGX_OK)
+                      sizeof(ngx_table_elt_t))
+        != NGX_OK)
     {
         return NGX_ERROR;
     }
@@ -851,6 +860,15 @@ ngx_http_upstream_send_request_handler(n
         return;
     }
 
+#if (NGX_HTTP_SSL)
+
+    if (u->conf->ssl && c->ssl == NULL) {
+        ngx_http_upstream_ssl_init_connection(r, u, c);
+        return;
+    }
+
+#endif
+
     ngx_http_upstream_send_request(r, u);
 }