3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
14 ngx_uint_t hash_max_size
;
15 ngx_uint_t hash_bucket_size
;
16 } ngx_http_map_conf_t
;
20 ngx_hash_keys_arrays_t keys
;
22 ngx_array_t
*values_hash
;
23 ngx_array_t var_values
;
28 ngx_http_variable_value_t
*default_value
;
30 ngx_uint_t hostnames
; /* unsigned hostnames:1 */
31 } ngx_http_map_conf_ctx_t
;
36 ngx_http_complex_value_t value
;
37 ngx_http_variable_value_t
*default_value
;
38 ngx_uint_t hostnames
; /* unsigned hostnames:1 */
42 static int ngx_libc_cdecl
ngx_http_map_cmp_dns_wildcards(const void *one
,
44 static void *ngx_http_map_create_conf(ngx_conf_t
*cf
);
45 static char *ngx_http_map_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
);
46 static char *ngx_http_map(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
);
49 static ngx_command_t ngx_http_map_commands
[] = {
52 NGX_HTTP_MAIN_CONF
|NGX_CONF_BLOCK
|NGX_CONF_TAKE2
,
54 NGX_HTTP_MAIN_CONF_OFFSET
,
58 { ngx_string("map_hash_max_size"),
59 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE1
,
60 ngx_conf_set_num_slot
,
61 NGX_HTTP_MAIN_CONF_OFFSET
,
62 offsetof(ngx_http_map_conf_t
, hash_max_size
),
65 { ngx_string("map_hash_bucket_size"),
66 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE1
,
67 ngx_conf_set_num_slot
,
68 NGX_HTTP_MAIN_CONF_OFFSET
,
69 offsetof(ngx_http_map_conf_t
, hash_bucket_size
),
76 static ngx_http_module_t ngx_http_map_module_ctx
= {
77 NULL
, /* preconfiguration */
78 NULL
, /* postconfiguration */
80 ngx_http_map_create_conf
, /* create main configuration */
81 NULL
, /* init main configuration */
83 NULL
, /* create server configuration */
84 NULL
, /* merge server configuration */
86 NULL
, /* create location configuration */
87 NULL
/* merge location configuration */
91 ngx_module_t ngx_http_map_module
= {
93 &ngx_http_map_module_ctx
, /* module context */
94 ngx_http_map_commands
, /* module directives */
95 NGX_HTTP_MODULE
, /* module type */
96 NULL
, /* init master */
97 NULL
, /* init module */
98 NULL
, /* init process */
99 NULL
, /* init thread */
100 NULL
, /* exit thread */
101 NULL
, /* exit process */
102 NULL
, /* exit master */
103 NGX_MODULE_V1_PADDING
108 ngx_http_map_variable(ngx_http_request_t
*r
, ngx_http_variable_value_t
*v
,
111 ngx_http_map_ctx_t
*map
= (ngx_http_map_ctx_t
*) data
;
114 ngx_http_variable_value_t
*value
;
116 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
119 if (ngx_http_complex_value(r
, &map
->value
, &val
) != NGX_OK
) {
123 if (map
->hostnames
&& val
.len
> 0 && val
.data
[val
.len
- 1] == '.') {
127 value
= ngx_http_map_find(r
, &map
->map
, &val
);
130 value
= map
->default_value
;
134 value
= ngx_http_get_flushed_variable(r
, (ngx_uint_t
) value
->data
);
136 if (value
== NULL
|| value
->not_found
) {
137 value
= &ngx_http_variable_null_value
;
143 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
144 "http map: \"%v\" \"%v\"", &val
, v
);
151 ngx_http_map_create_conf(ngx_conf_t
*cf
)
153 ngx_http_map_conf_t
*mcf
;
155 mcf
= ngx_palloc(cf
->pool
, sizeof(ngx_http_map_conf_t
));
160 mcf
->hash_max_size
= NGX_CONF_UNSET_UINT
;
161 mcf
->hash_bucket_size
= NGX_CONF_UNSET_UINT
;
168 ngx_http_map_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
170 ngx_http_map_conf_t
*mcf
= conf
;
173 ngx_str_t
*value
, name
;
176 ngx_hash_init_t hash
;
177 ngx_http_map_ctx_t
*map
;
178 ngx_http_variable_t
*var
;
179 ngx_http_map_conf_ctx_t ctx
;
180 ngx_http_compile_complex_value_t ccv
;
182 if (mcf
->hash_max_size
== NGX_CONF_UNSET_UINT
) {
183 mcf
->hash_max_size
= 2048;
186 if (mcf
->hash_bucket_size
== NGX_CONF_UNSET_UINT
) {
187 mcf
->hash_bucket_size
= ngx_cacheline_size
;
190 mcf
->hash_bucket_size
= ngx_align(mcf
->hash_bucket_size
,
194 map
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_map_ctx_t
));
196 return NGX_CONF_ERROR
;
199 value
= cf
->args
->elts
;
201 ngx_memzero(&ccv
, sizeof(ngx_http_compile_complex_value_t
));
204 ccv
.value
= &value
[1];
205 ccv
.complex_value
= &map
->value
;
207 if (ngx_http_compile_complex_value(&ccv
) != NGX_OK
) {
208 return NGX_CONF_ERROR
;
213 if (name
.data
[0] != '$') {
214 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
215 "invalid variable name \"%V\"", &name
);
216 return NGX_CONF_ERROR
;
222 var
= ngx_http_add_variable(cf
, &name
, NGX_HTTP_VAR_CHANGEABLE
);
224 return NGX_CONF_ERROR
;
227 var
->get_handler
= ngx_http_map_variable
;
228 var
->data
= (uintptr_t) map
;
230 pool
= ngx_create_pool(NGX_DEFAULT_POOL_SIZE
, cf
->log
);
232 return NGX_CONF_ERROR
;
235 ctx
.keys
.pool
= cf
->pool
;
236 ctx
.keys
.temp_pool
= pool
;
238 if (ngx_hash_keys_array_init(&ctx
.keys
, NGX_HASH_LARGE
) != NGX_OK
) {
239 ngx_destroy_pool(pool
);
240 return NGX_CONF_ERROR
;
243 ctx
.values_hash
= ngx_pcalloc(pool
, sizeof(ngx_array_t
) * ctx
.keys
.hsize
);
244 if (ctx
.values_hash
== NULL
) {
245 ngx_destroy_pool(pool
);
246 return NGX_CONF_ERROR
;
249 if (ngx_array_init(&ctx
.var_values
, cf
->pool
, 2,
250 sizeof(ngx_http_variable_value_t
))
253 ngx_destroy_pool(pool
);
254 return NGX_CONF_ERROR
;
258 if (ngx_array_init(&ctx
.regexes
, cf
->pool
, 2, sizeof(ngx_http_map_regex_t
))
261 ngx_destroy_pool(pool
);
262 return NGX_CONF_ERROR
;
266 ctx
.default_value
= NULL
;
273 cf
->handler
= ngx_http_map
;
274 cf
->handler_conf
= conf
;
276 rv
= ngx_conf_parse(cf
, NULL
);
280 if (rv
!= NGX_CONF_OK
) {
281 ngx_destroy_pool(pool
);
285 map
->default_value
= ctx
.default_value
? ctx
.default_value
:
286 &ngx_http_variable_null_value
;
288 map
->hostnames
= ctx
.hostnames
;
290 hash
.key
= ngx_hash_key_lc
;
291 hash
.max_size
= mcf
->hash_max_size
;
292 hash
.bucket_size
= mcf
->hash_bucket_size
;
293 hash
.name
= "map_hash";
294 hash
.pool
= cf
->pool
;
296 if (ctx
.keys
.keys
.nelts
) {
297 hash
.hash
= &map
->map
.hash
.hash
;
298 hash
.temp_pool
= NULL
;
300 if (ngx_hash_init(&hash
, ctx
.keys
.keys
.elts
, ctx
.keys
.keys
.nelts
)
303 ngx_destroy_pool(pool
);
304 return NGX_CONF_ERROR
;
308 if (ctx
.keys
.dns_wc_head
.nelts
) {
310 ngx_qsort(ctx
.keys
.dns_wc_head
.elts
,
311 (size_t) ctx
.keys
.dns_wc_head
.nelts
,
312 sizeof(ngx_hash_key_t
), ngx_http_map_cmp_dns_wildcards
);
315 hash
.temp_pool
= pool
;
317 if (ngx_hash_wildcard_init(&hash
, ctx
.keys
.dns_wc_head
.elts
,
318 ctx
.keys
.dns_wc_head
.nelts
)
321 ngx_destroy_pool(pool
);
322 return NGX_CONF_ERROR
;
325 map
->map
.hash
.wc_head
= (ngx_hash_wildcard_t
*) hash
.hash
;
328 if (ctx
.keys
.dns_wc_tail
.nelts
) {
330 ngx_qsort(ctx
.keys
.dns_wc_tail
.elts
,
331 (size_t) ctx
.keys
.dns_wc_tail
.nelts
,
332 sizeof(ngx_hash_key_t
), ngx_http_map_cmp_dns_wildcards
);
335 hash
.temp_pool
= pool
;
337 if (ngx_hash_wildcard_init(&hash
, ctx
.keys
.dns_wc_tail
.elts
,
338 ctx
.keys
.dns_wc_tail
.nelts
)
341 ngx_destroy_pool(pool
);
342 return NGX_CONF_ERROR
;
345 map
->map
.hash
.wc_tail
= (ngx_hash_wildcard_t
*) hash
.hash
;
350 if (ctx
.regexes
.nelts
) {
351 map
->map
.regex
= ctx
.regexes
.elts
;
352 map
->map
.nregex
= ctx
.regexes
.nelts
;
357 ngx_destroy_pool(pool
);
363 static int ngx_libc_cdecl
364 ngx_http_map_cmp_dns_wildcards(const void *one
, const void *two
)
366 ngx_hash_key_t
*first
, *second
;
368 first
= (ngx_hash_key_t
*) one
;
369 second
= (ngx_hash_key_t
*) two
;
371 return ngx_dns_strcmp(first
->key
.data
, second
->key
.data
);
376 ngx_http_map(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
)
379 ngx_str_t
*value
, name
;
381 ngx_http_map_conf_ctx_t
*ctx
;
382 ngx_http_variable_value_t
*var
, **vp
;
386 value
= cf
->args
->elts
;
388 if (cf
->args
->nelts
== 1
389 && ngx_strcmp(value
[0].data
, "hostnames") == 0)
394 } else if (cf
->args
->nelts
!= 2) {
395 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
396 "invalid number of the map parameters");
397 return NGX_CONF_ERROR
;
400 if (ngx_strcmp(value
[0].data
, "include") == 0) {
401 return ngx_conf_include(cf
, dummy
, conf
);
404 if (value
[1].data
[0] == '$') {
409 index
= ngx_http_get_variable_index(ctx
->cf
, &name
);
410 if (index
== NGX_ERROR
) {
411 return NGX_CONF_ERROR
;
414 var
= ctx
->var_values
.elts
;
416 for (i
= 0; i
< ctx
->var_values
.nelts
; i
++) {
417 if (index
== (ngx_int_t
) var
[i
].data
) {
423 var
= ngx_array_push(&ctx
->var_values
);
425 return NGX_CONF_ERROR
;
429 var
->no_cacheable
= 0;
432 var
->data
= (u_char
*) index
;
439 for (i
= 0; i
< value
[1].len
; i
++) {
440 key
= ngx_hash(key
, value
[1].data
[i
]);
443 key
%= ctx
->keys
.hsize
;
445 vp
= ctx
->values_hash
[key
].elts
;
448 for (i
= 0; i
< ctx
->values_hash
[key
].nelts
; i
++) {
449 if (value
[1].len
!= (size_t) vp
[i
]->len
) {
453 if (ngx_strncmp(value
[1].data
, vp
[i
]->data
, value
[1].len
) == 0) {
460 if (ngx_array_init(&ctx
->values_hash
[key
], cf
->pool
, 4,
461 sizeof(ngx_http_variable_value_t
*))
464 return NGX_CONF_ERROR
;
468 var
= ngx_palloc(ctx
->keys
.pool
, sizeof(ngx_http_variable_value_t
));
470 return NGX_CONF_ERROR
;
473 var
->len
= value
[1].len
;
474 var
->data
= ngx_pstrdup(ctx
->keys
.pool
, &value
[1]);
475 if (var
->data
== NULL
) {
476 return NGX_CONF_ERROR
;
480 var
->no_cacheable
= 0;
483 vp
= ngx_array_push(&ctx
->values_hash
[key
]);
485 return NGX_CONF_ERROR
;
492 if (ngx_strcmp(value
[0].data
, "default") == 0) {
494 if (ctx
->default_value
) {
495 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
496 "duplicate default map parameter");
497 return NGX_CONF_ERROR
;
500 ctx
->default_value
= var
;
507 if (value
[0].len
&& value
[0].data
[0] == '~') {
508 ngx_regex_compile_t rc
;
509 ngx_http_map_regex_t
*regex
;
510 u_char errstr
[NGX_MAX_CONF_ERRSTR
];
512 regex
= ngx_array_push(&ctx
->regexes
);
514 return NGX_CONF_ERROR
;
520 ngx_memzero(&rc
, sizeof(ngx_regex_compile_t
));
522 if (value
[0].data
[0] == '*') {
525 rc
.options
= NGX_REGEX_CASELESS
;
528 rc
.pattern
= value
[0];
529 rc
.err
.len
= NGX_MAX_CONF_ERRSTR
;
530 rc
.err
.data
= errstr
;
532 regex
->regex
= ngx_http_regex_compile(ctx
->cf
, &rc
);
533 if (regex
->regex
== NULL
) {
534 return NGX_CONF_ERROR
;
544 if (value
[0].len
&& value
[0].data
[0] == '\\') {
549 rc
= ngx_hash_add_key(&ctx
->keys
, &value
[0], var
,
550 (ctx
->hostnames
) ? NGX_HASH_WILDCARD_KEY
: 0);
556 if (rc
== NGX_DECLINED
) {
557 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
558 "invalid hostname or wildcard \"%V\"", &value
[0]);
561 if (rc
== NGX_BUSY
) {
562 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
563 "conflicting parameter \"%V\"", &value
[0]);
566 return NGX_CONF_ERROR
;