src/http/modules/ngx_http_map_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_uint_t                  hash_max_size;
  10.     ngx_uint_t                  hash_bucket_size;
  11. } ngx_http_map_conf_t;


  12. typedef struct {
  13.     ngx_hash_keys_arrays_t      keys;

  14.     ngx_array_t                *values_hash;
  15.     ngx_array_t                 var_values;
  16. #if (NGX_PCRE)
  17.     ngx_array_t                 regexes;
  18. #endif

  19.     ngx_http_variable_value_t  *default_value;
  20.     ngx_conf_t                 *cf;
  21.     ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */
  22. } ngx_http_map_conf_ctx_t;


  23. typedef struct {
  24.     ngx_http_map_t              map;
  25.     ngx_http_complex_value_t    value;
  26.     ngx_http_variable_value_t  *default_value;
  27.     ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */
  28. } ngx_http_map_ctx_t;


  29. static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
  30.     const void *two);
  31. static void *ngx_http_map_create_conf(ngx_conf_t *cf);
  32. static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  33. static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);


  34. static ngx_command_t  ngx_http_map_commands[] = {

  35.     { ngx_string("map"),
  36.       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
  37.       ngx_http_map_block,
  38.       NGX_HTTP_MAIN_CONF_OFFSET,
  39.       0,
  40.       NULL },

  41.     { ngx_string("map_hash_max_size"),
  42.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
  43.       ngx_conf_set_num_slot,
  44.       NGX_HTTP_MAIN_CONF_OFFSET,
  45.       offsetof(ngx_http_map_conf_t, hash_max_size),
  46.       NULL },

  47.     { ngx_string("map_hash_bucket_size"),
  48.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
  49.       ngx_conf_set_num_slot,
  50.       NGX_HTTP_MAIN_CONF_OFFSET,
  51.       offsetof(ngx_http_map_conf_t, hash_bucket_size),
  52.       NULL },

  53.       ngx_null_command
  54. };


  55. static ngx_http_module_t  ngx_http_map_module_ctx = {
  56.     NULL,                                  /* preconfiguration */
  57.     NULL,                                  /* postconfiguration */

  58.     ngx_http_map_create_conf,              /* create main configuration */
  59.     NULL,                                  /* init main configuration */

  60.     NULL,                                  /* create server configuration */
  61.     NULL,                                  /* merge server configuration */

  62.     NULL,                                  /* create location configuration */
  63.     NULL                                   /* merge location configuration */
  64. };


  65. ngx_module_t  ngx_http_map_module = {
  66.     NGX_MODULE_V1,
  67.     &ngx_http_map_module_ctx,              /* module context */
  68.     ngx_http_map_commands,                 /* module directives */
  69.     NGX_HTTP_MODULE,                       /* module type */
  70.     NULL,                                  /* init master */
  71.     NULL,                                  /* init module */
  72.     NULL,                                  /* init process */
  73.     NULL,                                  /* init thread */
  74.     NULL,                                  /* exit thread */
  75.     NULL,                                  /* exit process */
  76.     NULL,                                  /* exit master */
  77.     NGX_MODULE_V1_PADDING
  78. };


  79. static ngx_int_t
  80. ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  81.     uintptr_t data)
  82. {
  83.     ngx_http_map_ctx_t  *map = (ngx_http_map_ctx_t *) data;

  84.     ngx_str_t                   val;
  85.     ngx_http_variable_value_t  *value;

  86.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  87.                    "http map started");

  88.     if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
  89.         return NGX_ERROR;
  90.     }

  91.     if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
  92.         val.len--;
  93.     }

  94.     value = ngx_http_map_find(r, &map->map, &val);

  95.     if (value == NULL) {
  96.         value = map->default_value;
  97.     }

  98.     if (!value->valid) {
  99.         value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data);

  100.         if (value == NULL || value->not_found) {
  101.             value = &ngx_http_variable_null_value;
  102.         }
  103.     }

  104.     *v = *value;

  105.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  106.                    "http map: \"%v\" \"%v\"", &val, v);

  107.     return NGX_OK;
  108. }


  109. static void *
  110. ngx_http_map_create_conf(ngx_conf_t *cf)
  111. {
  112.     ngx_http_map_conf_t  *mcf;

  113.     mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
  114.     if (mcf == NULL) {
  115.         return NULL;
  116.     }

  117.     mcf->hash_max_size = NGX_CONF_UNSET_UINT;
  118.     mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;

  119.     return mcf;
  120. }


  121. static char *
  122. ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  123. {
  124.     ngx_http_map_conf_t  *mcf = conf;

  125.     char                              *rv;
  126.     ngx_str_t                         *value, name;
  127.     ngx_conf_t                         save;
  128.     ngx_pool_t                        *pool;
  129.     ngx_hash_init_t                    hash;
  130.     ngx_http_map_ctx_t                *map;
  131.     ngx_http_variable_t               *var;
  132.     ngx_http_map_conf_ctx_t            ctx;
  133.     ngx_http_compile_complex_value_t   ccv;

  134.     if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
  135.         mcf->hash_max_size = 2048;
  136.     }

  137.     if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
  138.         mcf->hash_bucket_size = ngx_cacheline_size;

  139.     } else {
  140.         mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
  141.                                           ngx_cacheline_size);
  142.     }

  143.     map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
  144.     if (map == NULL) {
  145.         return NGX_CONF_ERROR;
  146.     }

  147.     value = cf->args->elts;

  148.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  149.     ccv.cf = cf;
  150.     ccv.value = &value[1];
  151.     ccv.complex_value = &map->value;

  152.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  153.         return NGX_CONF_ERROR;
  154.     }

  155.     name = value[2];

  156.     if (name.data[0] != '$') {
  157.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  158.                            "invalid variable name \"%V\"", &name);
  159.         return NGX_CONF_ERROR;
  160.     }

  161.     name.len--;
  162.     name.data++;

  163.     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
  164.     if (var == NULL) {
  165.         return NGX_CONF_ERROR;
  166.     }

  167.     var->get_handler = ngx_http_map_variable;
  168.     var->data = (uintptr_t) map;

  169.     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  170.     if (pool == NULL) {
  171.         return NGX_CONF_ERROR;
  172.     }

  173.     ctx.keys.pool = cf->pool;
  174.     ctx.keys.temp_pool = pool;

  175.     if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
  176.         ngx_destroy_pool(pool);
  177.         return NGX_CONF_ERROR;
  178.     }

  179.     ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
  180.     if (ctx.values_hash == NULL) {
  181.         ngx_destroy_pool(pool);
  182.         return NGX_CONF_ERROR;
  183.     }

  184.     if (ngx_array_init(&ctx.var_values, cf->pool, 2,
  185.                        sizeof(ngx_http_variable_value_t))
  186.         != NGX_OK)
  187.     {
  188.         ngx_destroy_pool(pool);
  189.         return NGX_CONF_ERROR;
  190.     }

  191. #if (NGX_PCRE)
  192.     if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
  193.         != NGX_OK)
  194.     {
  195.         ngx_destroy_pool(pool);
  196.         return NGX_CONF_ERROR;
  197.     }
  198. #endif

  199.     ctx.default_value = NULL;
  200.     ctx.cf = &save;
  201.     ctx.hostnames = 0;

  202.     save = *cf;
  203.     cf->pool = pool;
  204.     cf->ctx = &ctx;
  205.     cf->handler = ngx_http_map;
  206.     cf->handler_conf = conf;

  207.     rv = ngx_conf_parse(cf, NULL);

  208.     *cf = save;

  209.     if (rv != NGX_CONF_OK) {
  210.         ngx_destroy_pool(pool);
  211.         return rv;
  212.     }

  213.     map->default_value = ctx.default_value ? ctx.default_value:
  214.                                              &ngx_http_variable_null_value;

  215.     map->hostnames = ctx.hostnames;

  216.     hash.key = ngx_hash_key_lc;
  217.     hash.max_size = mcf->hash_max_size;
  218.     hash.bucket_size = mcf->hash_bucket_size;
  219.     hash.name = "map_hash";
  220.     hash.pool = cf->pool;

  221.     if (ctx.keys.keys.nelts) {
  222.         hash.hash = &map->map.hash.hash;
  223.         hash.temp_pool = NULL;

  224.         if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
  225.             != NGX_OK)
  226.         {
  227.             ngx_destroy_pool(pool);
  228.             return NGX_CONF_ERROR;
  229.         }
  230.     }

  231.     if (ctx.keys.dns_wc_head.nelts) {

  232.         ngx_qsort(ctx.keys.dns_wc_head.elts,
  233.                   (size_t) ctx.keys.dns_wc_head.nelts,
  234.                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);

  235.         hash.hash = NULL;
  236.         hash.temp_pool = pool;

  237.         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
  238.                                    ctx.keys.dns_wc_head.nelts)
  239.             != NGX_OK)
  240.         {
  241.             ngx_destroy_pool(pool);
  242.             return NGX_CONF_ERROR;
  243.         }

  244.         map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
  245.     }

  246.     if (ctx.keys.dns_wc_tail.nelts) {

  247.         ngx_qsort(ctx.keys.dns_wc_tail.elts,
  248.                   (size_t) ctx.keys.dns_wc_tail.nelts,
  249.                   sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);

  250.         hash.hash = NULL;
  251.         hash.temp_pool = pool;

  252.         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
  253.                                    ctx.keys.dns_wc_tail.nelts)
  254.             != NGX_OK)
  255.         {
  256.             ngx_destroy_pool(pool);
  257.             return NGX_CONF_ERROR;
  258.         }

  259.         map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
  260.     }

  261. #if (NGX_PCRE)

  262.     if (ctx.regexes.nelts) {
  263.         map->map.regex = ctx.regexes.elts;
  264.         map->map.nregex = ctx.regexes.nelts;
  265.     }

  266. #endif

  267.     ngx_destroy_pool(pool);

  268.     return rv;
  269. }


  270. static int ngx_libc_cdecl
  271. ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
  272. {
  273.     ngx_hash_key_t  *first, *second;

  274.     first = (ngx_hash_key_t *) one;
  275.     second = (ngx_hash_key_t *) two;

  276.     return ngx_dns_strcmp(first->key.data, second->key.data);
  277. }


  278. static char *
  279. ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  280. {
  281.     ngx_int_t                   rc, index;
  282.     ngx_str_t                  *value, name;
  283.     ngx_uint_t                  i, key;
  284.     ngx_http_map_conf_ctx_t    *ctx;
  285.     ngx_http_variable_value_t  *var, **vp;

  286.     ctx = cf->ctx;

  287.     value = cf->args->elts;

  288.     if (cf->args->nelts == 1
  289.         && ngx_strcmp(value[0].data, "hostnames") == 0)
  290.     {
  291.         ctx->hostnames = 1;
  292.         return NGX_CONF_OK;

  293.     } else if (cf->args->nelts != 2) {
  294.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  295.                            "invalid number of the map parameters");
  296.         return NGX_CONF_ERROR;
  297.     }

  298.     if (ngx_strcmp(value[0].data, "include") == 0) {
  299.         return ngx_conf_include(cf, dummy, conf);
  300.     }

  301.     if (value[1].data[0] == '$') {
  302.         name = value[1];
  303.         name.len--;
  304.         name.data++;

  305.         index = ngx_http_get_variable_index(ctx->cf, &name);
  306.         if (index == NGX_ERROR) {
  307.             return NGX_CONF_ERROR;
  308.         }

  309.         var = ctx->var_values.elts;

  310.         for (i = 0; i < ctx->var_values.nelts; i++) {
  311.             if (index == (intptr_t) var[i].data) {
  312.                 var = &var[i];
  313.                 goto found;
  314.             }
  315.         }

  316.         var = ngx_array_push(&ctx->var_values);
  317.         if (var == NULL) {
  318.             return NGX_CONF_ERROR;
  319.         }

  320.         var->valid = 0;
  321.         var->no_cacheable = 0;
  322.         var->not_found = 0;
  323.         var->len = 0;
  324.         var->data = (u_char *) (intptr_t) index;

  325.         goto found;
  326.     }

  327.     key = 0;

  328.     for (i = 0; i < value[1].len; i++) {
  329.         key = ngx_hash(key, value[1].data[i]);
  330.     }

  331.     key %= ctx->keys.hsize;

  332.     vp = ctx->values_hash[key].elts;

  333.     if (vp) {
  334.         for (i = 0; i < ctx->values_hash[key].nelts; i++) {
  335.             if (value[1].len != (size_t) vp[i]->len) {
  336.                 continue;
  337.             }

  338.             if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {
  339.                 var = vp[i];
  340.                 goto found;
  341.             }
  342.         }

  343.     } else {
  344.         if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
  345.                            sizeof(ngx_http_variable_value_t *))
  346.             != NGX_OK)
  347.         {
  348.             return NGX_CONF_ERROR;
  349.         }
  350.     }

  351.     var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
  352.     if (var == NULL) {
  353.         return NGX_CONF_ERROR;
  354.     }

  355.     var->len = value[1].len;
  356.     var->data = ngx_pstrdup(ctx->keys.pool, &value[1]);
  357.     if (var->data == NULL) {
  358.         return NGX_CONF_ERROR;
  359.     }

  360.     var->valid = 1;
  361.     var->no_cacheable = 0;
  362.     var->not_found = 0;

  363.     vp = ngx_array_push(&ctx->values_hash[key]);
  364.     if (vp == NULL) {
  365.         return NGX_CONF_ERROR;
  366.     }

  367.     *vp = var;

  368. found:

  369.     if (ngx_strcmp(value[0].data, "default") == 0) {

  370.         if (ctx->default_value) {
  371.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  372.                                "duplicate default map parameter");
  373.             return NGX_CONF_ERROR;
  374.         }

  375.         ctx->default_value = var;

  376.         return NGX_CONF_OK;
  377.     }

  378. #if (NGX_PCRE)

  379.     if (value[0].len && value[0].data[0] == '~') {
  380.         ngx_regex_compile_t    rc;
  381.         ngx_http_map_regex_t  *regex;
  382.         u_char                 errstr[NGX_MAX_CONF_ERRSTR];

  383.         regex = ngx_array_push(&ctx->regexes);
  384.         if (regex == NULL) {
  385.             return NGX_CONF_ERROR;
  386.         }

  387.         value[0].len--;
  388.         value[0].data++;

  389.         ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

  390.         if (value[0].data[0] == '*') {
  391.             value[0].len--;
  392.             value[0].data++;
  393.             rc.options = NGX_REGEX_CASELESS;
  394.         }

  395.         rc.pattern = value[0];
  396.         rc.err.len = NGX_MAX_CONF_ERRSTR;
  397.         rc.err.data = errstr;

  398.         regex->regex = ngx_http_regex_compile(ctx->cf, &rc);
  399.         if (regex->regex == NULL) {
  400.             return NGX_CONF_ERROR;
  401.         }

  402.         regex->value = var;

  403.         return NGX_CONF_OK;
  404.     }

  405. #endif

  406.     if (value[0].len && value[0].data[0] == '\\') {
  407.         value[0].len--;
  408.         value[0].data++;
  409.     }

  410.     rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
  411.                           (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);

  412.     if (rc == NGX_OK) {
  413.         return NGX_CONF_OK;
  414.     }

  415.     if (rc == NGX_DECLINED) {
  416.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  417.                            "invalid hostname or wildcard \"%V\"", &value[0]);
  418.     }

  419.     if (rc == NGX_BUSY) {
  420.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  421.                            "conflicting parameter \"%V\"", &value[0]);
  422.     }

  423.     return NGX_CONF_ERROR;
  424. }