# HG changeset patch # User Igor Sysoev # Date 1147291200 -14400 # Node ID e6da4931e0e0ac9a09a9eee4fc05f6f4e7b86d79 # Parent 93658b91fad265beda784ebc8261f49507ef32de 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. diff --git a/CHANGES b/CHANGES --- 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 diff --git a/CHANGES.ru b/CHANGES.ru --- 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 и diff --git a/conf/mime.types b/conf/mime.types --- 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; } diff --git a/src/core/nginx.h b/src/core/nginx.h --- 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" diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- 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; diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- 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); diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c --- 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; diff --git a/src/core/ngx_hash.h b/src/core/ngx_hash.h --- 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_ */ diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- 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 } diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- 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; diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c --- 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; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- 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; } diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c --- 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, ¶ms[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; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- 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) { diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- 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; } diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- 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; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -9,6 +9,8 @@ #include +/* 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; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- 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; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- 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]; }; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- 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; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- 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; };