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
;