changeset 3513:e60e00b4fe0c

ngx_http_split_clients_module
author Igor Sysoev <igor@sysoev.ru>
date Fri, 14 May 2010 09:02:10 +0000
parents 098100f5f54b
children 58865853f29b
files auto/modules auto/options auto/sources src/http/modules/ngx_http_split_clients_module.c
diffstat 4 files changed, 249 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules
+++ b/auto/modules
@@ -245,6 +245,11 @@ if [ $HTTP_MAP = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_MAP_SRCS"
 fi
 
+if [ $HTTP_SPLIT_CLIENTS = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_SPLIT_CLIENTS_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_SPLIT_CLIENTS_SRCS"
+fi
+
 if [ $HTTP_REFERER = YES ]; then
     HTTP_MODULES="$HTTP_MODULES $HTTP_REFERER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_REFERER_SRCS"
--- a/auto/options
+++ b/auto/options
@@ -74,6 +74,7 @@ HTTP_STATUS=NO
 HTTP_GEO=YES
 HTTP_GEOIP=NO
 HTTP_MAP=YES
+HTTP_SPLIT_CLIENTS=YES
 HTTP_REFERER=YES
 HTTP_REWRITE=YES
 HTTP_PROXY=YES
@@ -209,6 +210,7 @@ do
         --without-http_status_module)    HTTP_STATUS=NO             ;;
         --without-http_geo_module)       HTTP_GEO=NO                ;;
         --without-http_map_module)       HTTP_MAP=NO                ;;
+        --without-http_split_clients_module) HTTP_SPLIT_CLIENTS=NO  ;;
         --without-http_referer_module)   HTTP_REFERER=NO            ;;
         --without-http_rewrite_module)   HTTP_REWRITE=NO            ;;
         --without-http_proxy_module)     HTTP_PROXY=NO              ;;
@@ -341,6 +343,7 @@ cat << END
   --without-http_autoindex_module    disable ngx_http_autoindex_module
   --without-http_geo_module          disable ngx_http_geo_module
   --without-http_map_module          disable ngx_http_map_module
+  --without-http_split_clients_module disable ngx_http_split_clients_module
   --without-http_referer_module      disable ngx_http_referer_module
   --without-http_rewrite_module      disable ngx_http_rewrite_module
   --without-http_proxy_module        disable ngx_http_proxy_module
--- a/auto/sources
+++ b/auto/sources
@@ -387,6 +387,10 @@ HTTP_MAP_MODULE=ngx_http_map_module
 HTTP_MAP_SRCS=src/http/modules/ngx_http_map_module.c
 
 
+HTTP_SPLIT_CLIENTS_MODULE=ngx_http_split_clients_module
+HTTP_SPLIT_CLIENTS_SRCS=src/http/modules/ngx_http_split_clients_module.c
+
+
 HTTP_REFERER_MODULE=ngx_http_referer_module
 HTTP_REFERER_SRCS=src/http/modules/ngx_http_referer_module.c
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_split_clients_module.c
@@ -0,0 +1,237 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    uint32_t                    percent;
+    ngx_http_variable_value_t   value;
+} ngx_http_split_clients_part_t;
+
+
+typedef struct {
+    ngx_http_complex_value_t    value;
+    ngx_array_t                 parts;
+} ngx_http_split_clients_ctx_t;
+
+
+static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
+    void *conf);
+
+static ngx_command_t  ngx_http_split_clients_commands[] = {
+
+    { ngx_string("split_clients"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
+      ngx_conf_split_clients_block,
+      NGX_HTTP_MAIN_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_split_clients_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    NULL,                                  /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    NULL,                                  /* create location configuration */
+    NULL                                   /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_split_clients_module = {
+    NGX_MODULE_V1,
+    &ngx_http_split_clients_module_ctx,    /* module context */
+    ngx_http_split_clients_commands,       /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_split_clients_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;
+
+    uint32_t                        hash;
+    ngx_str_t                       val;
+    ngx_uint_t                      i;
+    ngx_http_split_clients_part_t  *part;
+
+    *v = ngx_http_variable_null_value;
+
+    if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
+        return NGX_OK;
+    }
+
+    hash = ngx_crc32_short(val.data, val.len);
+
+    part = ctx->parts.elts;
+
+    for (i = 0; i < ctx->parts.nelts; i++) {
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "%D %D", hash, part[i].percent);
+
+        if (hash < part[i].percent) {
+            *v = part[i].value;
+            return NGX_OK;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static char *
+ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char                                *rv;
+    ngx_str_t                           *value, name;
+    ngx_uint_t                           i, sum, last;
+    ngx_conf_t                           save;
+    ngx_http_variable_t                 *var;
+    ngx_http_split_clients_ctx_t        *ctx;
+    ngx_http_split_clients_part_t       *part;
+    ngx_http_compile_complex_value_t     ccv;
+
+    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));
+    if (ctx == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = &ctx->value;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    name = value[2];
+    name.len--;
+    name.data++;
+
+    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+    if (var == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    var->get_handler = ngx_http_split_clients_variable;
+    var->data = (uintptr_t) ctx;
+
+    if (ngx_array_init(&ctx->parts, cf->pool, 2,
+                       sizeof(ngx_http_split_clients_part_t))
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    save = *cf;
+    cf->ctx = ctx;
+    cf->handler = ngx_http_split_clients;
+    cf->handler_conf = conf;
+
+    rv = ngx_conf_parse(cf, NULL);
+
+    *cf = save;
+
+    sum = 0;
+    last = 0;
+    part = ctx->parts.elts;
+
+    for (i = 0; i < ctx->parts.nelts; i++) {
+        sum = part[i].percent ? sum + part[i].percent : 10000;
+        if (sum > 10000) {
+           ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                              "percent sum is more than 100%%");
+           return NGX_CONF_ERROR;
+        }
+
+        if (part[i].percent) {
+            part[i].percent = (uint32_t)
+                                 (last + 0xffffffff / 10000 * part[i].percent);
+        } else {
+            part[i].percent = 0xffffffff;
+        }
+
+        last = part[i].percent;
+    }
+
+    return rv;
+}
+
+
+static char *
+ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+    ngx_int_t                       n;
+    ngx_str_t                      *value;
+    ngx_http_split_clients_ctx_t   *ctx;
+    ngx_http_split_clients_part_t  *part;
+
+    ctx = cf->ctx;
+    value = cf->args->elts;
+
+    part = ngx_array_push(&ctx->parts);
+    if (part == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (value[0].len == 1 && value[0].data[0] == '*') {
+        part->percent = 0;
+
+    } else {
+        if (value[0].data[value[0].len - 1] != '%') {
+            goto invalid;
+        }
+
+        n = ngx_atofp(value[0].data, value[0].len - 1, 2);
+        if (n == NGX_ERROR || n == 0) {
+            goto invalid;
+        }
+
+        part->percent = (uint32_t) n;
+    }
+
+    part->value.len = value[1].len;
+    part->value.valid = 1;
+    part->value.no_cacheable = 0;
+    part->value.not_found = 0;
+    part->value.data = value[1].data;
+
+    return NGX_CONF_OK;
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid percent value \"%V\"", &value[0]);
+    return NGX_CONF_ERROR;
+}