Annotation of embedaddon/nginx/src/http/modules/ngx_http_map_module.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_http.h>
11:
12:
13: typedef struct {
14: ngx_uint_t hash_max_size;
15: ngx_uint_t hash_bucket_size;
16: } ngx_http_map_conf_t;
17:
18:
19: typedef struct {
20: ngx_hash_keys_arrays_t keys;
21:
22: ngx_array_t *values_hash;
23: ngx_array_t var_values;
24: #if (NGX_PCRE)
25: ngx_array_t regexes;
26: #endif
27:
28: ngx_http_variable_value_t *default_value;
29: ngx_conf_t *cf;
30: ngx_uint_t hostnames; /* unsigned hostnames:1 */
31: } ngx_http_map_conf_ctx_t;
32:
33:
34: typedef struct {
35: ngx_http_map_t map;
36: ngx_http_complex_value_t value;
37: ngx_http_variable_value_t *default_value;
38: ngx_uint_t hostnames; /* unsigned hostnames:1 */
39: } ngx_http_map_ctx_t;
40:
41:
42: static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
43: const void *two);
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);
47:
48:
49: static ngx_command_t ngx_http_map_commands[] = {
50:
51: { ngx_string("map"),
52: NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
53: ngx_http_map_block,
54: NGX_HTTP_MAIN_CONF_OFFSET,
55: 0,
56: NULL },
57:
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),
63: NULL },
64:
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),
70: NULL },
71:
72: ngx_null_command
73: };
74:
75:
76: static ngx_http_module_t ngx_http_map_module_ctx = {
77: NULL, /* preconfiguration */
78: NULL, /* postconfiguration */
79:
80: ngx_http_map_create_conf, /* create main configuration */
81: NULL, /* init main configuration */
82:
83: NULL, /* create server configuration */
84: NULL, /* merge server configuration */
85:
86: NULL, /* create location configuration */
87: NULL /* merge location configuration */
88: };
89:
90:
91: ngx_module_t ngx_http_map_module = {
92: NGX_MODULE_V1,
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
104: };
105:
106:
107: static ngx_int_t
108: ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
109: uintptr_t data)
110: {
111: ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
112:
113: ngx_str_t val;
114: ngx_http_variable_value_t *value;
115:
116: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
117: "http map started");
118:
119: if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
120: return NGX_ERROR;
121: }
122:
123: if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
124: val.len--;
125: }
126:
127: value = ngx_http_map_find(r, &map->map, &val);
128:
129: if (value == NULL) {
130: value = map->default_value;
131: }
132:
133: if (!value->valid) {
134: value = ngx_http_get_flushed_variable(r, (ngx_uint_t) value->data);
135:
136: if (value == NULL || value->not_found) {
137: value = &ngx_http_variable_null_value;
138: }
139: }
140:
141: *v = *value;
142:
143: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
144: "http map: \"%v\" \"%v\"", &val, v);
145:
146: return NGX_OK;
147: }
148:
149:
150: static void *
151: ngx_http_map_create_conf(ngx_conf_t *cf)
152: {
153: ngx_http_map_conf_t *mcf;
154:
155: mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
156: if (mcf == NULL) {
157: return NULL;
158: }
159:
160: mcf->hash_max_size = NGX_CONF_UNSET_UINT;
161: mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
162:
163: return mcf;
164: }
165:
166:
167: static char *
168: ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
169: {
170: ngx_http_map_conf_t *mcf = conf;
171:
172: char *rv;
173: ngx_str_t *value, name;
174: ngx_conf_t save;
175: ngx_pool_t *pool;
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;
181:
182: if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
183: mcf->hash_max_size = 2048;
184: }
185:
186: if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
187: mcf->hash_bucket_size = ngx_cacheline_size;
188:
189: } else {
190: mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
191: ngx_cacheline_size);
192: }
193:
194: map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
195: if (map == NULL) {
196: return NGX_CONF_ERROR;
197: }
198:
199: value = cf->args->elts;
200:
201: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
202:
203: ccv.cf = cf;
204: ccv.value = &value[1];
205: ccv.complex_value = &map->value;
206:
207: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
208: return NGX_CONF_ERROR;
209: }
210:
211: name = value[2];
212:
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;
217: }
218:
219: name.len--;
220: name.data++;
221:
222: var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
223: if (var == NULL) {
224: return NGX_CONF_ERROR;
225: }
226:
227: var->get_handler = ngx_http_map_variable;
228: var->data = (uintptr_t) map;
229:
230: pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
231: if (pool == NULL) {
232: return NGX_CONF_ERROR;
233: }
234:
235: ctx.keys.pool = cf->pool;
236: ctx.keys.temp_pool = pool;
237:
238: if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
239: ngx_destroy_pool(pool);
240: return NGX_CONF_ERROR;
241: }
242:
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;
247: }
248:
249: if (ngx_array_init(&ctx.var_values, cf->pool, 2,
250: sizeof(ngx_http_variable_value_t))
251: != NGX_OK)
252: {
253: ngx_destroy_pool(pool);
254: return NGX_CONF_ERROR;
255: }
256:
257: #if (NGX_PCRE)
258: if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
259: != NGX_OK)
260: {
261: ngx_destroy_pool(pool);
262: return NGX_CONF_ERROR;
263: }
264: #endif
265:
266: ctx.default_value = NULL;
267: ctx.cf = &save;
268: ctx.hostnames = 0;
269:
270: save = *cf;
271: cf->pool = pool;
272: cf->ctx = &ctx;
273: cf->handler = ngx_http_map;
274: cf->handler_conf = conf;
275:
276: rv = ngx_conf_parse(cf, NULL);
277:
278: *cf = save;
279:
280: if (rv != NGX_CONF_OK) {
281: ngx_destroy_pool(pool);
282: return rv;
283: }
284:
285: map->default_value = ctx.default_value ? ctx.default_value:
286: &ngx_http_variable_null_value;
287:
288: map->hostnames = ctx.hostnames;
289:
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;
295:
296: if (ctx.keys.keys.nelts) {
297: hash.hash = &map->map.hash.hash;
298: hash.temp_pool = NULL;
299:
300: if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
301: != NGX_OK)
302: {
303: ngx_destroy_pool(pool);
304: return NGX_CONF_ERROR;
305: }
306: }
307:
308: if (ctx.keys.dns_wc_head.nelts) {
309:
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);
313:
314: hash.hash = NULL;
315: hash.temp_pool = pool;
316:
317: if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
318: ctx.keys.dns_wc_head.nelts)
319: != NGX_OK)
320: {
321: ngx_destroy_pool(pool);
322: return NGX_CONF_ERROR;
323: }
324:
325: map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
326: }
327:
328: if (ctx.keys.dns_wc_tail.nelts) {
329:
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);
333:
334: hash.hash = NULL;
335: hash.temp_pool = pool;
336:
337: if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
338: ctx.keys.dns_wc_tail.nelts)
339: != NGX_OK)
340: {
341: ngx_destroy_pool(pool);
342: return NGX_CONF_ERROR;
343: }
344:
345: map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
346: }
347:
348: #if (NGX_PCRE)
349:
350: if (ctx.regexes.nelts) {
351: map->map.regex = ctx.regexes.elts;
352: map->map.nregex = ctx.regexes.nelts;
353: }
354:
355: #endif
356:
357: ngx_destroy_pool(pool);
358:
359: return rv;
360: }
361:
362:
363: static int ngx_libc_cdecl
364: ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
365: {
366: ngx_hash_key_t *first, *second;
367:
368: first = (ngx_hash_key_t *) one;
369: second = (ngx_hash_key_t *) two;
370:
371: return ngx_dns_strcmp(first->key.data, second->key.data);
372: }
373:
374:
375: static char *
376: ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
377: {
378: ngx_int_t rc, index;
379: ngx_str_t *value, name;
380: ngx_uint_t i, key;
381: ngx_http_map_conf_ctx_t *ctx;
382: ngx_http_variable_value_t *var, **vp;
383:
384: ctx = cf->ctx;
385:
386: value = cf->args->elts;
387:
388: if (cf->args->nelts == 1
389: && ngx_strcmp(value[0].data, "hostnames") == 0)
390: {
391: ctx->hostnames = 1;
392: return NGX_CONF_OK;
393:
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;
398: }
399:
400: if (ngx_strcmp(value[0].data, "include") == 0) {
401: return ngx_conf_include(cf, dummy, conf);
402: }
403:
404: if (value[1].data[0] == '$') {
405: name = value[1];
406: name.len--;
407: name.data++;
408:
409: index = ngx_http_get_variable_index(ctx->cf, &name);
410: if (index == NGX_ERROR) {
411: return NGX_CONF_ERROR;
412: }
413:
414: var = ctx->var_values.elts;
415:
416: for (i = 0; i < ctx->var_values.nelts; i++) {
417: if (index == (ngx_int_t) var[i].data) {
418: var = &var[i];
419: goto found;
420: }
421: }
422:
423: var = ngx_array_push(&ctx->var_values);
424: if (var == NULL) {
425: return NGX_CONF_ERROR;
426: }
427:
428: var->valid = 0;
429: var->no_cacheable = 0;
430: var->not_found = 0;
431: var->len = 0;
432: var->data = (u_char *) index;
433:
434: goto found;
435: }
436:
437: key = 0;
438:
439: for (i = 0; i < value[1].len; i++) {
440: key = ngx_hash(key, value[1].data[i]);
441: }
442:
443: key %= ctx->keys.hsize;
444:
445: vp = ctx->values_hash[key].elts;
446:
447: if (vp) {
448: for (i = 0; i < ctx->values_hash[key].nelts; i++) {
449: if (value[1].len != (size_t) vp[i]->len) {
450: continue;
451: }
452:
453: if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {
454: var = vp[i];
455: goto found;
456: }
457: }
458:
459: } else {
460: if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
461: sizeof(ngx_http_variable_value_t *))
462: != NGX_OK)
463: {
464: return NGX_CONF_ERROR;
465: }
466: }
467:
468: var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
469: if (var == NULL) {
470: return NGX_CONF_ERROR;
471: }
472:
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;
477: }
478:
479: var->valid = 1;
480: var->no_cacheable = 0;
481: var->not_found = 0;
482:
483: vp = ngx_array_push(&ctx->values_hash[key]);
484: if (vp == NULL) {
485: return NGX_CONF_ERROR;
486: }
487:
488: *vp = var;
489:
490: found:
491:
492: if (ngx_strcmp(value[0].data, "default") == 0) {
493:
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;
498: }
499:
500: ctx->default_value = var;
501:
502: return NGX_CONF_OK;
503: }
504:
505: #if (NGX_PCRE)
506:
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];
511:
512: regex = ngx_array_push(&ctx->regexes);
513: if (regex == NULL) {
514: return NGX_CONF_ERROR;
515: }
516:
517: value[0].len--;
518: value[0].data++;
519:
520: ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
521:
522: if (value[0].data[0] == '*') {
523: value[0].len--;
524: value[0].data++;
525: rc.options = NGX_REGEX_CASELESS;
526: }
527:
528: rc.pattern = value[0];
529: rc.err.len = NGX_MAX_CONF_ERRSTR;
530: rc.err.data = errstr;
531:
532: regex->regex = ngx_http_regex_compile(ctx->cf, &rc);
533: if (regex->regex == NULL) {
534: return NGX_CONF_ERROR;
535: }
536:
537: regex->value = var;
538:
539: return NGX_CONF_OK;
540: }
541:
542: #endif
543:
544: if (value[0].len && value[0].data[0] == '\\') {
545: value[0].len--;
546: value[0].data++;
547: }
548:
549: rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
550: (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
551:
552: if (rc == NGX_OK) {
553: return NGX_CONF_OK;
554: }
555:
556: if (rc == NGX_DECLINED) {
557: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
558: "invalid hostname or wildcard \"%V\"", &value[0]);
559: }
560:
561: if (rc == NGX_BUSY) {
562: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
563: "conflicting parameter \"%V\"", &value[0]);
564: }
565:
566: return NGX_CONF_ERROR;
567: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>