changeset 198:e6da4931e0e0 NGINX_0_3_46

nginx 0.3.46 *) Feature: the "proxy_hide_header", "proxy_pass_header", "fastcgi_hide_header", and "fastcgi_pass_header" directives. *) Change: the "proxy_pass_x_powered_by", "fastcgi_x_powered_by", and "proxy_pass_server" directives were canceled. *) Feature: the "X-Accel-Buffering" response header line is supported in proxy mode. *) Bugfix: the reconfiguration bug and memory leaks in the ngx_http_perl_module.
author Igor Sysoev <http://sysoev.ru>
date Thu, 11 May 2006 00:00:00 +0400
parents 93658b91fad2
children 869664706c09
files CHANGES CHANGES.ru conf/mime.types src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_conf_file.h src/core/ngx_hash.c src/core/ngx_hash.h src/core/ngx_string.h src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/perl/ngx_http_perl_module.c src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_parse.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h
diffstat 21 files changed, 800 insertions(+), 512 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,19 @@
 
+Changes with nginx 0.3.46                                        11 May 2006
+
+    *) Feature: the "proxy_hide_header", "proxy_pass_header", 
+       "fastcgi_hide_header", and "fastcgi_pass_header" directives.
+
+    *) Change: the "proxy_pass_x_powered_by", "fastcgi_x_powered_by", and 
+       "proxy_pass_server" directives were canceled.
+
+    *) Feature: the "X-Accel-Buffering" response header line is supported 
+       in proxy mode.
+
+    *) Bugfix: the reconfiguration bug and memory leaks in the 
+       ngx_http_perl_module.
+
+
 Changes with nginx 0.3.45                                        06 May 2006
 
     *) Feature: the "ssl_verify_client", "ssl_verify_depth", and 
@@ -245,7 +260,7 @@ Changes with nginx 0.3.29               
 
 Changes with nginx 0.3.28                                        16 Feb 2006
 
-    *) Feature: the "restrict_host_names" directive is canceled.
+    *) Feature: the "restrict_host_names" directive was canceled.
 
     *) Feature: the --with-cpu-opt=ppc64 configuration parameter.
 
@@ -337,7 +352,7 @@ Changes with nginx 0.3.22               
     *) Feature: the ngx_http_perl_module supports the $r->args and 
        $r->unescape methods.
 
-    *) Feature: the method $r->query_string of ngx_http_perl_module is 
+    *) Feature: the method $r->query_string of ngx_http_perl_module was 
        canceled.
 
     *) Bugfix: segmentation fault was occurred if the "none" or "blocked" 
@@ -841,7 +856,7 @@ Changes with nginx 0.2.0                
 
 Changes with nginx 0.1.45                                        08 Sep 2005
 
-    *) Change: the "ssl_engine" directive is canceled in the 
+    *) Change: the "ssl_engine" directive was canceled in the 
        ngx_http_ssl_module and now is introduced at global level.
 
     *) Bugfix: the responses with SSI subrequests did not transferred via 
@@ -915,7 +930,7 @@ Changes with nginx 0.1.40               
 Changes with nginx 0.1.39                                        14 Jul 2005
 
     *) The changes in the ngx_http_charset_module: the "default_charset" 
-       directive is canceled; the "charset" directive sets the response 
+       directive was canceled; the "charset" directive sets the response 
        charset; the "source_charset" directive sets the source charset only.
 
     *) Bugfix: the backend "WWW-Authenticate" header line did not 
@@ -1133,7 +1148,7 @@ Changes with nginx 0.1.29               
     *) Feature: the "proxy_redirect", "proxy_pass_request_headers", 
        "proxy_pass_request_body", and "proxy_method" directives.
 
-    *) Feature: the "proxy_set_header" directive. The "proxy_x_var" is 
+    *) Feature: the "proxy_set_header" directive. The "proxy_x_var" was 
        canceled and must be replaced with the proxy_set_header directive.
 
     *) Change: the "proxy_preserve_host" is canceled and must be replaced 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,19 @@
 
+Изменения в nginx 0.3.46                                          11.05.2006
+
+    *) Добавление: директивы proxy_hide_header, proxy_pass_header, 
+       fastcgi_hide_header и fastcgi_pass_header.
+
+    *) Изменение: директивы proxy_x_powered_by, fastcgi_x_powered_by и 
+       proxy_pass_server упразднены.
+
+    *) Добавление: в режиме прокси поддерживается строка заголовка 
+       "X-Accel-Buffering" в ответе бэкенда.
+
+    *) Исправление: ошибок и утечек памяти при переконфигурации в модуле 
+       ngx_http_perl_module.
+
+
 Изменения в nginx 0.3.45                                          06.05.2006
 
     *) Добавление: директивы ssl_verify_client, ssl_verify_depth и 
--- a/conf/mime.types
+++ b/conf/mime.types
@@ -49,5 +49,6 @@ types {
     video/x-flv                           flv;
     video/x-msvideo                       avi;
     video/x-ms-wmv                        wmv;
+    video/x-ms-asf                        asx asf;
     video/x-mng                           mng;
 }
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.45"
+#define NGINX_VER          "nginx/0.3.46"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -900,38 +900,73 @@ ngx_conf_set_str_slot(ngx_conf_t *cf, ng
 
 
 char *
-ngx_conf_set_table_elt_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char  *p = conf;
 
-    ngx_str_t         *value;
+    ngx_str_t         *value, *s;
     ngx_array_t      **a;
-    ngx_table_elt_t   *elt;
     ngx_conf_post_t   *post;
 
     a = (ngx_array_t **) (p + cmd->offset);
 
     if (*a == NULL) {
-        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_table_elt_t));
+        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
         if (*a == NULL) {
             return NGX_CONF_ERROR;
         }
     }
 
-    elt = ngx_array_push(*a);
-    if (elt == NULL) {
+    s = ngx_array_push(*a);
+    if (s == NULL) {
         return NGX_CONF_ERROR;
     }
 
     value = cf->args->elts;
 
-    elt->hash = 0;
-    elt->key = value[1];
-    elt->value = value[2];
+    *s = value[1];
 
     if (cmd->post) {
         post = cmd->post;
-        return post->post_handler(cf, post, elt);
+        return post->post_handler(cf, post, s);
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char  *p = conf;
+
+    ngx_str_t         *value;
+    ngx_array_t      **a;
+    ngx_keyval_t      *kv;
+    ngx_conf_post_t   *post;
+
+    a = (ngx_array_t **) (p + cmd->offset);
+
+    if (*a == NULL) {
+        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t));
+        if (*a == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    kv = ngx_array_push(*a);
+    if (kv == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    kv->key = value[1];
+    kv->value = value[2];
+
+    if (cmd->post) {
+        post = cmd->post;
+        return post->post_handler(cf, post, kv);
     }
 
     return NGX_CONF_OK;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -323,8 +323,9 @@ void ngx_cdecl ngx_conf_log_error(ngx_ui
 
 char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
-char *ngx_conf_set_table_elt_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+char *ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+char *ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -525,180 +525,6 @@ ngx_hash_key_lc(u_char *data, size_t len
 
 
 ngx_int_t
-ngx_hash0_init(ngx_hash0_t *hash, ngx_pool_t *pool, void *names,
-    ngx_uint_t nelts)
-{
-    u_char      *p;
-    ngx_str_t   *name, *bucket;
-    ngx_uint_t   i, n, key, size, best, *test, buckets, min_buckets;
-
-    if (nelts == 0) {
-        for (name = (ngx_str_t *) names;
-             name->len;
-             name = (ngx_str_t *) ((char *) name + hash->bucket_size))
-        {
-            nelts++;
-        }
-    }
-
-    test = ngx_alloc(hash->max_size * sizeof(ngx_uint_t), pool->log);
-    if (test == NULL) {
-        return NGX_ERROR;
-    }
-
-    min_buckets = hash->bucket_limit + 1;
-
-#if (NGX_SUPPRESS_WARN)
-    best = 0;
-#endif
-
-    for (size = 1; size < hash->max_size; size++) {
-
-        buckets = 0;
-
-        for (i = 0; i < size; i++) {
-            test[i] = 0;
-        }
-
-        for (n = 0, name = (ngx_str_t *) names;
-             n < nelts;
-             n++, name = (ngx_str_t *) ((char *) name + hash->bucket_size))
-        {
-            if (name->data == NULL) {
-                continue;
-            }
-
-            key = 0;
-
-            for (i = 0; i < name->len; i++) {
-                key += ngx_tolower(name->data[i]);
-            }
-
-            key %= size;
-
-            if (test[key] == hash->bucket_limit) {
-                break;
-            }
-
-            test[key]++;
-
-            if (buckets < test[key]) {
-                buckets = test[key];
-            }
-        }
-
-        if (n == nelts) {
-            if (min_buckets > buckets) {
-                min_buckets = buckets;
-                best = size;
-            }
-
-            if (hash->bucket_limit == 1) {
-                break;
-            }
-        }
-    }
-
-    if (min_buckets == hash->bucket_limit + 1) {
-        ngx_log_error(NGX_LOG_EMERG, pool->log, 0,
-                      "could not build the %s hash, you should increase "
-                      "either %s_size: %i or %s_bucket_limit: %i",
-                      hash->name, hash->name, hash->max_size,
-                      hash->name, hash->bucket_limit);
-        ngx_free(test);
-        return NGX_ERROR;
-    }
-
-    hash->buckets = ngx_pcalloc(pool, best * hash->bucket_size);
-    if (hash->buckets == NULL) {
-        ngx_free(test);
-        return NGX_ERROR;
-    }
-
-    if (hash->bucket_limit != 1) {
-
-        for (i = 0; i < best; i++) {
-            test[i] = 0;
-        }
-
-        for (n = 0, name = (ngx_str_t *) names;
-             n < nelts;
-             n++, name = (ngx_str_t *) ((char *) name + hash->bucket_size))
-        {
-            if (name->data == NULL) {
-                continue;
-            }
-
-            key = 0;
-
-            for (i = 0; i < name->len; i++) {
-                key += ngx_tolower(name->data[i]);
-            }
-
-            key %= best;
-
-            test[key]++;
-        }
-
-        for (i = 0; i < best; i++) {
-            if (test[i] == 0) {
-                continue;
-            }
-
-            bucket = ngx_palloc(pool, test[i] * hash->bucket_size);
-            if (bucket == NULL) {
-                ngx_free(test);
-                return NGX_ERROR;
-            }
-
-            hash->buckets[i] = bucket;
-            bucket->len = 0;
-        }
-    }
-
-    for (n = 0, name = (ngx_str_t *) names;
-         n < nelts;
-         n++, name = (ngx_str_t *) ((char *) name + hash->bucket_size))
-    {
-        if (name->data == NULL) {
-            continue;
-        }
-
-        key = 0;
-
-        for (i = 0; i < name->len; i++) {
-            key += ngx_tolower(name->data[i]);
-        }
-
-        key %= best;
-
-        if (hash->bucket_limit == 1) {
-            p = (u_char *) hash->buckets + key * hash->bucket_size;
-            ngx_memcpy(p, name, hash->bucket_size);
-            continue;
-        }
-
-        for (bucket = hash->buckets[key];
-             bucket->len;
-             bucket = (ngx_str_t *) ((char *) bucket + hash->bucket_size))
-        {
-            bucket->len &= 0x7fffffff;
-        }
-
-        ngx_memcpy(bucket, name, hash->bucket_size);
-        bucket->len |= 0x80000000;
-    }
-
-    ngx_free(test);
-
-    hash->hash_size = best;
-    hash->min_buckets = min_buckets;
-
-    return NGX_OK;
-}
-
-
-ngx_int_t
 ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
 {
     ngx_uint_t  asize;
--- a/src/core/ngx_hash.h
+++ b/src/core/ngx_hash.h
@@ -94,6 +94,7 @@ typedef struct {
     ngx_uint_t        hash;
     ngx_str_t         key;
     ngx_str_t         value;
+    u_char           *lowcase_key;
 } ngx_table_elt_t;
 
 
@@ -106,7 +107,7 @@ ngx_int_t ngx_hash_init(ngx_hash_init_t 
 ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
     ngx_uint_t nelts);
 
-#define ngx_hash(key, c)   key * 31 + c
+#define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)
 ngx_uint_t ngx_hash_key(u_char *data, size_t len);
 ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);
 
@@ -115,9 +116,4 @@ ngx_int_t ngx_hash_add_key(ngx_hash_keys
     void *value, ngx_uint_t flags);
 
 
-#define ngx_hash0(key, c)   key + c
-ngx_int_t ngx_hash0_init(ngx_hash0_t *hash, ngx_pool_t *pool, void *names,
-    ngx_uint_t nelts);
-
-
 #endif /* _NGX_HASH_H_INCLUDED_ */
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -13,11 +13,17 @@
 
 
 typedef struct {
-    size_t    len;
-    u_char   *data;
+    size_t     len;
+    u_char    *data;
 } ngx_str_t;
 
 
+typedef struct {
+    ngx_str_t  key;
+    ngx_str_t  value;
+} ngx_keyval_t;
+
+
 #define ngx_string(str)  { sizeof(str) - 1, (u_char *) str }
 #define ngx_null_string  { 0, NULL }
 
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -246,13 +246,6 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.redirect_errors),
       NULL },
 
-    { ngx_string("fastcgi_x_powered_by"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_x_powered_by),
-      NULL },
-
     { ngx_string("fastcgi_read_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -318,11 +311,25 @@ static ngx_command_t  ngx_http_fastcgi_c
 
     { ngx_string("fastcgi_param"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_conf_set_table_elt_slot,
+      ngx_conf_set_keyval_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
       NULL },
 
+    { ngx_string("fastcgi_pass_header"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_array_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
+      NULL },
+
+    { ngx_string("fastcgi_hide_header"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_array_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
+      NULL },
+
       ngx_null_command
 };
 
@@ -358,6 +365,16 @@ ngx_module_t  ngx_http_fastcgi_module = 
 };
 
 
+static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {
+    ngx_string("Status"),
+    ngx_string("X-Accel-Expires"),
+    ngx_string("X-Accel-Redirect"),
+    ngx_string("X-Accel-Limit-Rate"),
+    ngx_string("X-Accel-Buffer"),
+    ngx_null_string
+};
+
+
 static ngx_int_t
 ngx_http_fastcgi_handler(ngx_http_request_t *r)
 {
@@ -390,6 +407,8 @@ ngx_http_fastcgi_handler(ngx_http_reques
     u->abort_request = ngx_http_fastcgi_abort_request;
     u->finalize_request = ngx_http_fastcgi_finalize_request;
 
+    u->buffering = 1;
+
     u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
     if (u->pipe == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -796,7 +815,7 @@ ngx_http_fastcgi_process_header(ngx_http
     u_char                         *start, *last;
     ngx_str_t                      *status_line, line;
     ngx_int_t                       rc, status;
-    ngx_uint_t                      key;
+    ngx_uint_t                      i;
     ngx_table_elt_t                *h;
     ngx_http_upstream_t            *u;
     ngx_http_fastcgi_ctx_t         *f;
@@ -806,7 +825,6 @@ ngx_http_fastcgi_process_header(ngx_http
     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-    hh = (ngx_http_upstream_header_t *) umcf->headers_in_hash.buckets;
 
     if (f == NULL) {
         f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
@@ -980,26 +998,34 @@ ngx_http_fastcgi_process_header(ngx_http
                 h->value.len = r->header_end - r->header_start;
 
                 h->key.data = ngx_palloc(r->pool,
-                                         h->key.len + 1 + h->value.len + 1);
+                               h->key.len + 1 + h->value.len + 1 + h->key.len);
                 if (h->key.data == NULL) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
                 h->value.data = h->key.data + h->key.len + 1;
+                h->lowcase_key = h->key.data + h->key.len + 1
+                                 + h->value.len + 1;
 
                 ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
                 ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
 
-                key = h->hash % umcf->headers_in_hash.hash_size;
-
-                if (hh[key].name.len == h->key.len
-                    && ngx_strcasecmp(hh[key].name.data, h->key.data) == 0)
-                {
-                    if (hh[key].handler(r, h, hh[key].offset) != NGX_OK) {
-                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                if (h->key.len == r->lowcase_index) {
+                    ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+                } else {
+                    for (i = 0; i < h->key.len; i++) {
+                        h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]);
                     }
                 }
 
+                hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+                                   h->lowcase_key, h->key.len);
+
+                if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                "http fastcgi header: \"%V: %V\"",
                                &h->key, &h->value);
@@ -1474,9 +1500,11 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      * set by ngx_pcalloc():
      *
      *     conf->upstream.bufs.num = 0;
-     *     conf->upstream.path = NULL;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
+     *     conf->upstream.hide_headers_hash = { NULL, 0 };
+     *     conf->upstream.hide_headers = NULL;
+     *     conf->upstream.pass_headers = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1510,12 +1538,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     /* "fastcgi_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
-    conf->upstream.pass_x_powered_by = NGX_CONF_UNSET;
-
-    /* the hardcoded values */
-    conf->upstream.pass_server = 1;
-    conf->upstream.pass_date = 1;
-
     return conf;
 }
 
@@ -1529,8 +1551,12 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     u_char                       *p;
     size_t                        size;
     uintptr_t                    *code;
-    ngx_uint_t                    i;
-    ngx_table_elt_t              *src;
+    ngx_str_t                    *header;
+    ngx_uint_t                    i, j;
+    ngx_array_t                   hide_headers;
+    ngx_keyval_t                 *src;
+    ngx_hash_key_t               *hk;
+    ngx_hash_init_t               hash;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
@@ -1682,12 +1708,111 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     ngx_conf_merge_value(conf->upstream.redirect_errors,
                               prev->upstream.redirect_errors, 0);
 
-    ngx_conf_merge_value(conf->upstream.pass_x_powered_by,
-                              prev->upstream.pass_x_powered_by, 1);
-
 
     ngx_conf_merge_str_value(conf->index, prev->index, "");
 
+    if (conf->upstream.hide_headers == NULL
+        && conf->upstream.pass_headers == NULL)
+    {
+        conf->upstream.hide_headers = prev->upstream.hide_headers;
+        conf->upstream.pass_headers = prev->upstream.pass_headers;
+        conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash;
+
+        if (conf->upstream.hide_headers_hash.buckets) {
+            goto peers;
+        }
+
+    } else {
+        if (conf->upstream.hide_headers == NULL) {
+            conf->upstream.hide_headers = prev->upstream.hide_headers;
+        }
+
+        if (conf->upstream.pass_headers == NULL) {
+            conf->upstream.pass_headers = prev->upstream.pass_headers;
+        }
+    }
+
+    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    for (header = ngx_http_fastcgi_hide_headers; header->len; header++) {
+        hk = ngx_array_push(&hide_headers);
+        if (hk == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        hk->key = *header;
+        hk->key_hash = ngx_hash_key_lc(header->data, header->len);
+        hk->value = (void *) 1;
+    }
+
+    if (conf->upstream.hide_headers) {
+
+        header = conf->upstream.hide_headers->elts;
+
+        for (i = 0; i < conf->upstream.hide_headers->nelts; i++) {
+
+            hk = hide_headers.elts;
+
+            for (j = 0; j < hide_headers.nelts; j++) {
+                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
+                    goto exist;
+                }
+            }
+
+            hk = ngx_array_push(&hide_headers);
+            if (hk == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            hk->key = header[i];
+            hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len);
+            hk->value = (void *) 1;
+
+        exist:
+
+            continue;
+        }
+    }
+
+    if (conf->upstream.pass_headers) {
+
+        hk = hide_headers.elts;
+        header = conf->upstream.pass_headers->elts;
+
+        for (i = 0; i < conf->upstream.pass_headers->nelts; i++) {
+
+            for (j = 0; j < hide_headers.nelts; j++) {
+
+                if (hk[j].key.data == NULL) {
+                    continue;
+                }
+
+                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
+                    hk[j].key.data = NULL;
+                    break;
+                }
+            }
+        }
+    }
+
+    hash.hash = &conf->upstream.hide_headers_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "fastcgi_hide_headers_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+peers:
+
     if (conf->peers == NULL) {
         conf->peers = prev->peers;
         conf->upstream.schema = prev->upstream.schema;
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -524,11 +524,8 @@ ngx_http_memcached_create_loc_conf(ngx_c
     conf->upstream.busy_buffers_size = 0;
     conf->upstream.max_temp_file_size = 0;
     conf->upstream.temp_file_write_size = 0;
-    conf->upstream.pass_x_powered_by = 0;
     conf->upstream.redirect_errors = 1;
     conf->upstream.redirect_404 = 1;
-    conf->upstream.pass_server = 1;
-    conf->upstream.pass_date = 1;
     conf->upstream.pass_request_headers = 0;
     conf->upstream.pass_request_body = 0;
 
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -42,7 +42,7 @@ typedef struct {
     ngx_array_t                 *body_set;
     ngx_array_t                 *headers_set_len;
     ngx_array_t                 *headers_set;
-    ngx_hash0_t                 *headers_set_hash;
+    ngx_hash_t                   headers_set_hash;
 
     ngx_array_t                 *headers_source;
     ngx_array_t                 *headers_names;
@@ -187,7 +187,7 @@ static ngx_command_t  ngx_http_proxy_com
 
     { ngx_string("proxy_set_header"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_conf_set_table_elt_slot,
+      ngx_conf_set_keyval_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_proxy_loc_conf_t, headers_source),
       NULL },
@@ -297,25 +297,18 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.fail_timeout),
       NULL },
 
-    { ngx_string("proxy_pass_x_powered_by"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_x_powered_by),
-      NULL },
-
-    { ngx_string("proxy_pass_server"),
+    { ngx_string("proxy_pass_header"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_server),
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_headers),
       NULL },
 
-    { ngx_string("proxy_pass_x_accel_expires"),
+    { ngx_string("proxy_hide_header"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
-      ngx_conf_set_flag_slot,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_x_accel_expires),
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),
       NULL },
 
       ngx_null_command
@@ -356,11 +349,23 @@ ngx_module_t  ngx_http_proxy_module = {
 static char  ngx_http_proxy_version[] = " HTTP/1.0" CRLF;
 
 
-static ngx_table_elt_t  ngx_http_proxy_headers[] = {
-    { 0, ngx_string("Host"), ngx_string("$proxy_host") },
-    { 0, ngx_string("Connection"), ngx_string("close") },
-    { 0, ngx_string("Keep-Alive"), ngx_string("") },
-    { 0, ngx_null_string, ngx_null_string }
+static ngx_keyval_t  ngx_http_proxy_headers[] = {
+    { ngx_string("Host"), ngx_string("$proxy_host") },
+    { ngx_string("Connection"), ngx_string("close") },
+    { ngx_string("Keep-Alive"), ngx_string("") },
+    { ngx_null_string, ngx_null_string }
+};
+
+
+static ngx_str_t  ngx_http_proxy_hide_headers[] = {
+    ngx_string("Date"),
+    ngx_string("Server"),
+    ngx_string("X-Pad"),
+    ngx_string("X-Accel-Expires"),
+    ngx_string("X-Accel-Redirect"),
+    ngx_string("X-Accel-Limit-Rate"),
+    ngx_string("X-Accel-Buffer"),
+    ngx_null_string
 };
 
 
@@ -422,16 +427,15 @@ ngx_http_proxy_handler(ngx_http_request_
         u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
     }
 
-    if (plcf->upstream.buffering) {
-
-        u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
-        if (u->pipe == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
+    u->buffering = plcf->upstream.buffering;
+
+    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
+    if (u->pipe == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
+
     u->accel = 1;
 
     r->upstream = u;
@@ -450,10 +454,10 @@ static ngx_int_t
 ngx_http_proxy_create_request(ngx_http_request_t *r)
 {
     size_t                        len, loc_len, body_len;
-    ngx_uint_t                    i, key, unparsed_uri;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
-    ngx_str_t                    *hh, method;
+    ngx_str_t                     method;
+    ngx_uint_t                    i, unparsed_uri;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
     ngx_table_elt_t              *header;
@@ -541,8 +545,6 @@ ngx_http_proxy_create_request(ngx_http_r
     }
 
 
-    hh = (ngx_str_t *) plcf->headers_set_hash->buckets;
-
     if (plcf->upstream.pass_request_headers) {
         part = &r->headers_in.headers.part;
         header = part->elts;
@@ -559,10 +561,8 @@ ngx_http_proxy_create_request(ngx_http_r
                 i = 0;
             }
 
-            key = header[i].hash % plcf->headers_set_hash->hash_size;
-
-            if (hh[key].len == header[i].key.len
-                && ngx_strcasecmp(hh[key].data, header[i].key.data) == 0)
+            if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
+                              header[i].lowcase_key, header[i].key.len))
             {
                 continue;
             }
@@ -676,10 +676,8 @@ ngx_http_proxy_create_request(ngx_http_r
                 i = 0;
             }
 
-            key = header[i].hash % plcf->headers_set_hash->hash_size;
-
-            if (hh[key].len == header[i].key.len
-                && ngx_strcasecmp(hh[key].data, header[i].key.data) == 0)
+            if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
+                              header[i].lowcase_key, header[i].key.len))
             {
                 continue;
             }
@@ -1055,13 +1053,12 @@ static ngx_int_t
 ngx_http_proxy_process_header(ngx_http_request_t *r)
 {
     ngx_int_t                       rc;
-    ngx_uint_t                      key;
+    ngx_uint_t                      i;
     ngx_table_elt_t                *h;
     ngx_http_upstream_header_t     *hh;
     ngx_http_upstream_main_conf_t  *umcf;
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-    hh = (ngx_http_upstream_header_t *) umcf->headers_in_hash.buckets;
 
     for ( ;;  ) {
 
@@ -1082,26 +1079,33 @@ ngx_http_proxy_process_header(ngx_http_r
             h->value.len = r->header_end - r->header_start;
 
             h->key.data = ngx_palloc(r->pool,
-                                     h->key.len + 1 + h->value.len + 1);
+                               h->key.len + 1 + h->value.len + 1 + h->key.len);
             if (h->key.data == NULL) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
             h->value.data = h->key.data + h->key.len + 1;
+            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
 
             ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
             ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
 
-            key = h->hash % umcf->headers_in_hash.hash_size;
-
-            if (hh[key].name.len == h->key.len
-                && ngx_strcasecmp(hh[key].name.data, h->key.data) == 0)
-            {
-                if (hh[key].handler(r, h, hh[key].offset) != NGX_OK) {
-                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            if (h->key.len == r->lowcase_index) {
+                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+            } else {
+                for (i = 0; i < h->key.len; i++) {
+                    h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]);
                 }
             }
 
+            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+                               h->lowcase_key, h->key.len);
+
+            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http proxy header: \"%V: %V\"",
                            &h->key, &h->value);
@@ -1116,6 +1120,42 @@ ngx_http_proxy_process_header(ngx_http_r
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http proxy header done");
 
+            /*
+             * if no "Server" and "Date" in header line,
+             * then add the special empty headers
+             */
+
+            if (r->upstream->headers_in.server == NULL) {
+                h = ngx_list_push(&r->upstream->headers_in.headers);
+                if (h == NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
+                                    ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');
+
+                h->key.len = sizeof("Server") - 1;
+                h->key.data = (u_char *) "Server";
+                h->value.len = 0;
+                h->value.data = NULL;
+                h->lowcase_key = (u_char *) "server";
+            }
+
+            if (r->upstream->headers_in.date == NULL) {
+                h = ngx_list_push(&r->upstream->headers_in.headers);
+                if (h == NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');
+
+                h->key.len = sizeof("Date") - 1;
+                h->key.data = (u_char *) "Date";
+                h->value.len = 0;
+                h->value.data = NULL;
+                h->lowcase_key = (u_char *) "date";
+            }
+
             return NGX_OK;
         }
 
@@ -1406,6 +1446,9 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *     conf->upstream.bufs.num = 0;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
+     *     conf->upstream.hide_headers_hash = { NULL, 0 };
+     *     conf->upstream.hide_headers = NULL;
+     *     conf->upstream.pass_headers = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1446,12 +1489,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
     /* "proxy_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
-    conf->upstream.pass_x_powered_by = NGX_CONF_UNSET;
-    conf->upstream.pass_server = NGX_CONF_UNSET;
-    conf->upstream.pass_date = 0;
-    conf->upstream.pass_x_accel_expires = NGX_CONF_UNSET;
-
     conf->redirect = NGX_CONF_UNSET;
+    conf->upstream.change_buffering = 1;
 
     return conf;
 }
@@ -1466,9 +1505,12 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     u_char                       *p;
     size_t                        size;
     uintptr_t                    *code;
-    ngx_str_t                    *name;
-    ngx_uint_t                    i;
-    ngx_table_elt_t              *src, *s, *h;
+    ngx_str_t                    *header;
+    ngx_uint_t                    i, j;
+    ngx_array_t                   hide_headers;
+    ngx_keyval_t                 *src, *s, *h;
+    ngx_hash_key_t               *hk;
+    ngx_hash_init_t               hash;
     ngx_http_proxy_redirect_t    *pr;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
@@ -1627,14 +1669,6 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     ngx_conf_merge_value(conf->upstream.redirect_errors,
                               prev->upstream.redirect_errors, 0);
 
-    ngx_conf_merge_value(conf->upstream.pass_x_powered_by,
-                              prev->upstream.pass_x_powered_by, 1);
-    ngx_conf_merge_value(conf->upstream.pass_server,
-                              prev->upstream.pass_server, 0);
-    ngx_conf_merge_value(conf->upstream.pass_x_accel_expires,
-                              prev->upstream.pass_x_accel_expires, 0);
-
-
     ngx_conf_merge_value(conf->redirect, prev->redirect, 1);
 
     if (conf->redirect) {
@@ -1662,6 +1696,106 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         }
     }
 
+    if (conf->upstream.hide_headers == NULL
+        && conf->upstream.pass_headers == NULL)
+    {
+        conf->upstream.hide_headers = prev->upstream.hide_headers;
+        conf->upstream.pass_headers = prev->upstream.pass_headers;
+        conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash;
+
+        if (conf->upstream.hide_headers_hash.buckets) {
+            goto peers;
+        }
+
+    } else {
+        if (conf->upstream.hide_headers == NULL) {
+            conf->upstream.hide_headers = prev->upstream.hide_headers;
+        }
+
+        if (conf->upstream.pass_headers == NULL) {
+            conf->upstream.pass_headers = prev->upstream.pass_headers;
+        }
+    }
+
+    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    for (header = ngx_http_proxy_hide_headers; header->len; header++) {
+        hk = ngx_array_push(&hide_headers);
+        if (hk == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        hk->key = *header;
+        hk->key_hash = ngx_hash_key_lc(header->data, header->len);
+        hk->value = (void *) 1;
+    }
+
+    if (conf->upstream.hide_headers) {
+
+        header = conf->upstream.hide_headers->elts;
+
+        for (i = 0; i < conf->upstream.hide_headers->nelts; i++) {
+
+            hk = hide_headers.elts;
+
+            for (j = 0; j < hide_headers.nelts; j++) {
+                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
+                    goto exist;
+                }
+            }
+
+            hk = ngx_array_push(&hide_headers);
+            if (hk == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            hk->key = header[i];
+            hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len);
+            hk->value = (void *) 1;
+
+        exist:
+
+            continue;
+        }
+    }
+
+    if (conf->upstream.pass_headers) {
+
+        hk = hide_headers.elts;
+        header = conf->upstream.pass_headers->elts;
+
+        for (i = 0; i < conf->upstream.pass_headers->nelts; i++) {
+            for (j = 0; j < hide_headers.nelts; j++) {
+
+                if (hk[j].key.data == NULL) {
+                    continue;
+                }
+
+                if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) {
+                    hk[j].key.data = NULL;
+                    break;
+                }
+            }
+        }
+    }
+
+    hash.hash = &conf->upstream.hide_headers_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "proxy_hide_headers_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+peers:
 
     if (conf->peers == NULL) {
         conf->peers = prev->peers;
@@ -1696,7 +1830,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
         if (conf->headers_source == NULL) {
             conf->headers_source = ngx_array_create(cf->pool, 4,
-                                                    sizeof(ngx_table_elt_t));
+                                                    sizeof(ngx_keyval_t));
             if (conf->headers_source == NULL) {
                 return NGX_CONF_ERROR;
             }
@@ -1707,7 +1841,6 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
             return NGX_CONF_ERROR;
         }
 
-        s->hash = 0;
         s->key.len = sizeof("Content-Length") - 1;
         s->key.data = (u_char *) "Content-Length";
         s->value.len = sizeof("$proxy_internal_body_length") - 1;
@@ -1723,19 +1856,19 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
         conf->headers_source = prev->headers_source;
     }
 
-    if (conf->headers_set_hash) {
+    if (conf->headers_set_hash.buckets) {
         return NGX_CONF_OK;
     }
 
 
-    conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
+    conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t));
     if (conf->headers_names == NULL) {
         return NGX_CONF_ERROR;
     }
 
     if (conf->headers_source == NULL) {
         conf->headers_source = ngx_array_create(cf->pool, 4,
-                                                sizeof(ngx_table_elt_t));
+                                                sizeof(ngx_keyval_t));
         if (conf->headers_source == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -1780,12 +1913,14 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     src = conf->headers_source->elts;
     for (i = 0; i < conf->headers_source->nelts; i++) {
 
-        name = ngx_array_push(conf->headers_names);
-        if (name == NULL) {
+        hk = ngx_array_push(conf->headers_names);
+        if (hk == NULL) {
             return NGX_CONF_ERROR;
         }
 
-        *name = src[i].key;
+        hk->key = src[i].key;
+        hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);
+        hk->value = (void *) 1;
 
         if (src[i].value.len == 0) {
             continue;
@@ -1918,6 +2053,23 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     *code = (uintptr_t) NULL;
 
 
+    hash.hash = &conf->headers_set_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "proxy_set_header_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, conf->headers_names->elts,
+                      conf->headers_names->nelts)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+
+#if 0
     conf->headers_set_hash = ngx_pcalloc(cf->pool, sizeof(ngx_hash0_t));
     if (conf->headers_set_hash == NULL) {
         return NGX_CONF_ERROR;
@@ -1940,6 +2092,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
                    "max buckets per entry: %ui",
                    conf->headers_set_hash->hash_size,
                    conf->headers_set_hash->min_buckets);
+#endif
 
     return NGX_CONF_OK;
 }
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -51,6 +51,8 @@ static char *ngx_http_perl_init_interpre
 static PerlInterpreter *
     ngx_http_perl_create_interpreter(ngx_http_perl_main_conf_t *pmcf,
     ngx_log_t *log);
+static ngx_int_t ngx_http_perl_run_requires(ngx_array_t *requires,
+    ngx_log_t *log);
 static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
     SV *sub, ngx_str_t **args, ngx_str_t *handler, ngx_str_t *rv);
 static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
@@ -67,7 +69,10 @@ static char *ngx_http_perl(ngx_conf_t *c
 static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_perl_interp_max_unsupported(ngx_conf_t *cf, void *post,
     void *data);
+#if (NGX_HAVE_PERL_CLONE || NGX_HAVE_PERL_MULTIPLICITY)
 static void ngx_http_perl_cleanup_perl(void *data);
+#endif
+static void ngx_http_perl_cleanup_sv(void *data);
 
 
 static ngx_conf_post_handler_pt  ngx_http_perl_interp_max_p =
@@ -394,22 +399,6 @@ ngx_http_perl_ssi(ngx_http_request_t *r,
 
     dTHXa(ctx->perl);
 
-#if 0
-
-    ngx_http_perl_eval_anon_sub(aTHX_ handler, &sv);
-
-    if (sv == &PL_sv_undef) {
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "eval_pv(\"%V\") failed", handler);
-        return NGX_ERROR;
-    }
-
-    if (sv == NULL) {
-        sv = newSVpvn((char *) handler->data, handler->len);
-    }
-
-#endif
-
     sv = newSVpvn((char *) handler->data, handler->len);
 
     rc = ngx_http_perl_call_handler(aTHX_ r, sv, &params[NGX_HTTP_PERL_SSI_ARG],
@@ -470,13 +459,18 @@ ngx_http_perl_free_interpreter(ngx_http_
 static char *
 ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)
 {
-    ngx_pool_cleanup_t         *cln;
+#if (NGX_HAVE_PERL_CLONE || NGX_HAVE_PERL_MULTIPLICITY)
+    ngx_pool_cleanup_t  *cln;
 
     cln = ngx_pool_cleanup_add(cf->pool, 0);
     if (cln == NULL) {
         return NGX_CONF_ERROR;
     }
 
+#else
+    static PerlInterpreter  *perl;
+#endif
+
 #ifdef NGX_PERL_MODULES
     if (pmcf->modules.data == NULL) {
         pmcf->modules.data = NGX_PERL_MODULES;
@@ -489,6 +483,20 @@ ngx_http_perl_init_interpreter(ngx_conf_
         }
     }
 
+#if !(NGX_HAVE_PERL_CLONE || NGX_HAVE_PERL_MULTIPLICITY)
+
+    if (perl) {
+        if (ngx_http_perl_run_requires(&pmcf->requires, cf->log) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        pmcf->perl = perl;
+
+        return NGX_CONF_OK;
+    }
+
+#endif
+
     PERL_SYS_INIT(&ngx_argc, &ngx_argv);
 
     pmcf->perl = ngx_http_perl_create_interpreter(pmcf, cf->log);
@@ -498,9 +506,17 @@ ngx_http_perl_init_interpreter(ngx_conf_
         return NGX_CONF_ERROR;
     }
 
+#if (NGX_HAVE_PERL_CLONE || NGX_HAVE_PERL_MULTIPLICITY)
+
     cln->handler = ngx_http_perl_cleanup_perl;
     cln->data = pmcf->perl;
 
+#else
+
+    perl = pmcf->perl;
+
+#endif
+
     return NGX_CONF_OK;
 }
 
@@ -511,10 +527,6 @@ ngx_http_perl_create_interpreter(ngx_htt
 {
     int                n;
     char              *embedding[6];
-    char             **script;
-    STRLEN             len;
-    ngx_str_t          err;
-    ngx_uint_t         i;
     PerlInterpreter   *perl;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "create perl interpreter");
@@ -583,22 +595,8 @@ ngx_http_perl_create_interpreter(ngx_htt
         goto fail;
     }
 
-    script = pmcf->requires.elts;
-    for (i = 0; i < pmcf->requires.nelts; i++) {
-        require_pv(script[i]);
-
-        if (SvTRUE(ERRSV)) {
-
-            err.data = (u_char *) SvPV(ERRSV, len);
-            for (len--; err.data[len] == LF || err.data[len] == CR; len--) {
-                /* void */
-            }
-            err.len = len + 1;
-
-            ngx_log_error(NGX_LOG_EMERG, log, 0,
-                          "require_pv(\"%s\") failed: \"%V\"", script[i], &err);
-            goto fail;
-        }
+    if (ngx_http_perl_run_requires(&pmcf->requires, log) != NGX_OK) {
+        goto fail;
     }
 
     }
@@ -617,6 +615,38 @@ fail:
 }
 
 
+static ngx_int_t
+ngx_http_perl_run_requires(ngx_array_t *requires, ngx_log_t *log)
+{
+    char       **script;
+    STRLEN       len;
+    ngx_str_t    err;
+    ngx_uint_t   i;
+
+    script = requires->elts;
+    for (i = 0; i < requires->nelts; i++) {
+
+        require_pv(script[i]);
+
+        if (SvTRUE(ERRSV)) {
+
+            err.data = (u_char *) SvPV(ERRSV, len);
+            for (len--; err.data[len] == LF || err.data[len] == CR; len--) {
+                /* void */
+            }
+            err.len = len + 1;
+
+            ngx_log_error(NGX_LOG_EMERG, log, 0,
+                          "require_pv(\"%s\") failed: \"%V\"", script[i], &err);
+
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 #if (__INTEL_COMPILER)
 /*
  * disable 'declaration hides parameter "my_perl"' warning for ENTER and LEAVE
@@ -740,11 +770,8 @@ ngx_http_perl_eval_anon_sub(pTHX_ ngx_st
         }
     }
 
-    if (ngx_strncmp(p, "sub ", 4) == 0
-        || ngx_strncmp(p, "use ", 4) == 0)
-    {
+    if (ngx_strncmp(p, "sub ", 4) == 0 || ngx_strncmp(p, "use ", 4) == 0) {
         *sv = eval_pv((char *) p, FALSE);
-
         return;
     }
 
@@ -805,10 +832,12 @@ ngx_http_perl_init_main_conf(ngx_conf_t 
 }
 
 
+#if (NGX_HAVE_PERL_CLONE || NGX_HAVE_PERL_MULTIPLICITY)
+
 static void
 ngx_http_perl_cleanup_perl(void *data)
 {
-    PerlInterpreter *perl = data;
+    PerlInterpreter  *perl = data;
 
     (void) perl_destruct(perl);
 
@@ -817,6 +846,17 @@ ngx_http_perl_cleanup_perl(void *data)
     PERL_SYS_TERM();
 }
 
+#endif
+
+
+static void
+ngx_http_perl_cleanup_sv(void *data)
+{
+    SV  *sv = data;
+
+    SvREFCNT_dec(sv);
+}
+
 
 static ngx_int_t
 ngx_http_perl_preconfiguration(ngx_conf_t *cf)
@@ -908,6 +948,7 @@ ngx_http_perl(ngx_conf_t *cf, ngx_comman
     ngx_http_perl_loc_conf_t *plcf = conf;
 
     ngx_str_t                  *value;
+    ngx_pool_cleanup_t         *cln;
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_perl_main_conf_t  *pmcf;
 
@@ -927,6 +968,11 @@ ngx_http_perl(ngx_conf_t *cf, ngx_comman
         }
     }
 
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
     plcf->handler = value[1];
 
     {
@@ -947,6 +993,9 @@ ngx_http_perl(ngx_conf_t *cf, ngx_comman
 
     }
 
+    cln->handler = ngx_http_perl_cleanup_sv;
+    cln->data = plcf->sub;
+
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
     clcf->handler = ngx_http_perl_handler;
 
@@ -959,6 +1008,7 @@ ngx_http_perl_set(ngx_conf_t *cf, ngx_co
 {
     ngx_int_t                   index;
     ngx_str_t                  *value;
+    ngx_pool_cleanup_t         *cln;
     ngx_http_variable_t        *v;
     ngx_http_perl_variable_t   *pv;
     ngx_http_perl_main_conf_t  *pmcf;
@@ -997,6 +1047,11 @@ ngx_http_perl_set(ngx_conf_t *cf, ngx_co
         }
     }
 
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
     pv->handler = value[2];
 
     {
@@ -1017,6 +1072,9 @@ ngx_http_perl_set(ngx_conf_t *cf, ngx_co
 
     }
 
+    cln->handler = ngx_http_perl_cleanup_sv;
+    cln->data = pv->sub;
+
     v->get_handler = ngx_http_perl_variable;
     v->data = (uintptr_t) pv;
 
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -79,11 +79,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     ngx_uint_t                   mi, m, s, l, p, a, i;
     ngx_uint_t                   last, bind_all, done;
     ngx_conf_t                   pcf;
-    ngx_array_t                  in_ports;
+    ngx_array_t                  headers_in, in_ports;
+    ngx_hash_key_t              *hk;
     ngx_hash_init_t              hash;
     ngx_listening_t             *ls;
     ngx_http_listen_t           *lscf;
     ngx_http_module_t           *module;
+    ngx_http_header_t           *header;
     ngx_http_in_port_t          *hip;
     ngx_http_handler_pt         *h;
     ngx_http_conf_ctx_t         *ctx;
@@ -373,21 +375,35 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     cmcf->phases[NGX_HTTP_LOG_PHASE].type = NGX_OK;
 
 
-    cmcf->headers_in_hash.max_size = 200;
-    cmcf->headers_in_hash.bucket_limit = 1;
-    cmcf->headers_in_hash.bucket_size = sizeof(ngx_http_header_t);
-    cmcf->headers_in_hash.name = "http headers_in";
-
-    if (ngx_hash0_init(&cmcf->headers_in_hash, cf->pool, ngx_http_headers_in, 0)
+    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                   "http headers_in hash size: %ui, max buckets per entry: %ui",
-                   cmcf->headers_in_hash.hash_size,
-                   cmcf->headers_in_hash.min_buckets);
+    for (header = ngx_http_headers_in; header->name.len; header++) {
+        hk = ngx_array_push(&headers_in);
+        if (hk == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        hk->key = header->name;
+        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+        hk->value = header;
+    }
+
+    hash.hash = &cmcf->headers_in_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "headers_in_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
 
     for (m = 0; ngx_modules[m]; m++) {
         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -921,42 +921,43 @@ ngx_http_set_content_type(ngx_http_reque
 {
     u_char                     c, *p, *exten;
     ngx_str_t                 *type;
-    ngx_uint_t                 i;
+    ngx_uint_t                 i, hash;
     ngx_http_core_loc_conf_t  *clcf;
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     if (r->exten.len) {
 
-        if (!r->low_case_exten) {
-            for (i = 0; i < r->exten.len; i++) {
-                c = r->exten.data[i];
-                if (c >= 'A' && c <= 'Z') {
-                    break;
-                }
-            }
-
-            if (i < r->exten.len) {
+        hash = 0;
+
+        for (i = 0; i < r->exten.len; i++) {
+            c = r->exten.data[i];
+
+            if (c >= 'A' && c <= 'Z') {
+
                 p = ngx_palloc(r->pool, r->exten.len);
                 if (p == NULL) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
+                hash = 0;
                 exten = p;
 
                 for (i = 0; i < r->exten.len; i++) {
-                    c = r->exten.data[i];
-                    *p++ = ngx_tolower(c);
+                    c = ngx_tolower(r->exten.data[i]);
+                    hash = ngx_hash(hash, c);
+                    *p++ = c;
                 }
 
                 r->exten.data = exten;
+
+                break;
             }
 
-            r->low_case_exten = 1;
+            hash = ngx_hash(hash, c);
         }
 
-        type = ngx_hash_find(&clcf->types_hash,
-                             ngx_hash_key(r->exten.data, r->exten.len),
+        type = ngx_hash_find(&clcf->types_hash, hash,
                              r->exten.data, r->exten.len);
 
         if (type) {
@@ -981,17 +982,9 @@ ngx_http_set_exten(ngx_http_request_t *r
 
     for (i = r->uri.len - 1; i > 1; i--) {
         if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') {
+
             r->exten.len = r->uri.len - i - 1;
-
-            if (r->exten.len > 0) {
-                r->exten.data = ngx_palloc(r->pool, r->exten.len + 1);
-                if (r->exten.data == NULL) {
-                    return NGX_ERROR;
-                }
-
-                ngx_cpystrn(r->exten.data, &r->uri.data[i + 1],
-                            r->exten.len + 1);
-            }
+            r->exten.data = &r->uri.data[i + 1];
 
             break;
 
@@ -1000,8 +993,6 @@ ngx_http_set_exten(ngx_http_request_t *r
         }
     }
 
-    r->low_case_exten = 0;
-
     return NGX_OK;
 }
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -72,7 +72,7 @@ typedef struct {
 
     ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
 
-    ngx_hash0_t                headers_in_hash;
+    ngx_hash_t                 headers_in_hash;
 
     ngx_hash_t                 variables_hash;
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -9,6 +9,8 @@
 #include <ngx_http.h>
 
 
+/* gcc, icc, msvc and others compile these switches as an jump table */
+
 ngx_int_t
 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
 {
@@ -43,8 +45,6 @@ ngx_http_parse_request_line(ngx_http_req
     for (p = b->pos; p < b->last; p++) {
         ch = *p;
 
-        /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */
-
         switch (state) {
 
         /* HTTP methods: GET, HEAD, POST */
@@ -528,7 +528,7 @@ ngx_int_t
 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
 {
     u_char      c, ch, *p;
-    ngx_uint_t  hash;
+    ngx_uint_t  hash, i;
     enum {
         sw_start = 0,
         sw_name,
@@ -540,8 +540,19 @@ ngx_http_parse_header_line(ngx_http_requ
         sw_header_almost_done
     } state;
 
+    static u_char  lowcase[] =
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
+        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
     state = r->state;
     hash = r->header_hash;
+    i = r->lowcase_index;
 
     for (p = b->pos; p < b->last; p++) {
         ch = *p;
@@ -564,14 +575,12 @@ ngx_http_parse_header_line(ngx_http_requ
                 state = sw_name;
                 r->header_name_start = p;
 
-                c = (u_char) (ch | 0x20);
-                if (c >= 'a' && c <= 'z') {
-                    hash = c;
-                    break;
-                }
+                c = lowcase[ch];
 
-                if (ch >= '0' && ch <= '9') {
-                    hash = ch;
+                if (c) {
+                    hash = ngx_hash(0, c);
+                    r->lowcase_header[0] = c;
+                    i = 1;
                     break;
                 }
 
@@ -584,9 +593,12 @@ ngx_http_parse_header_line(ngx_http_requ
 
         /* header name */
         case sw_name:
-            c = (u_char) (ch | 0x20);
-            if (c >= 'a' && c <= 'z') {
-                hash += c;
+            c = lowcase[ch];
+
+            if (c) {
+                hash = ngx_hash(hash, c);
+                r->lowcase_header[i++] = c;
+                i &= ~NGX_HTTP_LC_HEADER_LEN;
                 break;
             }
 
@@ -596,16 +608,6 @@ ngx_http_parse_header_line(ngx_http_requ
                 break;
             }
 
-            if (ch == '-') {
-                hash += ch;
-                break;
-            }
-
-            if (ch >= '0' && ch <= '9') {
-                hash += ch;
-                break;
-            }
-
             if (ch == CR) {
                 r->header_name_end = p;
                 r->header_start = p;
@@ -726,6 +728,7 @@ ngx_http_parse_header_line(ngx_http_requ
     b->pos = p;
     r->state = state;
     r->header_hash = hash;
+    r->lowcase_index = i;
 
     return NGX_AGAIN;
 
@@ -734,6 +737,7 @@ done:
     b->pos = p + 1;
     r->state = sw_start;
     r->header_hash = hash;
+    r->lowcase_index = i;
 
     return NGX_OK;
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -25,7 +25,8 @@ static ngx_int_t ngx_http_process_cookie
     ngx_table_elt_t *h, ngx_uint_t offset);
 
 static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
-static void ngx_http_find_virtual_server(ngx_http_request_t *r);
+static void ngx_http_find_virtual_server(ngx_http_request_t *r,
+    ngx_http_virtual_names_t *vn, ngx_uint_t hash);
 
 static void ngx_http_request_handler(ngx_event_t *ev);
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
@@ -739,8 +740,8 @@ ngx_http_process_request_headers(ngx_eve
 {
     ssize_t                     n;
     ngx_int_t                   rc, rv;
-    ngx_uint_t                  key;
     ngx_str_t                   header;
+    ngx_uint_t                  i;
     ngx_table_elt_t            *h;
     ngx_connection_t           *c;
     ngx_http_header_t          *hh;
@@ -763,7 +764,6 @@ ngx_http_process_request_headers(ngx_eve
 
     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-    hh = (ngx_http_header_t *) cmcf->headers_in_hash.buckets;
 
     rc = NGX_AGAIN;
 
@@ -841,16 +841,28 @@ ngx_http_process_request_headers(ngx_eve
             h->value.data = r->header_start;
             h->value.data[h->value.len] = '\0';
 
-            key = h->hash % cmcf->headers_in_hash.hash_size;
-
-            if (hh[key].name.len == h->key.len
-                && ngx_strcasecmp(hh[key].name.data, h->key.data) == 0)
-            {
-                if (hh[key].handler(r, h, hh[key].offset) != NGX_OK) {
-                    return;
+            h->lowcase_key = ngx_palloc(r->pool, h->key.len);
+            if (h->lowcase_key == NULL) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            if (h->key.len == r->lowcase_index) {
+                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+            } else {
+                for (i = 0; i < h->key.len; i++) {
+                    h->lowcase_key[i] = ngx_tolower(h->lowcase_key[i]);
                 }
             }
 
+            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
+                               h->lowcase_key, h->key.len);
+
+            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+                return;
+            }
+
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http header: \"%V: %V\"",
                            &h->key, &h->value);
@@ -1174,12 +1186,16 @@ ngx_http_process_request_header(ngx_http
 {
     size_t                    len;
     u_char                   *ua, *user_agent, ch;
+    ngx_uint_t                hash;
 #if (NGX_HTTP_SSL)
     long                      rc;
     ngx_http_ssl_srv_conf_t  *sscf;
 #endif
 
     if (r->headers_in.host) {
+
+        hash = 0;
+
         for (len = 0; len < r->headers_in.host->value.len; len++) {
             ch = r->headers_in.host->value.data[len];
 
@@ -1187,16 +1203,21 @@ ngx_http_process_request_header(ngx_http
                 break;
             }
 
-            r->headers_in.host->value.data[len] = ngx_tolower(ch);
+            ch = ngx_tolower(ch);
+            r->headers_in.host->value.data[len] = ch;
+            hash = ngx_hash(hash, ch);
         }
 
-        if (r->headers_in.host->value.data[len - 1] == '.') {
+        if (len && r->headers_in.host->value.data[len - 1] == '.') {
             len--;
+            hash = ngx_hash_key(r->headers_in.host->value.data, len);
         }
 
         r->headers_in.host_name_len = len;
 
-        ngx_http_find_virtual_server(r);
+        if (r->virtual_names) {
+            ngx_http_find_virtual_server(r, r->virtual_names, hash);
+        }
 
     } else {
         if (r->http_version > NGX_HTTP_VERSION_10) {
@@ -1345,27 +1366,19 @@ ngx_http_process_request_header(ngx_http
 
 
 static void
-ngx_http_find_virtual_server(ngx_http_request_t *r)
+ngx_http_find_virtual_server(ngx_http_request_t *r,
+    ngx_http_virtual_names_t *vn, ngx_uint_t hash)
 {
     size_t                     len;
     u_char                    *host;
-    ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
 
-    vn = r->virtual_names;
-
-    if (vn == NULL) {
-        return;
-    }
-
     host = r->headers_in.host->value.data;
     len = r->headers_in.host_name_len;
 
-    /* STUB: ngx_hash_key() here is STUB */
-
     if (vn->hash.buckets) {
-        cscf = ngx_hash_find(&vn->hash, ngx_hash_key(host, len), host, len);
+        cscf = ngx_hash_find(&vn->hash, hash, host, len);
         if (cscf) {
             goto found;
         }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -10,6 +10,9 @@
 
 #define NGX_HTTP_MAX_REWRITE_CYCLES        10
 
+/* must be 2^n */
+#define NGX_HTTP_LC_HEADER_LEN             32
+
 
 #define NGX_HTTP_DISCARD_BUFFER_SIZE       4096
 #define NGX_HTTP_LINGERING_BUFFER_SIZE     4096
@@ -400,7 +403,6 @@ struct ngx_http_request_s {
 
     unsigned                          fast_subrequest:1;
 
-    unsigned                          low_case_exten:1;
     unsigned                          header_timeout_set:1;
 
     unsigned                          proxy:1;
@@ -463,7 +465,10 @@ struct ngx_http_request_s {
     u_char                           *header_name_end;
     u_char                           *header_start;
     u_char                           *header_end;
+
     ngx_uint_t                        header_hash;
+    ngx_uint_t                        lowcase_index;
+    u_char                            lowcase_header[NGX_HTTP_LC_HEADER_LEN];
 };
 
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -47,10 +47,9 @@ static ngx_int_t ngx_http_upstream_ignor
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
-static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
+static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
-static ngx_int_t
-    ngx_http_upstream_conditional_copy_header_line(ngx_http_request_t *r,
+static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t
     ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
@@ -103,7 +102,7 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("Status"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, status),
-                 /* STUB */ ngx_http_upstream_ignore_header_line, 0, 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("Content-Type"),
                  ngx_http_upstream_process_header_line,
@@ -118,14 +117,14 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("Date"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, date),
-                 ngx_http_upstream_conditional_copy_header_line,
-                 offsetof(ngx_http_upstream_conf_t, pass_date), 0 },
+                 ngx_http_upstream_copy_header_line,
+                 offsetof(ngx_http_headers_out_t, date), 0 },
 
     { ngx_string("Server"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, server),
-                 ngx_http_upstream_conditional_copy_header_line,
-                 offsetof(ngx_http_upstream_conf_t, pass_server), 0 },
+                 ngx_http_upstream_copy_header_line,
+                 offsetof(ngx_http_headers_out_t, server), 0 },
 
     { ngx_string("WWW-Authenticate"),
                  ngx_http_upstream_process_header_line,
@@ -174,20 +173,14 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_ignore_header_line, 0,
                  ngx_http_upstream_ignore_header_line, 0, 0 },
 
-    { ngx_string("X-Pad"),
-                 ngx_http_upstream_ignore_header_line, 0,
-                 ngx_http_upstream_ignore_header_line, 0, 0 },
-
     { ngx_string("X-Powered-By"),
                  ngx_http_upstream_ignore_header_line, 0,
-                 ngx_http_upstream_conditional_copy_header_line,
-                 offsetof(ngx_http_upstream_conf_t, pass_x_powered_by), 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Expires"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, x_accel_expires),
-                 ngx_http_upstream_conditional_copy_header_line,
-                 offsetof(ngx_http_upstream_conf_t, pass_x_accel_expires), 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Redirect"),
                  ngx_http_upstream_process_header_line,
@@ -198,6 +191,10 @@ ngx_http_upstream_header_t  ngx_http_ups
                  ngx_http_upstream_process_limit_rate, 0,
                  ngx_http_upstream_ignore_header_line, 0, 0 },
 
+    { ngx_string("X-Accel-Buffering"),
+                 ngx_http_upstream_process_buffering, 0,
+                 ngx_http_upstream_ignore_header_line, 0, 0 },
+
 #if (NGX_HTTP_GZIP)
     { ngx_string("Content-Encoding"),
                  ngx_http_upstream_process_header_line,
@@ -881,7 +878,7 @@ ngx_http_upstream_process_header(ngx_eve
     ssize_t                         n;
     ngx_int_t                       rc;
     ngx_str_t                      *uri, args;
-    ngx_uint_t                      i, key, flags;
+    ngx_uint_t                      i, flags;
     ngx_list_part_t                *part;
     ngx_table_elt_t                *h;
     ngx_connection_t               *c;
@@ -1089,7 +1086,6 @@ ngx_http_upstream_process_header(ngx_eve
         ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
 
         umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-        hh = (ngx_http_upstream_header_t *) umcf->headers_in_hash.buckets;
 
         part = &r->upstream->headers_in.headers.part;
         h = part->elts;
@@ -1106,18 +1102,15 @@ ngx_http_upstream_process_header(ngx_eve
                 i = 0;
             }
 
-            key = h[i].hash % umcf->headers_in_hash.hash_size;
-
-            if (hh[key].redirect
-                && hh[key].name.len == h[i].key.len
-                && ngx_strcasecmp(hh[key].name.data, h[i].key.data) == 0)
-            {
-                if (hh[key].copy_handler(r, &h[i], hh[key].conf) != NGX_OK) {
+            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                               h[i].lowcase_key, h[i].key.len);
+
+            if (hh && hh->redirect) {
+                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
                     ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
                     return;
                 }
-
             }
         }
 
@@ -1149,7 +1142,7 @@ ngx_http_upstream_send_response(ngx_http
     int                             tcp_nodelay;
     ssize_t                         size;
     ngx_int_t                       rc;
-    ngx_uint_t                      i, key;
+    ngx_uint_t                      i;
     ngx_list_part_t                *part;
     ngx_table_elt_t                *h;
     ngx_event_pipe_t               *p;
@@ -1161,7 +1154,6 @@ ngx_http_upstream_send_response(ngx_http
     ngx_http_upstream_main_conf_t  *umcf;
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
-    hh = (ngx_http_upstream_header_t *) umcf->headers_in_hash.buckets;
 
     part = &r->upstream->headers_in.headers.part;
     h = part->elts;
@@ -1178,12 +1170,17 @@ ngx_http_upstream_send_response(ngx_http
             i = 0;
         }
 
-        key = h[i].hash % umcf->headers_in_hash.hash_size;
-
-        if (hh[key].name.len == h[i].key.len
-            && ngx_strcasecmp(hh[key].name.data, h[i].key.data) == 0)
+        if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+                          h[i].lowcase_key, h[i].key.len))
         {
-            if (hh[key].copy_handler(r, &h[i], hh[key].conf) != NGX_OK) {
+            continue;
+        }
+
+        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+                           h[i].lowcase_key, h[i].key.len);
+
+        if (hh) {
+            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
                 ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
                 return;
@@ -1199,6 +1196,14 @@ ngx_http_upstream_send_response(ngx_http
         }
     }
 
+    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
+        r->headers_out.server->hash = 0;
+    }
+
+    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
+        r->headers_out.date->hash = 0;
+    }
+
     r->headers_out.status = u->headers_in.status_n;
     r->headers_out.status_line = u->headers_in.status_line;
 
@@ -1236,7 +1241,7 @@ ngx_http_upstream_send_response(ngx_http
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    if (u->pipe == NULL) {
+    if (!u->buffering) {
 
         if (u->input_filter == NULL) {
             u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
@@ -2054,6 +2059,37 @@ ngx_http_upstream_process_limit_rate(ngx
 
 
 static ngx_int_t
+ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    u_char  c0, c1, c2;
+
+    if (r->upstream->conf->change_buffering) {
+
+        if (h->value.len == 2) {
+            c0 = ngx_tolower(h->value.data[0]);
+            c1 = ngx_tolower(h->value.data[1]);
+
+            if (c0 == 'n' && c1 == 'o') {
+                r->upstream->buffering = 0;
+            }
+
+        } else if (h->value.len == 3) {
+            c0 = ngx_tolower(h->value.data[0]);
+            c1 = ngx_tolower(h->value.data[1]);
+            c2 = ngx_tolower(h->value.data[2]);
+
+            if (c0 == 'y' && c1 == 'e' && c2 == 's') {
+                r->upstream->buffering = 1;
+            }
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
@@ -2076,30 +2112,6 @@ ngx_http_upstream_copy_header_line(ngx_h
 
 
 static ngx_int_t
-ngx_http_upstream_conditional_copy_header_line(ngx_http_request_t *r,
-    ngx_table_elt_t *h, ngx_uint_t offset)
-{
-    ngx_flag_t       *f;
-    ngx_table_elt_t  *ho;
-
-    f = (ngx_flag_t *) ((char *) r->upstream->conf + offset);
-
-    if (*f == 0) {
-        return NGX_OK;
-    }
-
-    ho = ngx_list_push(&r->headers_out.headers);
-    if (ho == NULL) {
-        return NGX_ERROR;
-    }
-
-    *ho = *h;
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
 ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset)
 {
@@ -2554,22 +2566,39 @@ ngx_http_core_init_main_conf(ngx_conf_t 
 {
     ngx_http_upstream_main_conf_t  *umcf = conf;
 
-    umcf->headers_in_hash.max_size = 100;
-    umcf->headers_in_hash.bucket_limit = 1;
-    umcf->headers_in_hash.bucket_size = sizeof(ngx_http_upstream_header_t);
-    umcf->headers_in_hash.name = "upstream_headers_in";
-
-    if (ngx_hash0_init(&umcf->headers_in_hash, cf->pool,
-                      ngx_http_upstream_headers_in, 0) != NGX_OK)
+    ngx_array_t                  headers_in;
+    ngx_hash_key_t              *hk;
+    ngx_hash_init_t              hash;
+    ngx_http_upstream_header_t  *header;
+
+    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
+        != NGX_OK)
     {
         return NGX_CONF_ERROR;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0,
-                   "http upstream headers_in hash size: %ui, "
-                   "max buckets per entry: %ui",
-                   umcf->headers_in_hash.hash_size,
-                   umcf->headers_in_hash.min_buckets);
+    for (header = ngx_http_upstream_headers_in; header->name.len; header++) {
+        hk = ngx_array_push(&headers_in);
+        if (hk == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        hk->key = header->name;
+        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+        hk->value = header;
+    }
+
+    hash.hash = &umcf->headers_in_hash;
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = 512;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "upstream_headers_in_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
     return NGX_CONF_OK;
 }
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -41,7 +41,7 @@ typedef struct {
 
 
 typedef struct {
-    ngx_hash0_t                     headers_in_hash;
+    ngx_hash_t                      headers_in_hash;
 } ngx_http_upstream_main_conf_t;
 
 
@@ -77,19 +77,19 @@ typedef struct {
     ngx_flag_t                      redirect_errors;
     ngx_flag_t                      cyclic_temp_file;
 
-    ngx_flag_t                      pass_x_powered_by;
-    ngx_flag_t                      pass_server;
-    ngx_flag_t                      pass_date;
-    ngx_flag_t                      pass_x_accel_expires;
+    ngx_path_t                     *temp_path;
 
-    ngx_path_t                     *temp_path;
+    ngx_hash_t                      hide_headers_hash;
+    ngx_array_t                    *hide_headers;
+    ngx_array_t                    *pass_headers;
 
     ngx_str_t                       schema;
     ngx_str_t                       uri;
     ngx_str_t                       location;
     ngx_str_t                       url;  /* used in proxy_rewrite_location */
 
-    ngx_uint_t                      redirect_404; /* unsigned redirect_404:1; */
+    unsigned                        redirect_404:1;
+    unsigned                        change_buffering:1;
 
 #if (NGX_HTTP_SSL)
     ngx_ssl_t                      *ssl;
@@ -191,6 +191,8 @@ struct ngx_http_upstream_s {
     unsigned                        cachable:1;
     unsigned                        accel:1;
 
+    unsigned                        buffering:1;
+
     unsigned                        request_sent:1;
     unsigned                        header_sent:1;
 };