comparison src/http/modules/ngx_http_geo_module.c @ 422:88d3e895bdf9 NGINX_0_7_23

nginx 0.7.23 *) Feature: the "delete" and "ranges" parameters in the "geo" directive. *) Feature: speeding up loading of geo base with large number of values. *) Feature: decrease of memory required for geo base load.
author Igor Sysoev <http://sysoev.ru>
date Thu, 27 Nov 2008 00:00:00 +0300
parents 10cc350ed8a1
children 9da1d9d94d18
comparison
equal deleted inserted replaced
421:10e4013f5f54 422:88d3e895bdf9
8 #include <ngx_core.h> 8 #include <ngx_core.h>
9 #include <ngx_http.h> 9 #include <ngx_http.h>
10 10
11 11
12 typedef struct { 12 typedef struct {
13 ngx_radix_tree_t *tree; 13 u_short start;
14 ngx_pool_t *pool; 14 u_short end;
15 ngx_array_t values; 15 ngx_http_variable_value_t *value;
16 } ngx_http_geo_range_t;
17
18
19 typedef struct {
20 ngx_http_geo_range_t *ranges;
21 ngx_uint_t n;
22 } ngx_http_geo_low_ranges_t;
23
24
25 typedef struct {
26 ngx_http_geo_low_ranges_t low[0x10000];
27 ngx_http_variable_value_t *default_value;
28 } ngx_http_geo_high_ranges_t;
29
30
31 typedef struct {
32 ngx_http_variable_value_t *value;
33 ngx_str_t *net;
34 ngx_http_geo_high_ranges_t *high;
35 ngx_radix_tree_t *tree;
36 ngx_rbtree_t rbtree;
37 ngx_rbtree_node_t sentinel;
38 ngx_pool_t *pool;
39 ngx_pool_t *temp_pool;
16 } ngx_http_geo_conf_ctx_t; 40 } ngx_http_geo_conf_ctx_t;
17 41
18 42
19 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 43 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
20 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); 44 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
45 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
46 ngx_str_t *value);
47 static char *ngx_http_geo_add_range(ngx_conf_t *cf,
48 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
49 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
50 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
51 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
52 ngx_str_t *value);
53 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
54 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
21 55
22 56
23 static ngx_command_t ngx_http_geo_commands[] = { 57 static ngx_command_t ngx_http_geo_commands[] = {
24 58
25 { ngx_string("geo"), 59 { ngx_string("geo"),
65 99
66 100
67 /* AF_INET only */ 101 /* AF_INET only */
68 102
69 static ngx_int_t 103 static ngx_int_t
70 ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 104 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
71 uintptr_t data) 105 uintptr_t data)
72 { 106 {
73 ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; 107 ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data;
74 108
75 struct sockaddr_in *sin; 109 struct sockaddr_in *sin;
76 ngx_http_variable_value_t *vv; 110 ngx_http_variable_value_t *vv;
77 111
78 sin = (struct sockaddr_in *) r->connection->sockaddr;
79
80 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 112 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
81 "http geo started"); 113 "http geo started");
82 114
115 sin = (struct sockaddr_in *) r->connection->sockaddr;
116
83 vv = (ngx_http_variable_value_t *) 117 vv = (ngx_http_variable_value_t *)
84 ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); 118 ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr));
85 119
86 *v = *vv; 120 *v = *vv;
87 121
90 124
91 return NGX_OK; 125 return NGX_OK;
92 } 126 }
93 127
94 128
129 static ngx_int_t
130 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
131 uintptr_t data)
132 {
133 ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data;
134
135 in_addr_t addr;
136 ngx_uint_t i, n;
137 struct sockaddr_in *sin;
138 ngx_http_geo_range_t *range;
139
140 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
141 "http geo started");
142
143 sin = (struct sockaddr_in *) r->connection->sockaddr;
144
145 *v = *high->default_value;
146
147 addr = ntohl(sin->sin_addr.s_addr);
148
149 range = high->low[addr >> 16].ranges;
150
151 n = addr & 0xffff;
152
153 for (i = 0; i < high->low[addr >> 16].n; i++) {
154 if (n >= (ngx_uint_t) range[i].start
155 && n <= (ngx_uint_t) range[i].end)
156 {
157 *v = *range[i].value;
158 }
159 }
160
161 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
162 "http geo: %V %v", &r->connection->addr_text, v);
163
164 return NGX_OK;
165 }
166
167
95 static char * 168 static char *
96 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 169 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
97 { 170 {
98 char *rv; 171 char *rv;
172 size_t len;
99 ngx_str_t *value, name; 173 ngx_str_t *value, name;
174 ngx_uint_t i;
100 ngx_conf_t save; 175 ngx_conf_t save;
101 ngx_pool_t *pool; 176 ngx_pool_t *pool;
102 ngx_radix_tree_t *tree; 177 ngx_array_t *a;
103 ngx_http_variable_t *var; 178 ngx_http_variable_t *var;
104 ngx_http_geo_conf_ctx_t ctx; 179 ngx_http_geo_conf_ctx_t ctx;
105 180
106 value = cf->args->elts; 181 value = cf->args->elts;
107 182
119 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); 194 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
120 if (var == NULL) { 195 if (var == NULL) {
121 return NGX_CONF_ERROR; 196 return NGX_CONF_ERROR;
122 } 197 }
123 198
124 tree = ngx_radix_tree_create(cf->pool, -1);
125
126 if (tree == NULL) {
127 return NGX_CONF_ERROR;
128 }
129
130 var->get_handler = ngx_http_geo_variable;
131 var->data = (uintptr_t) tree;
132
133 pool = ngx_create_pool(16384, cf->log); 199 pool = ngx_create_pool(16384, cf->log);
134 if (pool == NULL) { 200 if (pool == NULL) {
135 return NGX_CONF_ERROR; 201 return NGX_CONF_ERROR;
136 } 202 }
137 203
138 if (ngx_array_init(&ctx.values, pool, 512, 204 ctx.temp_pool = ngx_create_pool(16384, cf->log);
139 sizeof(ngx_http_variable_value_t *)) 205 if (ctx.temp_pool == NULL) {
140 != NGX_OK)
141 {
142 ngx_destroy_pool(pool);
143 return NGX_CONF_ERROR; 206 return NGX_CONF_ERROR;
144 } 207 }
145 208
146 ctx.tree = tree; 209 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
210 ngx_http_variable_value_rbtree_insert);
211
212 ctx.high = NULL;
213 ctx.tree = NULL;
147 ctx.pool = cf->pool; 214 ctx.pool = cf->pool;
148 215
149 save = *cf; 216 save = *cf;
150 cf->pool = pool; 217 cf->pool = pool;
151 cf->ctx = &ctx; 218 cf->ctx = &ctx;
154 221
155 rv = ngx_conf_parse(cf, NULL); 222 rv = ngx_conf_parse(cf, NULL);
156 223
157 *cf = save; 224 *cf = save;
158 225
159 ngx_destroy_pool(pool); 226 if (ctx.high) {
160 227
161 if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { 228 for (i = 0; i < 0x10000; i++) {
162 return rv; 229 a = (ngx_array_t *) ctx.high->low[i].ranges;
163 } 230
164 231 if (a == NULL || a->nelts == 0) {
165 if (ngx_radix32tree_insert(tree, 0, 0, 232 continue;
166 (uintptr_t) &ngx_http_variable_null_value) 233 }
167 == NGX_ERROR) 234
168 { 235 ctx.high->low[i].n = a->nelts;
169 return NGX_CONF_ERROR; 236
237 len = a->nelts * sizeof(ngx_http_geo_range_t);
238
239 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
240 if (ctx.high->low[i].ranges == NULL ){
241 return NGX_CONF_ERROR;
242 }
243
244 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
245 }
246
247 var->get_handler = ngx_http_geo_range_variable;
248 var->data = (uintptr_t) ctx.high;
249
250 ngx_destroy_pool(ctx.temp_pool);
251 ngx_destroy_pool(pool);
252
253 if (ctx.high->default_value == NULL) {
254 ctx.high->default_value = &ngx_http_variable_null_value;
255 }
256
257 } else {
258 var->get_handler = ngx_http_geo_cidr_variable;
259 var->data = (uintptr_t) ctx.tree;
260
261 ngx_destroy_pool(ctx.temp_pool);
262 ngx_destroy_pool(pool);
263
264 if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
265 return rv;
266 }
267
268 if (ngx_radix32tree_insert(ctx.tree, 0, 0,
269 (uintptr_t) &ngx_http_variable_null_value)
270 == NGX_ERROR)
271 {
272 return NGX_CONF_ERROR;
273 }
170 } 274 }
171 275
172 return rv; 276 return rv;
173 } 277 }
174 278
175
176 /* AF_INET only */
177 279
178 static char * 280 static char *
179 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 281 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
180 { 282 {
181 ngx_int_t rc; 283 char *rv;
182 ngx_str_t *value, file; 284 ngx_str_t *value, file;
183 ngx_uint_t i; 285 ngx_http_geo_conf_ctx_t *ctx;
184 ngx_inet_cidr_t cidrin;
185 ngx_http_geo_conf_ctx_t *ctx;
186 ngx_http_variable_value_t *var, *old, **v;
187 286
188 ctx = cf->ctx; 287 ctx = cf->ctx;
288
289 value = cf->args->elts;
290
291 if (cf->args->nelts == 1) {
292
293 if (ngx_strcmp(value[0].data, "ranges") == 0) {
294
295 if (ctx->tree) {
296 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
297 "the \"ranges\" directive must be "
298 "the first directive inside \"geo\" block");
299 goto failed;
300 }
301
302 ctx->high = ngx_pcalloc(ctx->pool,
303 sizeof(ngx_http_geo_high_ranges_t));
304 if (ctx->high == NULL) {
305 goto failed;
306 }
307
308 rv = NGX_CONF_OK;
309
310 goto done;
311 }
312 }
189 313
190 if (cf->args->nelts != 2) { 314 if (cf->args->nelts != 2) {
191 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 315 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
192 "invalid number of the geo parameters"); 316 "invalid number of the geo parameters");
317 goto failed;
318 }
319
320 if (ngx_strcmp(value[0].data, "include") == 0) {
321
322 file.len = value[1].len++;
323
324 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
325 if (file.data == NULL) {
326 goto failed;
327 }
328
329 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
330 goto failed;
331 }
332
333 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
334
335 rv = ngx_conf_parse(cf, &file);
336
337 goto done;
338 }
339
340 if (ctx->high) {
341 rv = ngx_http_geo_range(cf, ctx, value);
342
343 } else {
344 rv = ngx_http_geo_cidr(cf, ctx, value);
345 }
346
347 done:
348
349 ngx_reset_pool(cf->pool);
350
351 return rv;
352
353 failed:
354
355 ngx_reset_pool(cf->pool);
356
357 return NGX_CONF_ERROR;
358 }
359
360
361 static char *
362 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
363 ngx_str_t *value)
364 {
365 u_char *p, *last;
366 in_addr_t start, end;
367 ngx_str_t *net;
368 ngx_uint_t del;
369 ngx_http_variable_value_t *old;
370
371 if (ngx_strcmp(value[0].data, "default") == 0) {
372
373 old = ctx->high->default_value;
374
375 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
376 if (ctx->high->default_value == NULL) {
377 return NGX_CONF_ERROR;
378 }
379
380 if (old) {
381 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
382 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
383 &value[0], ctx->high->default_value, old);
384 }
385
386 return NGX_CONF_OK;
387 }
388
389 if (ngx_strcmp(value[0].data, "delete") == 0) {
390 net = &value[1];
391 del = 1;
392
393 } else {
394 net = &value[0];
395 del = 0;
396 }
397
398 last = net->data + net->len;
399
400 p = ngx_strlchr(net->data, last, '-');
401
402 if (p == NULL) {
403 goto invalid;
404 }
405
406 start = ngx_inet_addr(net->data, p - net->data);
407
408 if (start == INADDR_NONE) {
409 goto invalid;
410 }
411
412 start = ntohl(start);
413
414 p++;
415
416 end = ngx_inet_addr(p, last - p);
417
418 if (end == INADDR_NONE) {
419 goto invalid;
420 }
421
422 end = ntohl(end);
423
424 if (start > end) {
425 goto invalid;
426 }
427
428 if (del) {
429 if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
430 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
431 "no address range \"%V\" to delete", net);
432 }
433
434 return NGX_CONF_OK;
435 }
436
437 ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
438
439 if (ctx->value == NULL) {
193 return NGX_CONF_ERROR; 440 return NGX_CONF_ERROR;
194 } 441 }
195 442
196 value = cf->args->elts; 443 ctx->net = net;
197 444
198 if (ngx_strcmp(value[0].data, "include") == 0) { 445 return ngx_http_geo_add_range(cf, ctx, start, end);
199 file = value[1]; 446
200 447 invalid:
201 if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ 448
449 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
450
451 return NGX_CONF_ERROR;
452 }
453
454
455 /* the add procedure is optimized to add a growing up sequence */
456
457 static char *
458 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
459 in_addr_t start, in_addr_t end)
460 {
461 in_addr_t n;
462 ngx_uint_t h, i, s, e;
463 ngx_array_t *a;
464 ngx_http_geo_range_t *range;
465
466 for (n = start; n < end; n += 0x10000) {
467
468 h = n >> 16;
469 s = n & 0xffff;
470
471 if ((n | 0xffff) > end) {
472 e = end & 0xffff;
473
474 } else {
475 e = 0xffff;
476 }
477
478 a = (ngx_array_t *) ctx->high->low[h].ranges;
479
480 if (a == NULL) {
481 a = ngx_array_create(ctx->temp_pool, 64,
482 sizeof(ngx_http_geo_range_t));
483 if (a == NULL) {
484 return NGX_CONF_ERROR;
485 }
486
487 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
488 }
489
490 i = a->nelts;
491 range = a->elts;
492
493 while (i) {
494
495 i--;
496
497 if (e < (ngx_uint_t) range[i].start) {
498 continue;
499 }
500
501 if (s > (ngx_uint_t) range[i].end) {
502
503 /* add after the range */
504
505 range = ngx_array_push(a);
506 if (range == NULL) {
507 return NGX_CONF_ERROR;
508 }
509
510 range = a->elts;
511
512 ngx_memcpy(&range[i + 2], &range[i + 1],
513 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
514
515 range = &range[i + 1];
516
517 goto next;
518 }
519
520 if (s == (ngx_uint_t) range[i].start
521 && e == (ngx_uint_t) range[i].end)
522 {
523 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
524 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
525 ctx->net, ctx->value, range[i].value);
526 continue;
527 }
528
529 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
530 "overlapped range \"%V\"", ctx->net);
531
202 return NGX_CONF_ERROR; 532 return NGX_CONF_ERROR;
203 } 533 }
204 534
205 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); 535 /* add the first range */
206 536
207 return ngx_conf_parse(cf, &file); 537 range = ngx_array_push(a);
538 if (range == NULL) {
539 return NGX_CONF_ERROR;
540 }
541
542 next:
543
544 range->start = (u_short) s;
545 range->end = (u_short) e;
546 range->value = ctx->value;
547 }
548
549 return NGX_CONF_OK;
550 }
551
552
553 static ngx_uint_t
554 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
555 in_addr_t start, in_addr_t end)
556 {
557 in_addr_t n;
558 ngx_uint_t h, i, s, e, warn;
559 ngx_array_t *a;
560 ngx_http_geo_range_t *range;
561
562 warn = 0;
563
564 for (n = start; n < end; n += 0x10000) {
565
566 h = n >> 16;
567 s = n & 0xffff;
568
569 if ((n | 0xffff) > end) {
570 e = end & 0xffff;
571
572 } else {
573 e = 0xffff;
574 }
575
576 a = (ngx_array_t *) ctx->high->low[h].ranges;
577
578 if (a == NULL) {
579 warn = 1;
580 continue;
581 }
582
583 range = a->elts;
584 for (i = 0; i < a->nelts; i++) {
585
586 if (s == (ngx_uint_t) range[i].start
587 && e == (ngx_uint_t) range[i].end)
588 {
589 ngx_memcpy(&range[i], &range[i + 1],
590 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
591 break;
592 }
593
594 if (s != (ngx_uint_t) range[i].start
595 && e != (ngx_uint_t) range[i].end)
596 {
597 continue;
598 }
599
600 warn = 1;
601 }
602 }
603
604 return warn;
605 }
606
607
608 static char *
609 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
610 ngx_str_t *value)
611 {
612 ngx_int_t rc, del;
613 ngx_str_t *net;
614 ngx_uint_t i;
615 ngx_inet_cidr_t cidrin;
616 ngx_http_variable_value_t *val, *old;
617
618 if (ctx->tree == NULL) {
619 ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
620 if (ctx->tree == NULL) {
621 return NGX_CONF_ERROR;
622 }
208 } 623 }
209 624
210 if (ngx_strcmp(value[0].data, "default") == 0) { 625 if (ngx_strcmp(value[0].data, "default") == 0) {
211 cidrin.addr = 0; 626 cidrin.addr = 0;
212 cidrin.mask = 0; 627 cidrin.mask = 0;
628 net = &value[0];
213 629
214 } else { 630 } else {
215 rc = ngx_ptocidr(&value[0], &cidrin); 631 if (ngx_strcmp(value[0].data, "delete") == 0) {
632 net = &value[1];
633 del = 1;
634
635 } else {
636 net = &value[0];
637 del = 0;
638 }
639
640 rc = ngx_ptocidr(net, &cidrin);
216 641
217 if (rc == NGX_ERROR) { 642 if (rc == NGX_ERROR) {
218 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 643 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
219 "invalid parameter \"%V\"", &value[0]); 644 "invalid network \"%V\"", net);
220 return NGX_CONF_ERROR; 645 return NGX_CONF_ERROR;
221 } 646 }
222 647
223 if (rc == NGX_DONE) { 648 if (rc == NGX_DONE) {
224 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 649 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
225 "low address bits of %V are meaningless", 650 "low address bits of %V are meaningless", net);
226 &value[0]);
227 } 651 }
228 652
229 cidrin.addr = ntohl(cidrin.addr); 653 cidrin.addr = ntohl(cidrin.addr);
230 cidrin.mask = ntohl(cidrin.mask); 654 cidrin.mask = ntohl(cidrin.mask);
231 } 655
232 656 if (del) {
233 var = NULL; 657 if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
234 v = ctx->values.elts; 658 != NGX_OK)
235 659 {
236 for (i = 0; i < ctx->values.nelts; i++) { 660 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
237 if ((size_t) v[i]->len != value[1].len) { 661 "no network \"%V\" to delete", net);
238 continue; 662 return NGX_CONF_OK;
239 } 663 }
240 664 }
241 if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) { 665 }
242 var = v[i]; 666
243 break; 667 val = ngx_http_geo_value(cf, ctx, &value[1]);
244 } 668
245 } 669 if (val == NULL) {
246 670 return NGX_CONF_ERROR;
247 if (var == NULL) {
248 var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
249 if (var == NULL) {
250 return NGX_CONF_ERROR;
251 }
252
253 var->len = value[1].len;
254 var->data = ngx_pstrdup(ctx->pool, &value[1]);
255 if (var->data == NULL) {
256 return NGX_CONF_ERROR;
257 }
258
259 var->valid = 1;
260 var->no_cacheable = 0;
261 var->not_found = 0;
262
263 v = ngx_array_push(&ctx->values);
264 if (v == NULL) {
265 return NGX_CONF_ERROR;
266 }
267
268 *v = var;
269 } 671 }
270 672
271 for (i = 2; i; i--) { 673 for (i = 2; i; i--) {
272 rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, 674 rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask,
273 (uintptr_t) var); 675 (uintptr_t) val);
274 if (rc == NGX_OK) { 676 if (rc == NGX_OK) {
275 return NGX_CONF_OK; 677 return NGX_CONF_OK;
276 } 678 }
277 679
278 if (rc == NGX_ERROR) { 680 if (rc == NGX_ERROR) {
280 } 682 }
281 683
282 /* rc == NGX_BUSY */ 684 /* rc == NGX_BUSY */
283 685
284 old = (ngx_http_variable_value_t *) 686 old = (ngx_http_variable_value_t *)
285 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); 687 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
286 688
287 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 689 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
288 "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", 690 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
289 &value[0], var, old); 691 net, val, old);
290 692
291 rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); 693 rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
292 694
293 if (rc == NGX_ERROR) { 695 if (rc == NGX_ERROR) {
696 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
294 return NGX_CONF_ERROR; 697 return NGX_CONF_ERROR;
295 } 698 }
296 } 699 }
297 700
298 return NGX_CONF_ERROR; 701 return NGX_CONF_ERROR;
299 } 702 }
703
704
705 static ngx_http_variable_value_t *
706 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
707 ngx_str_t *value)
708 {
709 uint32_t hash;
710 ngx_http_variable_value_t *val;
711 ngx_http_variable_value_node_t *vvn;
712
713 hash = ngx_crc32_long(value->data, value->len);
714
715 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
716
717 if (val) {
718 return val;
719 }
720
721 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
722 if (val == NULL) {
723 return NULL;
724 }
725
726 val->len = value->len;
727 val->data = ngx_pstrdup(ctx->pool, value);
728 if (val->data == NULL) {
729 return NULL;
730 }
731
732 val->valid = 1;
733 val->no_cacheable = 0;
734 val->not_found = 0;
735
736 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
737 if (vvn == NULL) {
738 return NULL;
739 }
740
741 vvn->node.key = hash;
742 vvn->len = val->len;
743 vvn->value = val;
744
745 ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
746
747 return val;
748 }