src/http/modules/ngx_http_split_clients_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.     uint32_t                    percent;
  10.     ngx_http_variable_value_t   value;
  11. } ngx_http_split_clients_part_t;


  12. typedef struct {
  13.     ngx_http_complex_value_t    value;
  14.     ngx_array_t                 parts;
  15. } ngx_http_split_clients_ctx_t;


  16. static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
  17.     void *conf);
  18. static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
  19.     void *conf);

  20. static ngx_command_t  ngx_http_split_clients_commands[] = {

  21.     { ngx_string("split_clients"),
  22.       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
  23.       ngx_conf_split_clients_block,
  24.       NGX_HTTP_MAIN_CONF_OFFSET,
  25.       0,
  26.       NULL },

  27.       ngx_null_command
  28. };


  29. static ngx_http_module_t  ngx_http_split_clients_module_ctx = {
  30.     NULL,                                  /* preconfiguration */
  31.     NULL,                                  /* postconfiguration */

  32.     NULL,                                  /* create main configuration */
  33.     NULL,                                  /* init main configuration */

  34.     NULL,                                  /* create server configuration */
  35.     NULL,                                  /* merge server configuration */

  36.     NULL,                                  /* create location configuration */
  37.     NULL                                   /* merge location configuration */
  38. };


  39. ngx_module_t  ngx_http_split_clients_module = {
  40.     NGX_MODULE_V1,
  41.     &ngx_http_split_clients_module_ctx,    /* module context */
  42.     ngx_http_split_clients_commands,       /* module directives */
  43.     NGX_HTTP_MODULE,                       /* module type */
  44.     NULL,                                  /* init master */
  45.     NULL,                                  /* init module */
  46.     NULL,                                  /* init process */
  47.     NULL,                                  /* init thread */
  48.     NULL,                                  /* exit thread */
  49.     NULL,                                  /* exit process */
  50.     NULL,                                  /* exit master */
  51.     NGX_MODULE_V1_PADDING
  52. };


  53. static ngx_int_t
  54. ngx_http_split_clients_variable(ngx_http_request_t *r,
  55.     ngx_http_variable_value_t *v, uintptr_t data)
  56. {
  57.     ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;

  58.     uint32_t                        hash;
  59.     ngx_str_t                       val;
  60.     ngx_uint_t                      i;
  61.     ngx_http_split_clients_part_t  *part;

  62.     *v = ngx_http_variable_null_value;

  63.     if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
  64.         return NGX_OK;
  65.     }

  66.     hash = ngx_murmur_hash2(val.data, val.len);

  67.     part = ctx->parts.elts;

  68.     for (i = 0; i < ctx->parts.nelts; i++) {

  69.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  70.                        "http split: %uD %uD", hash, part[i].percent);

  71.         if (hash < part[i].percent || part[i].percent == 0) {
  72.             *v = part[i].value;
  73.             return NGX_OK;
  74.         }
  75.     }

  76.     return NGX_OK;
  77. }


  78. static char *
  79. ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  80. {
  81.     char                                *rv;
  82.     uint32_t                             sum, last;
  83.     ngx_str_t                           *value, name;
  84.     ngx_uint_t                           i;
  85.     ngx_conf_t                           save;
  86.     ngx_http_variable_t                 *var;
  87.     ngx_http_split_clients_ctx_t        *ctx;
  88.     ngx_http_split_clients_part_t       *part;
  89.     ngx_http_compile_complex_value_t     ccv;

  90.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));
  91.     if (ctx == NULL) {
  92.         return NGX_CONF_ERROR;
  93.     }

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

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

  96.     ccv.cf = cf;
  97.     ccv.value = &value[1];
  98.     ccv.complex_value = &ctx->value;

  99.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  100.         return NGX_CONF_ERROR;
  101.     }

  102.     name = value[2];

  103.     if (name.data[0] != '$') {
  104.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  105.                            "invalid variable name \"%V\"", &name);
  106.         return NGX_CONF_ERROR;
  107.     }

  108.     name.len--;
  109.     name.data++;

  110.     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
  111.     if (var == NULL) {
  112.         return NGX_CONF_ERROR;
  113.     }

  114.     var->get_handler = ngx_http_split_clients_variable;
  115.     var->data = (uintptr_t) ctx;

  116.     if (ngx_array_init(&ctx->parts, cf->pool, 2,
  117.                        sizeof(ngx_http_split_clients_part_t))
  118.         != NGX_OK)
  119.     {
  120.         return NGX_CONF_ERROR;
  121.     }

  122.     save = *cf;
  123.     cf->ctx = ctx;
  124.     cf->handler = ngx_http_split_clients;
  125.     cf->handler_conf = conf;

  126.     rv = ngx_conf_parse(cf, NULL);

  127.     *cf = save;

  128.     if (rv != NGX_CONF_OK) {
  129.         return rv;
  130.     }

  131.     sum = 0;
  132.     last = 0;
  133.     part = ctx->parts.elts;

  134.     for (i = 0; i < ctx->parts.nelts; i++) {
  135.         sum = part[i].percent ? sum + part[i].percent : 10000;
  136.         if (sum > 10000) {
  137.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  138.                                "percent total is greater than 100%%");
  139.             return NGX_CONF_ERROR;
  140.         }

  141.         if (part[i].percent) {
  142.             last += part[i].percent * (uint64_t) 0xffffffff / 10000;
  143.             part[i].percent = last;
  144.         }
  145.     }

  146.     return rv;
  147. }


  148. static char *
  149. ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  150. {
  151.     ngx_int_t                       n;
  152.     ngx_str_t                      *value;
  153.     ngx_http_split_clients_ctx_t   *ctx;
  154.     ngx_http_split_clients_part_t  *part;

  155.     ctx = cf->ctx;
  156.     value = cf->args->elts;

  157.     part = ngx_array_push(&ctx->parts);
  158.     if (part == NULL) {
  159.         return NGX_CONF_ERROR;
  160.     }

  161.     if (value[0].len == 1 && value[0].data[0] == '*') {
  162.         part->percent = 0;

  163.     } else {
  164.         if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {
  165.             goto invalid;
  166.         }

  167.         n = ngx_atofp(value[0].data, value[0].len - 1, 2);
  168.         if (n == NGX_ERROR || n == 0) {
  169.             goto invalid;
  170.         }

  171.         part->percent = (uint32_t) n;
  172.     }

  173.     part->value.len = value[1].len;
  174.     part->value.valid = 1;
  175.     part->value.no_cacheable = 0;
  176.     part->value.not_found = 0;
  177.     part->value.data = value[1].data;

  178.     return NGX_CONF_OK;

  179. invalid:

  180.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  181.                        "invalid percent value \"%V\"", &value[0]);
  182.     return NGX_CONF_ERROR;
  183. }