diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c index 1f7786c..96c321e 100644 --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -10,24 +10,35 @@ typedef struct { + ngx_uint_t pool_size; ngx_uint_t hash_max_size; ngx_uint_t hash_bucket_size; } ngx_http_map_conf_t; typedef struct { - ngx_hash_keys_arrays_t keys; + ngx_array_t *key_lengths; + ngx_array_t *key_values; + ngx_array_t *key_flushes; - ngx_array_t *values_hash; + ngx_uint_t nvars; - ngx_http_variable_value_t *default_value; + ngx_hash_keys_arrays_t *keys; + + ngx_array_t **values_hash; + + ngx_http_variable_value_t **default_value; ngx_uint_t hostnames; /* unsigned hostnames:1 */ } ngx_http_map_conf_ctx_t; typedef struct { ngx_hash_combined_t hash; - ngx_int_t index; + + ngx_array_t *key_lengths; + ngx_array_t *key_values; + ngx_array_t *key_flushes; + ngx_http_variable_value_t *default_value; ngx_uint_t hostnames; /* unsigned hostnames:1 */ } ngx_http_map_ctx_t; @@ -43,12 +54,19 @@ static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static ngx_command_t ngx_http_map_commands[] = { { ngx_string("map"), - NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_2MORE, ngx_http_map_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, + { ngx_string("map_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_map_conf_t, pool_size), + NULL }, + { ngx_string("map_hash_max_size"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -104,40 +122,65 @@ ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, { ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data; - size_t len; - u_char *name; + ngx_str_t str; ngx_uint_t key; - ngx_http_variable_value_t *vv, *value; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e; + ngx_http_variable_value_t *value; + ngx_http_script_len_code_pt lcode; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http map started"); - vv = ngx_http_get_flushed_variable(r, map->index); + ngx_http_script_force_flush_variables(r, map->key_flushes); - if (vv == NULL || vv->not_found) { - *v = *map->default_value; - return NGX_OK; - } + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + e.ip = map->key_lengths->elts; + e.request = r; + e.flushed = 0; - len = vv->len; + str.len = 0; - if (len && map->hostnames && vv->data[len - 1] == '.') { - len--; + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + str.len += lcode(&e); } - if (len == 0) { + if (str.len == 0) { *v = *map->default_value; return NGX_OK; } - name = ngx_pnalloc(r->pool, len); - if (name == NULL) { + str.data = ngx_palloc(r->pool, str.len); + if (str.data == NULL) { return NGX_ERROR; } - key = ngx_hash_strlow(name, vv->data, len); + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + e.pos = str.data; + e.ip = map->key_values->elts; + e.request = r; + e.flushed = 0; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + str.len = e.pos - str.data; - value = ngx_hash_find_combined(&map->hash, key, name, len); + if (str.len && map->hostnames && str.data[str.len - 1] == '.') { + str.len--; + } + + if (str.len == 0) { + *v = *map->default_value; + return NGX_OK; + } + + key = ngx_hash_strlow(str.data, str.data, str.len); + + value = ngx_hash_find_combined(&map->hash, key, str.data, str.len); if (value) { *v = *value; @@ -147,7 +190,7 @@ ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http map: \"%v\" \"%v\"", vv, v); + "http map: \"%V\" \"%v\"", &str, v); return NGX_OK; } @@ -163,6 +206,7 @@ ngx_http_map_create_conf(ngx_conf_t *cf) return NULL; } + mcf->pool_size = NGX_CONF_UNSET_UINT; mcf->hash_max_size = NGX_CONF_UNSET_UINT; mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; @@ -179,10 +223,16 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_str_t *value, name; ngx_conf_t save; ngx_pool_t *pool; + ngx_uint_t i, n; ngx_hash_init_t hash; ngx_http_map_ctx_t *map; ngx_http_variable_t *var; ngx_http_map_conf_ctx_t ctx; + ngx_http_script_compile_t sc; + + if (mcf->pool_size == NGX_CONF_UNSET_UINT) { + mcf->pool_size = 16384; + } if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { mcf->hash_max_size = 2048; @@ -196,55 +246,96 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_cacheline_size); } - map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t)); - if (map == NULL) { - return NGX_CONF_ERROR; + value = cf->args->elts; + + ctx.key_flushes = NULL; + ctx.key_lengths = NULL; + ctx.key_values = NULL; + + n = ngx_http_script_variables_count(&value[1]); + + if (n == 0) { + return "not using a variables in map key"; } - value = cf->args->elts; + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - name = value[1]; - name.len--; - name.data++; + sc.cf = cf; + sc.source = &value[1]; + sc.lengths = &ctx.key_lengths; + sc.values = &ctx.key_values; + sc.flushes = &ctx.key_flushes; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; - map->index = ngx_http_get_variable_index(cf, &name); + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ctx.nvars = cf->args->nelts - 2; - if (map->index == NGX_ERROR) { + map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t) * ctx.nvars); + if (map == NULL) { return NGX_CONF_ERROR; } - name = value[2]; - name.len--; - name.data++; + ctx.keys = ngx_pcalloc(cf->pool, sizeof(ngx_hash_keys_arrays_t) * ctx.nvars); + if (ctx.keys == NULL) { + return NGX_CONF_ERROR; + } - var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); - if (var == NULL) { + ctx.values_hash = ngx_pcalloc(cf->pool, sizeof(ngx_array_t *) * ctx.nvars); + if (ctx.values_hash == NULL) { return NGX_CONF_ERROR; } - var->get_handler = ngx_http_map_variable; - var->data = (uintptr_t) map; + ctx.default_value = ngx_pcalloc(cf->pool, + sizeof(ngx_http_variable_value_t *) * ctx.nvars); + if (ctx.default_value == NULL) { + return NGX_CONF_ERROR; + } - pool = ngx_create_pool(16384, cf->log); + pool = ngx_create_pool(mcf->hash_max_size, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } - ctx.keys.pool = cf->pool; - ctx.keys.temp_pool = pool; + for (i = 0; i < ctx.nvars; i++) { + map[i].key_flushes = ctx.key_flushes; + map[i].key_lengths = ctx.key_lengths; + map[i].key_values = ctx.key_values; - if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; - } + name = value[i + 2]; + name.len--; + name.data++; - ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize); - if (ctx.values_hash == NULL) { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; + var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); + if (var == NULL) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_http_map_variable; + var->data = (uintptr_t) &map[i]; + + ctx.keys[i].pool = cf->pool; + ctx.keys[i].temp_pool = pool; + + if (ngx_hash_keys_array_init(&ctx.keys[i], NGX_HASH_LARGE) != NGX_OK) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + ctx.values_hash[i] = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys[i].hsize); + if (ctx.values_hash[i] == NULL) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + ctx.default_value[i] = NULL; } - ctx.default_value = NULL; ctx.hostnames = 0; save = *cf; @@ -262,65 +353,71 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return rv; } - map->default_value = ctx.default_value ? ctx.default_value: - &ngx_http_variable_null_value; + for (i = 0; i < ctx.nvars; i++) { - hash.key = ngx_hash_key_lc; - hash.max_size = mcf->hash_max_size; - hash.bucket_size = mcf->hash_bucket_size; - hash.name = "map_hash"; - hash.pool = cf->pool; + map[i].hostnames = ctx.hostnames; - if (ctx.keys.keys.nelts) { - hash.hash = &map->hash.hash; - hash.temp_pool = NULL; + map[i].default_value = ctx.default_value[i] ? ctx.default_value[i]: + &ngx_http_variable_null_value; - if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) - != NGX_OK) - { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; + hash.key = ngx_hash_key_lc; + hash.max_size = mcf->hash_max_size; + hash.bucket_size = mcf->hash_bucket_size; + hash.name = "map_hash"; + hash.pool = cf->pool; + + if (ctx.keys[i].keys.nelts) { + hash.hash = &map[i].hash.hash; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, ctx.keys[i].keys.elts, ctx.keys[i].keys.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } } - } - if (ctx.keys.dns_wc_head.nelts) { + if (ctx.keys[i].dns_wc_head.nelts) { - ngx_qsort(ctx.keys.dns_wc_head.elts, - (size_t) ctx.keys.dns_wc_head.nelts, - sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); + ngx_qsort(ctx.keys[i].dns_wc_head.elts, + (size_t) ctx.keys[i].dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); - hash.hash = NULL; - hash.temp_pool = pool; + hash.hash = NULL; + hash.temp_pool = pool; - if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts, - ctx.keys.dns_wc_head.nelts) - != NGX_OK) - { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; + if (ngx_hash_wildcard_init(&hash, ctx.keys[i].dns_wc_head.elts, + ctx.keys[i].dns_wc_head.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + map[i].hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; } - map->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; - } + if (ctx.keys[i].dns_wc_tail.nelts) { - if (ctx.keys.dns_wc_tail.nelts) { + ngx_qsort(ctx.keys[i].dns_wc_tail.elts, + (size_t) ctx.keys[i].dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); - ngx_qsort(ctx.keys.dns_wc_tail.elts, - (size_t) ctx.keys.dns_wc_tail.nelts, - sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); + hash.hash = NULL; + hash.temp_pool = pool; - hash.hash = NULL; - hash.temp_pool = pool; + if (ngx_hash_wildcard_init(&hash, ctx.keys[i].dns_wc_tail.elts, + ctx.keys[i].dns_wc_tail.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } - if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts, - ctx.keys.dns_wc_tail.nelts) - != NGX_OK) - { - ngx_destroy_pool(pool); - return NGX_CONF_ERROR; + map[i].hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; } - map->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; } ngx_destroy_pool(pool); @@ -346,7 +443,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { ngx_int_t rc; ngx_str_t *value, file; - ngx_uint_t i, key; + ngx_uint_t i, j, key; ngx_http_map_conf_ctx_t *ctx; ngx_http_variable_value_t *var, **vp; @@ -360,18 +457,9 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ctx->hostnames = 1; return NGX_CONF_OK; - } else if (cf->args->nelts != 2) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid number of the map parameters"); - return NGX_CONF_ERROR; - - } else if (value[0].len == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid first parameter"); - return NGX_CONF_ERROR; - } + } else if (cf->args->nelts == 2 && + ngx_strcmp(value[0].data, "include") == 0) { - if (ngx_strcmp(value[0].data, "include") == 0) { file = value[1]; if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ @@ -381,97 +469,112 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); return ngx_conf_parse(cf, &file); - } - key = 0; + } else if (cf->args->nelts != ctx->nvars + 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of the map parameters"); + return NGX_CONF_ERROR; - for (i = 0; i < value[1].len; i++) { - key = ngx_hash(key, value[1].data[i]); + } else if (value[0].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid first parameter"); + return NGX_CONF_ERROR; } - key %= ctx->keys.hsize; - vp = ctx->values_hash[key].elts; + for (i = 0; i < ctx->nvars; i++) { + key = 0; - if (vp) { - for (i = 0; i < ctx->values_hash[key].nelts; i++) { - if (value[1].len != (size_t) vp[i]->len) { - continue; - } + for (j = 0; j < value[i + 1].len; j++) { + key = ngx_hash(key, value[i + 1].data[i]); + } + + key %= ctx->keys[i].hsize; - if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) { - var = vp[i]; - goto found; + vp = ctx->values_hash[i][key].elts; + + if (vp) { + for (j = 0; j < ctx->values_hash[i][key].nelts; j++) { + if (value[i + 1].len != (size_t) vp[j]->len) { + continue; + } + + if (ngx_strncmp(value[i + 1].data, vp[j]->data, value[i + 1].len) == 0) { + var = vp[j]; + goto found; + } } + + } else { + if (ngx_array_init(&ctx->values_hash[i][key], cf->pool, 4, + sizeof(ngx_http_variable_value_t *)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } - } else { - if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, - sizeof(ngx_http_variable_value_t *)) - != NGX_OK) - { + var = ngx_palloc(ctx->keys[i].pool, sizeof(ngx_http_variable_value_t)); + if (var == NULL) { return NGX_CONF_ERROR; } - } - var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t)); - if (var == NULL) { - return NGX_CONF_ERROR; - } + var->len = value[i + 1].len; + var->data = ngx_pstrdup(ctx->keys[i].pool, &value[i + 1]); + if (var->data == NULL) { + return NGX_CONF_ERROR; + } - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->keys.pool, &value[1]); - if (var->data == NULL) { - return NGX_CONF_ERROR; - } + var->valid = 1; + var->no_cacheable = 1; + var->not_found = 0; + + vp = ngx_array_push(&ctx->values_hash[i][key]); + if (vp == NULL) { + return NGX_CONF_ERROR; + } - var->valid = 1; - var->no_cacheable = 0; - var->not_found = 0; + *vp = var; - vp = ngx_array_push(&ctx->values_hash[key]); - if (vp == NULL) { - return NGX_CONF_ERROR; - } + found: - *vp = var; + if (ngx_strcmp(value[0].data, "default") == 0) { -found: + if (ctx->default_value[i]) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate default map parameter"); + return NGX_CONF_ERROR; + } - if (ngx_strcmp(value[0].data, "default") == 0) { + ctx->default_value[i] = var; - if (ctx->default_value) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate default map parameter"); - return NGX_CONF_ERROR; + continue; } - ctx->default_value = var; - - return NGX_CONF_OK; - } + if (value[0].len && value[0].data[0] == '!') { + value[0].len--; + value[0].data++; + } - if (value[0].len && value[0].data[0] == '!') { - value[0].len--; - value[0].data++; - } + rc = ngx_hash_add_key(&ctx->keys[i], &value[0], var, + (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); - rc = ngx_hash_add_key(&ctx->keys, &value[0], var, - (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); + if (rc == NGX_OK) { + continue; + } - if (rc == NGX_OK) { - return NGX_CONF_OK; - } + if (rc == NGX_DECLINED) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid hostname or wildcard \"%V\"", &value[0]); + } - if (rc == NGX_DECLINED) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid hostname or wildcard \"%V\"", &value[0]); - } + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting parameter \"%V\"", &value[0]); + } - if (rc == NGX_BUSY) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "conflicting parameter \"%V\"", &value[0]); + return NGX_CONF_ERROR; } - return NGX_CONF_ERROR; + return NGX_CONF_OK; } diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c index ad9d88b..9184b33 100644 --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -448,6 +448,22 @@ ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r, } +void +ngx_http_script_force_flush_variables(ngx_http_request_t *r, + ngx_array_t *indices) +{ + ngx_uint_t n, *index; + + if (indices) { + index = indices->elts; + for (n = 0; n < indices->nelts; n++) { + r->variables[index[n]].valid = 0; + r->variables[index[n]].not_found = 0; + } + } +} + + static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc) { diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h index ea97069..20b50ca 100644 --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -215,6 +215,8 @@ u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value, void *code_lengths, size_t reserved, void *code_values); void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r, ngx_array_t *indices); +void ngx_http_script_force_flush_variables(ngx_http_request_t *r, + ngx_array_t *indices); void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size);