Mercurial > hg > nginx-vendor-current
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 } |