Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     ngx_uint_t                  hash_max_size;
0015     ngx_uint_t                  hash_bucket_size;
0016 } ngx_http_map_conf_t;
0017 
0018 
0019 typedef struct {
0020     ngx_hash_keys_arrays_t      keys;
0021 
0022     ngx_array_t                *values_hash;
0023 #if (NGX_PCRE)
0024     ngx_array_t                 regexes;
0025 #endif
0026 
0027     ngx_http_variable_value_t  *default_value;
0028     ngx_conf_t                 *cf;
0029     unsigned                    hostnames:1;
0030     unsigned                    no_cacheable:1;
0031 } ngx_http_map_conf_ctx_t;
0032 
0033 
0034 typedef struct {
0035     ngx_http_map_t              map;
0036     ngx_http_complex_value_t    value;
0037     ngx_http_variable_value_t  *default_value;
0038     ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */
0039 } ngx_http_map_ctx_t;
0040 
0041 
0042 static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
0043     const void *two);
0044 static void *ngx_http_map_create_conf(ngx_conf_t *cf);
0045 static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
0046 static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
0047 
0048 
0049 static ngx_command_t  ngx_http_map_commands[] = {
0050 
0051     { ngx_string("map"),
0052       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
0053       ngx_http_map_block,
0054       NGX_HTTP_MAIN_CONF_OFFSET,
0055       0,
0056       NULL },
0057 
0058     { ngx_string("map_hash_max_size"),
0059       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
0060       ngx_conf_set_num_slot,
0061       NGX_HTTP_MAIN_CONF_OFFSET,
0062       offsetof(ngx_http_map_conf_t, hash_max_size),
0063       NULL },
0064 
0065     { ngx_string("map_hash_bucket_size"),
0066       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
0067       ngx_conf_set_num_slot,
0068       NGX_HTTP_MAIN_CONF_OFFSET,
0069       offsetof(ngx_http_map_conf_t, hash_bucket_size),
0070       NULL },
0071 
0072       ngx_null_command
0073 };
0074 
0075 
0076 static ngx_http_module_t  ngx_http_map_module_ctx = {
0077     NULL,                                  /* preconfiguration */
0078     NULL,                                  /* postconfiguration */
0079 
0080     ngx_http_map_create_conf,              /* create main configuration */
0081     NULL,                                  /* init main configuration */
0082 
0083     NULL,                                  /* create server configuration */
0084     NULL,                                  /* merge server configuration */
0085 
0086     NULL,                                  /* create location configuration */
0087     NULL                                   /* merge location configuration */
0088 };
0089 
0090 
0091 ngx_module_t  ngx_http_map_module = {
0092     NGX_MODULE_V1,
0093     &ngx_http_map_module_ctx,              /* module context */
0094     ngx_http_map_commands,                 /* module directives */
0095     NGX_HTTP_MODULE,                       /* module type */
0096     NULL,                                  /* init master */
0097     NULL,                                  /* init module */
0098     NULL,                                  /* init process */
0099     NULL,                                  /* init thread */
0100     NULL,                                  /* exit thread */
0101     NULL,                                  /* exit process */
0102     NULL,                                  /* exit master */
0103     NGX_MODULE_V1_PADDING
0104 };
0105 
0106 
0107 static ngx_int_t
0108 ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
0109     uintptr_t data)
0110 {
0111     ngx_http_map_ctx_t  *map = (ngx_http_map_ctx_t *) data;
0112 
0113     ngx_str_t                   val, str;
0114     ngx_http_complex_value_t   *cv;
0115     ngx_http_variable_value_t  *value;
0116 
0117     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0118                    "http map started");
0119 
0120     if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
0121         return NGX_ERROR;
0122     }
0123 
0124     if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
0125         val.len--;
0126     }
0127 
0128     value = ngx_http_map_find(r, &map->map, &val);
0129 
0130     if (value == NULL) {
0131         value = map->default_value;
0132     }
0133 
0134     if (!value->valid) {
0135         cv = (ngx_http_complex_value_t *) value->data;
0136 
0137         if (ngx_http_complex_value(r, cv, &str) != NGX_OK) {
0138             return NGX_ERROR;
0139         }
0140 
0141         v->valid = 1;
0142         v->no_cacheable = 0;
0143         v->not_found = 0;
0144         v->len = str.len;
0145         v->data = str.data;
0146 
0147     } else {
0148         *v = *value;
0149     }
0150 
0151     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0152                    "http map: \"%V\" \"%v\"", &val, v);
0153 
0154     return NGX_OK;
0155 }
0156 
0157 
0158 static void *
0159 ngx_http_map_create_conf(ngx_conf_t *cf)
0160 {
0161     ngx_http_map_conf_t  *mcf;
0162 
0163     mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
0164     if (mcf == NULL) {
0165         return NULL;
0166     }
0167 
0168     mcf->hash_max_size = NGX_CONF_UNSET_UINT;
0169     mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
0170 
0171     return mcf;
0172 }
0173 
0174 
0175 static char *
0176 ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0177 {
0178     ngx_http_map_conf_t  *mcf = conf;
0179 
0180     char                              *rv;
0181     ngx_str_t                         *value, name;
0182     ngx_conf_t                         save;
0183     ngx_pool_t                        *pool;
0184     ngx_hash_init_t                    hash;
0185     ngx_http_map_ctx_t                *map;
0186     ngx_http_variable_t               *var;
0187     ngx_http_map_conf_ctx_t            ctx;
0188     ngx_http_compile_complex_value_t   ccv;
0189 
0190     if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
0191         mcf->hash_max_size = 2048;
0192     }
0193 
0194     if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
0195         mcf->hash_bucket_size = ngx_cacheline_size;
0196 
0197     } else {
0198         mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
0199                                           ngx_cacheline_size);
0200     }
0201 
0202     map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
0203     if (map == NULL) {
0204         return NGX_CONF_ERROR;
0205     }
0206 
0207     value = cf->args->elts;
0208 
0209     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0210 
0211     ccv.cf = cf;
0212     ccv.value = &value[1];
0213     ccv.complex_value = &map->value;
0214 
0215     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0216         return NGX_CONF_ERROR;
0217     }
0218 
0219     name = value[2];
0220 
0221     if (name.data[0] != '$') {
0222         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0223                            "invalid variable name \"%V\"", &name);
0224         return NGX_CONF_ERROR;
0225     }
0226 
0227     name.len--;
0228     name.data++;
0229 
0230     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
0231     if (var == NULL) {
0232         return NGX_CONF_ERROR;
0233     }
0234 
0235     var->get_handler = ngx_http_map_variable;
0236     var->data = (uintptr_t) map;
0237 
0238     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
0239     if (pool == NULL) {
0240         return NGX_CONF_ERROR;
0241     }
0242 
0243     ctx.keys.pool = cf->pool;
0244     ctx.keys.temp_pool = pool;
0245 
0246     if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
0247         ngx_destroy_pool(pool);
0248         return NGX_CONF_ERROR;
0249     }
0250 
0251     ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
0252     if (ctx.values_hash == NULL) {
0253         ngx_destroy_pool(pool);
0254         return NGX_CONF_ERROR;
0255     }
0256 
0257 #if (NGX_PCRE)
0258     if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
0259         != NGX_OK)
0260     {
0261         ngx_destroy_pool(pool);
0262         return NGX_CONF_ERROR;
0263     }
0264 #endif
0265 
0266     ctx.default_value = NULL;
0267     ctx.cf = &save;
0268     ctx.hostnames = 0;
0269     ctx.no_cacheable = 0;
0270 
0271     save = *cf;
0272     cf->pool = pool;
0273     cf->ctx = &ctx;
0274     cf->handler = ngx_http_map;
0275     cf->handler_conf = conf;
0276 
0277     rv = ngx_conf_parse(cf, NULL);
0278 
0279     *cf = save;
0280 
0281     if (rv != NGX_CONF_OK) {
0282         ngx_destroy_pool(pool);
0283         return rv;
0284     }
0285 
0286     if (ctx.no_cacheable) {
0287         var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
0288     }
0289 
0290     map->default_value = ctx.default_value ? ctx.default_value:
0291                                              &ngx_http_variable_null_value;
0292 
0293     map->hostnames = ctx.hostnames;
0294 
0295     hash.key = ngx_hash_key_lc;
0296     hash.max_size = mcf->hash_max_size;
0297     hash.bucket_size = mcf->hash_bucket_size;
0298     hash.name = "map_hash";
0299     hash.pool = cf->pool;
0300 
0301     if (ctx.keys.keys.nelts) {
0302         hash.hash = &map->map.hash.hash;
0303         hash.temp_pool = NULL;
0304 
0305         if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
0306             != NGX_OK)
0307         {
0308             ngx_destroy_pool(pool);
0309             return NGX_CONF_ERROR;
0310         }
0311     }
0312 
0313     if (ctx.keys.dns_wc_head.nelts) {
0314 
0315         ngx_qsort(ctx.keys.dns_wc_head.elts,
0316                   (size_t) ctx.keys.dns_wc_head.nelts,
0317                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
0318 
0319         hash.hash = NULL;
0320         hash.temp_pool = pool;
0321 
0322         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
0323                                    ctx.keys.dns_wc_head.nelts)
0324             != NGX_OK)
0325         {
0326             ngx_destroy_pool(pool);
0327             return NGX_CONF_ERROR;
0328         }
0329 
0330         map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
0331     }
0332 
0333     if (ctx.keys.dns_wc_tail.nelts) {
0334 
0335         ngx_qsort(ctx.keys.dns_wc_tail.elts,
0336                   (size_t) ctx.keys.dns_wc_tail.nelts,
0337                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
0338 
0339         hash.hash = NULL;
0340         hash.temp_pool = pool;
0341 
0342         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
0343                                    ctx.keys.dns_wc_tail.nelts)
0344             != NGX_OK)
0345         {
0346             ngx_destroy_pool(pool);
0347             return NGX_CONF_ERROR;
0348         }
0349 
0350         map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
0351     }
0352 
0353 #if (NGX_PCRE)
0354 
0355     if (ctx.regexes.nelts) {
0356         map->map.regex = ctx.regexes.elts;
0357         map->map.nregex = ctx.regexes.nelts;
0358     }
0359 
0360 #endif
0361 
0362     ngx_destroy_pool(pool);
0363 
0364     return rv;
0365 }
0366 
0367 
0368 static int ngx_libc_cdecl
0369 ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
0370 {
0371     ngx_hash_key_t  *first, *second;
0372 
0373     first = (ngx_hash_key_t *) one;
0374     second = (ngx_hash_key_t *) two;
0375 
0376     return ngx_dns_strcmp(first->key.data, second->key.data);
0377 }
0378 
0379 
0380 static char *
0381 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
0382 {
0383     u_char                            *data;
0384     size_t                             len;
0385     ngx_int_t                          rv;
0386     ngx_str_t                         *value, v;
0387     ngx_uint_t                         i, key;
0388     ngx_http_map_conf_ctx_t           *ctx;
0389     ngx_http_complex_value_t           cv, *cvp;
0390     ngx_http_variable_value_t         *var, **vp;
0391     ngx_http_compile_complex_value_t   ccv;
0392 
0393     ctx = cf->ctx;
0394 
0395     value = cf->args->elts;
0396 
0397     if (cf->args->nelts == 1
0398         && ngx_strcmp(value[0].data, "hostnames") == 0)
0399     {
0400         ctx->hostnames = 1;
0401         return NGX_CONF_OK;
0402     }
0403 
0404     if (cf->args->nelts == 1
0405         && ngx_strcmp(value[0].data, "volatile") == 0)
0406     {
0407         ctx->no_cacheable = 1;
0408         return NGX_CONF_OK;
0409     }
0410 
0411     if (cf->args->nelts != 2) {
0412         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0413                            "invalid number of the map parameters");
0414         return NGX_CONF_ERROR;
0415     }
0416 
0417     if (ngx_strcmp(value[0].data, "include") == 0) {
0418         return ngx_conf_include(cf, dummy, conf);
0419     }
0420 
0421     key = 0;
0422 
0423     for (i = 0; i < value[1].len; i++) {
0424         key = ngx_hash(key, value[1].data[i]);
0425     }
0426 
0427     key %= ctx->keys.hsize;
0428 
0429     vp = ctx->values_hash[key].elts;
0430 
0431     if (vp) {
0432         for (i = 0; i < ctx->values_hash[key].nelts; i++) {
0433 
0434             if (vp[i]->valid) {
0435                 data = vp[i]->data;
0436                 len = vp[i]->len;
0437 
0438             } else {
0439                 cvp = (ngx_http_complex_value_t *) vp[i]->data;
0440                 data = cvp->value.data;
0441                 len = cvp->value.len;
0442             }
0443 
0444             if (value[1].len != len) {
0445                 continue;
0446             }
0447 
0448             if (ngx_strncmp(value[1].data, data, len) == 0) {
0449                 var = vp[i];
0450                 goto found;
0451             }
0452         }
0453 
0454     } else {
0455         if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
0456                            sizeof(ngx_http_variable_value_t *))
0457             != NGX_OK)
0458         {
0459             return NGX_CONF_ERROR;
0460         }
0461     }
0462 
0463     var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
0464     if (var == NULL) {
0465         return NGX_CONF_ERROR;
0466     }
0467 
0468     v.len = value[1].len;
0469     v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);
0470     if (v.data == NULL) {
0471         return NGX_CONF_ERROR;
0472     }
0473 
0474     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0475 
0476     ccv.cf = ctx->cf;
0477     ccv.value = &v;
0478     ccv.complex_value = &cv;
0479 
0480     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0481         return NGX_CONF_ERROR;
0482     }
0483 
0484     if (cv.lengths != NULL) {
0485         cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_complex_value_t));
0486         if (cvp == NULL) {
0487             return NGX_CONF_ERROR;
0488         }
0489 
0490         *cvp = cv;
0491 
0492         var->len = 0;
0493         var->data = (u_char *) cvp;
0494         var->valid = 0;
0495 
0496     } else {
0497         var->len = v.len;
0498         var->data = v.data;
0499         var->valid = 1;
0500     }
0501 
0502     var->no_cacheable = 0;
0503     var->not_found = 0;
0504 
0505     vp = ngx_array_push(&ctx->values_hash[key]);
0506     if (vp == NULL) {
0507         return NGX_CONF_ERROR;
0508     }
0509 
0510     *vp = var;
0511 
0512 found:
0513 
0514     if (ngx_strcmp(value[0].data, "default") == 0) {
0515 
0516         if (ctx->default_value) {
0517             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0518                                "duplicate default map parameter");
0519             return NGX_CONF_ERROR;
0520         }
0521 
0522         ctx->default_value = var;
0523 
0524         return NGX_CONF_OK;
0525     }
0526 
0527 #if (NGX_PCRE)
0528 
0529     if (value[0].len && value[0].data[0] == '~') {
0530         ngx_regex_compile_t    rc;
0531         ngx_http_map_regex_t  *regex;
0532         u_char                 errstr[NGX_MAX_CONF_ERRSTR];
0533 
0534         regex = ngx_array_push(&ctx->regexes);
0535         if (regex == NULL) {
0536             return NGX_CONF_ERROR;
0537         }
0538 
0539         value[0].len--;
0540         value[0].data++;
0541 
0542         ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
0543 
0544         if (value[0].data[0] == '*') {
0545             value[0].len--;
0546             value[0].data++;
0547             rc.options = NGX_REGEX_CASELESS;
0548         }
0549 
0550         rc.pattern = value[0];
0551         rc.err.len = NGX_MAX_CONF_ERRSTR;
0552         rc.err.data = errstr;
0553 
0554         regex->regex = ngx_http_regex_compile(ctx->cf, &rc);
0555         if (regex->regex == NULL) {
0556             return NGX_CONF_ERROR;
0557         }
0558 
0559         regex->value = var;
0560 
0561         return NGX_CONF_OK;
0562     }
0563 
0564 #endif
0565 
0566     if (value[0].len && value[0].data[0] == '\\') {
0567         value[0].len--;
0568         value[0].data++;
0569     }
0570 
0571     rv = ngx_hash_add_key(&ctx->keys, &value[0], var,
0572                           (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
0573 
0574     if (rv == NGX_OK) {
0575         return NGX_CONF_OK;
0576     }
0577 
0578     if (rv == NGX_DECLINED) {
0579         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0580                            "invalid hostname or wildcard \"%V\"", &value[0]);
0581     }
0582 
0583     if (rv == NGX_BUSY) {
0584         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0585                            "conflicting parameter \"%V\"", &value[0]);
0586     }
0587 
0588     return NGX_CONF_ERROR;
0589 }