3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
14 ngx_http_variable_value_t value
;
15 } ngx_http_split_clients_part_t
;
19 ngx_http_complex_value_t value
;
21 } ngx_http_split_clients_ctx_t
;
24 static char *ngx_conf_split_clients_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
26 static char *ngx_http_split_clients(ngx_conf_t
*cf
, ngx_command_t
*dummy
,
29 static ngx_command_t ngx_http_split_clients_commands
[] = {
31 { ngx_string("split_clients"),
32 NGX_HTTP_MAIN_CONF
|NGX_CONF_BLOCK
|NGX_CONF_TAKE2
,
33 ngx_conf_split_clients_block
,
34 NGX_HTTP_MAIN_CONF_OFFSET
,
42 static ngx_http_module_t ngx_http_split_clients_module_ctx
= {
43 NULL
, /* preconfiguration */
44 NULL
, /* postconfiguration */
46 NULL
, /* create main configuration */
47 NULL
, /* init main configuration */
49 NULL
, /* create server configuration */
50 NULL
, /* merge server configuration */
52 NULL
, /* create location configuration */
53 NULL
/* merge location configuration */
57 ngx_module_t ngx_http_split_clients_module
= {
59 &ngx_http_split_clients_module_ctx
, /* module context */
60 ngx_http_split_clients_commands
, /* module directives */
61 NGX_HTTP_MODULE
, /* module type */
62 NULL
, /* init master */
63 NULL
, /* init module */
64 NULL
, /* init process */
65 NULL
, /* init thread */
66 NULL
, /* exit thread */
67 NULL
, /* exit process */
68 NULL
, /* exit master */
74 ngx_http_split_clients_variable(ngx_http_request_t
*r
,
75 ngx_http_variable_value_t
*v
, uintptr_t data
)
77 ngx_http_split_clients_ctx_t
*ctx
= (ngx_http_split_clients_ctx_t
*) data
;
82 ngx_http_split_clients_part_t
*part
;
84 *v
= ngx_http_variable_null_value
;
86 if (ngx_http_complex_value(r
, &ctx
->value
, &val
) != NGX_OK
) {
90 hash
= ngx_murmur_hash2(val
.data
, val
.len
);
92 part
= ctx
->parts
.elts
;
94 for (i
= 0; i
< ctx
->parts
.nelts
; i
++) {
96 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
97 "http split: %uD %uD", hash
, part
[i
].percent
);
99 if (hash
< part
[i
].percent
) {
110 ngx_conf_split_clients_block(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
113 ngx_str_t
*value
, name
;
114 ngx_uint_t i
, sum
, last
;
116 ngx_http_variable_t
*var
;
117 ngx_http_split_clients_ctx_t
*ctx
;
118 ngx_http_split_clients_part_t
*part
;
119 ngx_http_compile_complex_value_t ccv
;
121 ctx
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_split_clients_ctx_t
));
123 return NGX_CONF_ERROR
;
126 value
= cf
->args
->elts
;
128 ngx_memzero(&ccv
, sizeof(ngx_http_compile_complex_value_t
));
131 ccv
.value
= &value
[1];
132 ccv
.complex_value
= &ctx
->value
;
134 if (ngx_http_compile_complex_value(&ccv
) != NGX_OK
) {
135 return NGX_CONF_ERROR
;
142 var
= ngx_http_add_variable(cf
, &name
, NGX_HTTP_VAR_CHANGEABLE
);
144 return NGX_CONF_ERROR
;
147 var
->get_handler
= ngx_http_split_clients_variable
;
148 var
->data
= (uintptr_t) ctx
;
150 if (ngx_array_init(&ctx
->parts
, cf
->pool
, 2,
151 sizeof(ngx_http_split_clients_part_t
))
154 return NGX_CONF_ERROR
;
159 cf
->handler
= ngx_http_split_clients
;
160 cf
->handler_conf
= conf
;
162 rv
= ngx_conf_parse(cf
, NULL
);
166 if (rv
!= NGX_CONF_OK
) {
172 part
= ctx
->parts
.elts
;
174 for (i
= 0; i
< ctx
->parts
.nelts
; i
++) {
175 sum
= part
[i
].percent
? sum
+ part
[i
].percent
: 10000;
177 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
178 "percent sum is more than 100%%");
179 return NGX_CONF_ERROR
;
182 if (part
[i
].percent
) {
183 part
[i
].percent
= (uint32_t)
184 (last
+ 0xffffffff / 10000 * part
[i
].percent
);
186 part
[i
].percent
= 0xffffffff;
189 last
= part
[i
].percent
;
197 ngx_http_split_clients(ngx_conf_t
*cf
, ngx_command_t
*dummy
, void *conf
)
201 ngx_http_split_clients_ctx_t
*ctx
;
202 ngx_http_split_clients_part_t
*part
;
205 value
= cf
->args
->elts
;
207 part
= ngx_array_push(&ctx
->parts
);
209 return NGX_CONF_ERROR
;
212 if (value
[0].len
== 1 && value
[0].data
[0] == '*') {
216 if (value
[0].data
[value
[0].len
- 1] != '%') {
220 n
= ngx_atofp(value
[0].data
, value
[0].len
- 1, 2);
221 if (n
== NGX_ERROR
|| n
== 0) {
225 part
->percent
= (uint32_t) n
;
228 part
->value
.len
= value
[1].len
;
229 part
->value
.valid
= 1;
230 part
->value
.no_cacheable
= 0;
231 part
->value
.not_found
= 0;
232 part
->value
.data
= value
[1].data
;
238 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
239 "invalid percent value \"%V\"", &value
[0]);
240 return NGX_CONF_ERROR
;