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>