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>