diff src/http/modules/ngx_http_split_clients_module.c @ 570:8246d8a2c2be NGINX_0_8_37

nginx 0.8.37 *) Feature: the ngx_http_split_clients_module. *) Feature: the "map" directive supports keys more than 255 characters. *) Bugfix: nginx ignored the "private" and "no-store" values in the "Cache-Control" backend response header line. *) Bugfix: a "stub" parameter of an "include" SSI directive was not used, if empty response has 200 status code. *) Bugfix: if a proxied or FastCGI request was internally redirected to another proxied or FastCGI location, then a segmentation fault might occur in a worker process; the bug had appeared in 0.8.33. Thanks to Yichun Zhang. *) Bugfix: IMAP connections may hang until they timed out while talking to Zimbra server. Thanks to Alan Batie.
author Igor Sysoev <http://sysoev.ru>
date Mon, 17 May 2010 00:00:00 +0400
parents
children bc110f60c0de
line wrap: on
line diff
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;
+}