Annotation of embedaddon/nginx/src/http/modules/ngx_http_map_module.c, revision 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>