nginx 0.7.8
[nginx-catap.git] / src / http / modules / ngx_http_map_module.c
blobc2459f271b5205887e7c3dd51e0a83c83276bcfb
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
12 typedef struct {
13 ngx_uint_t hash_max_size;
14 ngx_uint_t hash_bucket_size;
15 } ngx_http_map_conf_t;
18 typedef struct {
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;
28 typedef struct {
29 ngx_hash_combined_t hash;
30 ngx_int_t index;
31 ngx_http_variable_value_t *default_value;
32 ngx_uint_t hostnames; /* unsigned hostnames:1 */
33 } ngx_http_map_ctx_t;
36 static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
37 const void *two);
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[] = {
45 { ngx_string("map"),
46 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
47 ngx_http_map_block,
48 NGX_HTTP_MAIN_CONF_OFFSET,
50 NULL },
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),
57 NULL },
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),
64 NULL },
66 ngx_null_command
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 = {
86 NGX_MODULE_V1,
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 */
97 NGX_MODULE_V1_PADDING
101 static ngx_int_t
102 ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
103 uintptr_t data)
105 ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
107 size_t len;
108 u_char *name;
109 ngx_uint_t key;
110 ngx_http_variable_value_t *vv, *value;
112 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
113 "http map started");
115 vv = ngx_http_get_flushed_variable(r, map->index);
117 if (vv == NULL || vv->not_found) {
118 *v = *map->default_value;
119 return NGX_OK;
122 len = vv->len;
124 if (len && map->hostnames && vv->data[len - 1] == '.') {
125 len--;
128 if (len == 0) {
129 *v = *map->default_value;
130 return NGX_OK;
133 name = ngx_pnalloc(r->pool, len);
134 if (name == NULL) {
135 return NGX_ERROR;
138 key = ngx_hash_strlow(name, vv->data, len);
140 value = ngx_hash_find_combined(&map->hash, key, name, len);
142 if (value) {
143 *v = *value;
145 } else {
146 *v = *map->default_value;
149 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
150 "http map: \"%v\" \"%v\"", vv, v);
152 return NGX_OK;
156 static void *
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));
162 if (mcf == NULL) {
163 return NGX_CONF_ERROR;
166 mcf->hash_max_size = NGX_CONF_UNSET_UINT;
167 mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
169 return mcf;
173 static char *
174 ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
176 ngx_http_map_conf_t *mcf = conf;
178 char *rv;
179 ngx_str_t *value, name;
180 ngx_conf_t save;
181 ngx_pool_t *pool;
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;
194 } else {
195 mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
196 ngx_cacheline_size);
199 map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
200 if (map == NULL) {
201 return NGX_CONF_ERROR;
204 value = cf->args->elts;
206 name = value[1];
207 name.len--;
208 name.data++;
210 map->index = ngx_http_get_variable_index(cf, &name);
212 if (map->index == NGX_ERROR) {
213 return NGX_CONF_ERROR;
216 name = value[2];
217 name.len--;
218 name.data++;
220 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
221 if (var == NULL) {
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);
229 if (pool == NULL) {
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;
248 ctx.hostnames = 0;
250 save = *cf;
251 cf->pool = pool;
252 cf->ctx = &ctx;
253 cf->handler = ngx_http_map;
254 cf->handler_conf = conf;
256 rv = ngx_conf_parse(cf, NULL);
258 *cf = save;
260 if (rv != NGX_CONF_OK) {
261 ngx_destroy_pool(pool);
262 return rv;
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)
279 != NGX_OK)
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);
292 hash.hash = NULL;
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)
297 != NGX_OK)
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);
312 hash.hash = NULL;
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)
317 != NGX_OK)
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);
328 return rv;
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);
344 static char *
345 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
347 ngx_int_t rc;
348 ngx_str_t *value, file;
349 ngx_uint_t i, key;
350 ngx_http_map_conf_ctx_t *ctx;
351 ngx_http_variable_value_t *var, **vp;
353 ctx = cf->ctx;
355 value = cf->args->elts;
357 if (cf->args->nelts == 1
358 && ngx_strcmp(value[0].data, "hostnames") == 0)
360 ctx->hostnames = 1;
361 return NGX_CONF_OK;
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) {
375 file = value[1];
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);
386 key = 0;
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;
396 if (vp) {
397 for (i = 0; i < ctx->values_hash[key].nelts; i++) {
398 if (value[1].len != (size_t) vp[i]->len) {
399 continue;
402 if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {
403 var = vp[i];
404 goto found;
408 } else {
409 if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
410 sizeof(ngx_http_variable_value_t *))
411 != NGX_OK)
413 return NGX_CONF_ERROR;
417 var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
418 if (var == NULL) {
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;
428 var->valid = 1;
429 var->no_cacheable = 0;
430 var->not_found = 0;
432 vp = ngx_array_push(&ctx->values_hash[key]);
433 if (vp == NULL) {
434 return NGX_CONF_ERROR;
437 *vp = var;
439 found:
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;
451 return NGX_CONF_OK;
454 if (value[0].len && value[0].data[0] == '!') {
455 value[0].len--;
456 value[0].data++;
459 rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
460 (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
462 if (rc == NGX_OK) {
463 return NGX_CONF_OK;
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;