3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
13 ngx_uint_t hash_max_size;
14 ngx_uint_t hash_bucket_size;
15 } ngx_http_map_conf_t;
19 ngx_hash_keys_arrays_t keys;
21 ngx_array_t *values_hash;
23 ngx_http_variable_value_t *default_value;
24 ngx_uint_t hostnames; /* unsigned hostnames:1 */
25 } ngx_http_map_conf_ctx_t;
29 ngx_hash_combined_t hash;
31 ngx_http_variable_value_t *default_value;
32 ngx_uint_t hostnames; /* unsigned hostnames:1 */
36 static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
38 static void *ngx_http_map_create_conf(ngx_conf_t *cf);
39 static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
40 static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
43 static ngx_command_t ngx_http_map_commands[] = {
46 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
48 NGX_HTTP_MAIN_CONF_OFFSET,
52 { ngx_string("map_hash_max_size"),
53 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
54 ngx_conf_set_num_slot,
55 NGX_HTTP_MAIN_CONF_OFFSET,
56 offsetof(ngx_http_map_conf_t, hash_max_size),
59 { ngx_string("map_hash_bucket_size"),
60 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
61 ngx_conf_set_num_slot,
62 NGX_HTTP_MAIN_CONF_OFFSET,
63 offsetof(ngx_http_map_conf_t, hash_bucket_size),
70 static ngx_http_module_t ngx_http_map_module_ctx = {
71 NULL, /* preconfiguration */
72 NULL, /* postconfiguration */
74 ngx_http_map_create_conf, /* create main configuration */
75 NULL, /* init main configuration */
77 NULL, /* create server configuration */
78 NULL, /* merge server configuration */
80 NULL, /* create location configuration */
81 NULL /* merge location configuration */
85 ngx_module_t ngx_http_map_module = {
87 &ngx_http_map_module_ctx, /* module context */
88 ngx_http_map_commands, /* module directives */
89 NGX_HTTP_MODULE, /* module type */
90 NULL, /* init master */
91 NULL, /* init module */
92 NULL, /* init process */
93 NULL, /* init thread */
94 NULL, /* exit thread */
95 NULL, /* exit process */
96 NULL, /* exit master */
102 ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
105 ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
110 ngx_http_variable_value_t *vv, *value;
112 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
115 vv = ngx_http_get_flushed_variable(r, map->index);
117 if (vv == NULL || vv->not_found) {
118 *v = *map->default_value;
124 if (len && map->hostnames && vv->data[len - 1] == '.') {
129 *v = *map->default_value;
133 name = ngx_pnalloc(r->pool, len);
138 key = ngx_hash_strlow(name, vv->data, len);
140 value = ngx_hash_find_combined(&map->hash, key, name, len);
146 *v = *map->default_value;
149 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
150 "http map: \"%v\" \"%v\"", vv, v);
157 ngx_http_map_create_conf(ngx_conf_t *cf)
159 ngx_http_map_conf_t *mcf;
161 mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
163 return NGX_CONF_ERROR;
166 mcf->hash_max_size = NGX_CONF_UNSET_UINT;
167 mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
174 ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
176 ngx_http_map_conf_t *mcf = conf;
179 ngx_str_t *value, name;
182 ngx_hash_init_t hash;
183 ngx_http_map_ctx_t *map;
184 ngx_http_variable_t *var;
185 ngx_http_map_conf_ctx_t ctx;
187 if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
188 mcf->hash_max_size = 2048;
191 if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
192 mcf->hash_bucket_size = ngx_cacheline_size;
195 mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
199 map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
201 return NGX_CONF_ERROR;
204 value = cf->args->elts;
210 map->index = ngx_http_get_variable_index(cf, &name);
212 if (map->index == NGX_ERROR) {
213 return NGX_CONF_ERROR;
220 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
222 return NGX_CONF_ERROR;
225 var->get_handler = ngx_http_map_variable;
226 var->data = (uintptr_t) map;
228 pool = ngx_create_pool(16384, cf->log);
230 return NGX_CONF_ERROR;
233 ctx.keys.pool = cf->pool;
234 ctx.keys.temp_pool = pool;
236 if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
237 ngx_destroy_pool(pool);
238 return NGX_CONF_ERROR;
241 ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
242 if (ctx.values_hash == NULL) {
243 ngx_destroy_pool(pool);
244 return NGX_CONF_ERROR;
247 ctx.default_value = NULL;
253 cf->handler = ngx_http_map;
254 cf->handler_conf = conf;
256 rv = ngx_conf_parse(cf, NULL);
260 if (rv != NGX_CONF_OK) {
261 ngx_destroy_pool(pool);
265 map->default_value = ctx.default_value ? ctx.default_value:
266 &ngx_http_variable_null_value;
268 hash.key = ngx_hash_key_lc;
269 hash.max_size = mcf->hash_max_size;
270 hash.bucket_size = mcf->hash_bucket_size;
271 hash.name = "map_hash";
272 hash.pool = cf->pool;
274 if (ctx.keys.keys.nelts) {
275 hash.hash = &map->hash.hash;
276 hash.temp_pool = NULL;
278 if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
281 ngx_destroy_pool(pool);
282 return NGX_CONF_ERROR;
286 if (ctx.keys.dns_wc_head.nelts) {
288 ngx_qsort(ctx.keys.dns_wc_head.elts,
289 (size_t) ctx.keys.dns_wc_head.nelts,
290 sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
293 hash.temp_pool = pool;
295 if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
296 ctx.keys.dns_wc_head.nelts)
299 ngx_destroy_pool(pool);
300 return NGX_CONF_ERROR;
303 map->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
306 if (ctx.keys.dns_wc_tail.nelts) {
308 ngx_qsort(ctx.keys.dns_wc_tail.elts,
309 (size_t) ctx.keys.dns_wc_tail.nelts,
310 sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
313 hash.temp_pool = pool;
315 if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
316 ctx.keys.dns_wc_tail.nelts)
319 ngx_destroy_pool(pool);
320 return NGX_CONF_ERROR;
323 map->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
326 ngx_destroy_pool(pool);
332 static int ngx_libc_cdecl
333 ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
335 ngx_hash_key_t *first, *second;
337 first = (ngx_hash_key_t *) one;
338 second = (ngx_hash_key_t *) two;
340 return ngx_strcmp(first->key.data, second->key.data);
345 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
348 ngx_str_t *value, file;
350 ngx_http_map_conf_ctx_t *ctx;
351 ngx_http_variable_value_t *var, **vp;
355 value = cf->args->elts;
357 if (cf->args->nelts == 1
358 && ngx_strcmp(value[0].data, "hostnames") == 0)
363 } else if (cf->args->nelts != 2) {
364 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
365 "invalid number of the map parameters");
366 return NGX_CONF_ERROR;
368 } else if (value[0].len == 0) {
369 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
370 "invalid first parameter");
371 return NGX_CONF_ERROR;
374 if (ngx_strcmp(value[0].data, "include") == 0) {
377 if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
378 return NGX_CONF_ERROR;
381 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
383 return ngx_conf_parse(cf, &file);
388 for (i = 0; i < value[1].len; i++) {
389 key = ngx_hash(key, value[1].data[i]);
392 key %= ctx->keys.hsize;
394 vp = ctx->values_hash[key].elts;
397 for (i = 0; i < ctx->values_hash[key].nelts; i++) {
398 if (value[1].len != (size_t) vp[i]->len) {
402 if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {
409 if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
410 sizeof(ngx_http_variable_value_t *))
413 return NGX_CONF_ERROR;
417 var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
419 return NGX_CONF_ERROR;
422 var->len = value[1].len;
423 var->data = ngx_pstrdup(ctx->keys.pool, &value[1]);
424 if (var->data == NULL) {
425 return NGX_CONF_ERROR;
429 var->no_cacheable = 0;
432 vp = ngx_array_push(&ctx->values_hash[key]);
434 return NGX_CONF_ERROR;
441 if (ngx_strcmp(value[0].data, "default") == 0) {
443 if (ctx->default_value) {
444 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
445 "duplicate default map parameter");
446 return NGX_CONF_ERROR;
449 ctx->default_value = var;
454 if (value[0].len && value[0].data[0] == '!') {
459 rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
460 (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
466 if (rc == NGX_DECLINED) {
467 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
468 "invalid hostname or wildcard \"%V\"", &value[0]);
471 if (rc == NGX_BUSY) {
472 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
473 "conflicting parameter \"%V\"", &value[0]);
476 return NGX_CONF_ERROR;