comparison src/http/modules/ngx_http_geo_module.c @ 582:c456a023113c NGINX_0_8_43

nginx 0.8.43 *) Feature: large geo ranges base loading speed-up. *) Bugfix: an error_page redirection to "location /zero { return 204; }" without changing status code kept the error body; the bug had appeared in 0.8.42. *) Bugfix: nginx might close IPv6 listen socket during reconfiguration. Thanks to Maxim Dounin. *) Bugfix: the $uid_set variable may be used at any request processing stage.
author Igor Sysoev <http://sysoev.ru>
date Wed, 30 Jun 2010 00:00:00 +0400
parents da3c99095432
children b4dcae568a2a
comparison
equal deleted inserted replaced
581:22b2345b75d9 582:c456a023113c
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_http_variable_value_t *value;
13 u_short start; 14 u_short start;
14 u_short end; 15 u_short end;
15 ngx_http_variable_value_t *value;
16 } ngx_http_geo_range_t; 16 } ngx_http_geo_range_t;
17 17
18 18
19 typedef struct { 19 typedef struct {
20 ngx_http_geo_range_t *ranges; 20 ngx_http_geo_range_t **low;
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; 21 ngx_http_variable_value_t *default_value;
28 } ngx_http_geo_high_ranges_t; 22 } ngx_http_geo_high_ranges_t;
23
24
25 typedef struct {
26 ngx_str_node_t sn;
27 ngx_http_variable_value_t *value;
28 size_t offset;
29 } ngx_http_geo_variable_value_node_t;
29 30
30 31
31 typedef struct { 32 typedef struct {
32 ngx_http_variable_value_t *value; 33 ngx_http_variable_value_t *value;
33 ngx_str_t *net; 34 ngx_str_t *net;
34 ngx_http_geo_high_ranges_t *high; 35 ngx_http_geo_high_ranges_t high;
35 ngx_radix_tree_t *tree; 36 ngx_radix_tree_t *tree;
36 ngx_rbtree_t rbtree; 37 ngx_rbtree_t rbtree;
37 ngx_rbtree_node_t sentinel; 38 ngx_rbtree_node_t sentinel;
38 ngx_array_t *proxies; 39 ngx_array_t *proxies;
39 ngx_pool_t *pool; 40 ngx_pool_t *pool;
40 ngx_pool_t *temp_pool; 41 ngx_pool_t *temp_pool;
42
43 size_t data_size;
44
45 ngx_str_t include_name;
46 ngx_uint_t includes;
47 ngx_uint_t entries;
48
49 unsigned ranges:1;
50 unsigned outside_entries:1;
51 unsigned allow_binary_include:1;
52 unsigned binary_include:1;
41 } ngx_http_geo_conf_ctx_t; 53 } ngx_http_geo_conf_ctx_t;
42 54
43 55
44 typedef struct { 56 typedef struct {
45 union { 57 union {
46 ngx_radix_tree_t *tree; 58 ngx_radix_tree_t *tree;
47 ngx_http_geo_high_ranges_t *high; 59 ngx_http_geo_high_ranges_t high;
48 } u; 60 } u;
49 61
50 ngx_array_t *proxies; 62 ngx_array_t *proxies;
51 63
52 ngx_int_t index; 64 ngx_int_t index;
71 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); 83 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
72 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf, 84 static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
73 ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr); 85 ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
74 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, 86 static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
75 ngx_cidr_t *cidr); 87 ngx_cidr_t *cidr);
88 static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
89 ngx_str_t *name);
90 static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
91 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
92 static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
93 static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
94 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
76 95
77 96
78 static ngx_command_t ngx_http_geo_commands[] = { 97 static ngx_command_t ngx_http_geo_commands[] = {
79 98
80 { ngx_string("geo"), 99 { ngx_string("geo"),
117 NULL, /* exit master */ 136 NULL, /* exit master */
118 NGX_MODULE_V1_PADDING 137 NGX_MODULE_V1_PADDING
119 }; 138 };
120 139
121 140
141 typedef struct {
142 u_char GEORNG[6];
143 u_char version;
144 u_char ptr_size;
145 uint32_t endianess;
146 uint32_t crc32;
147 } ngx_http_geo_header_t;
148
149
150 static ngx_http_geo_header_t ngx_http_geo_header = {
151 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
152 };
153
154
122 /* AF_INET only */ 155 /* AF_INET only */
123 156
124 static ngx_int_t 157 static ngx_int_t
125 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 158 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
126 uintptr_t data) 159 uintptr_t data)
146 uintptr_t data) 179 uintptr_t data)
147 { 180 {
148 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; 181 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
149 182
150 in_addr_t addr; 183 in_addr_t addr;
151 ngx_uint_t i, n; 184 ngx_uint_t n;
152 ngx_http_geo_range_t *range; 185 ngx_http_geo_range_t *range;
153 186
154 *v = *ctx->u.high->default_value; 187 *v = *ctx->u.high.default_value;
155 188
156 addr = ngx_http_geo_addr(r, ctx); 189 addr = ngx_http_geo_addr(r, ctx);
157 190
158 range = ctx->u.high->low[addr >> 16].ranges; 191 range = ctx->u.high.low[addr >> 16];
159 192
160 n = addr & 0xffff; 193 if (range) {
161 194 n = addr & 0xffff;
162 for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { 195 do {
163 if (n >= (ngx_uint_t) range[i].start 196 if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end)
164 && n <= (ngx_uint_t) range[i].end) 197 {
165 { 198 *v = *range->value;
166 *v = *range[i].value; 199 break;
167 } 200 }
201 } while ((++range)->value);
168 } 202 }
169 203
170 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 204 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
171 "http geo: %v", v); 205 "http geo: %v", v);
172 206
254 288
255 static char * 289 static char *
256 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 290 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
257 { 291 {
258 char *rv; 292 char *rv;
293 void **p;
259 size_t len; 294 size_t len;
260 ngx_str_t *value, name; 295 ngx_str_t *value, name;
261 ngx_uint_t i; 296 ngx_uint_t i;
262 ngx_conf_t save; 297 ngx_conf_t save;
263 ngx_pool_t *pool; 298 ngx_pool_t *pool;
300 pool = ngx_create_pool(16384, cf->log); 335 pool = ngx_create_pool(16384, cf->log);
301 if (pool == NULL) { 336 if (pool == NULL) {
302 return NGX_CONF_ERROR; 337 return NGX_CONF_ERROR;
303 } 338 }
304 339
340 ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
341
305 ctx.temp_pool = ngx_create_pool(16384, cf->log); 342 ctx.temp_pool = ngx_create_pool(16384, cf->log);
306 if (ctx.temp_pool == NULL) { 343 if (ctx.temp_pool == NULL) {
307 return NGX_CONF_ERROR; 344 return NGX_CONF_ERROR;
308 } 345 }
309 346
310 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, 347 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
311 ngx_http_variable_value_rbtree_insert); 348
312
313 ctx.high = NULL;
314 ctx.tree = NULL;
315 ctx.proxies = NULL;
316 ctx.pool = cf->pool; 349 ctx.pool = cf->pool;
350 ctx.data_size = sizeof(ngx_http_geo_header_t)
351 + sizeof(ngx_http_variable_value_t)
352 + 0x10000 * sizeof(ngx_http_geo_range_t *);
353 ctx.allow_binary_include = 1;
317 354
318 save = *cf; 355 save = *cf;
319 cf->pool = pool; 356 cf->pool = pool;
320 cf->ctx = &ctx; 357 cf->ctx = &ctx;
321 cf->handler = ngx_http_geo; 358 cf->handler = ngx_http_geo;
325 362
326 *cf = save; 363 *cf = save;
327 364
328 geo->proxies = ctx.proxies; 365 geo->proxies = ctx.proxies;
329 366
330 if (ctx.high) { 367 if (ctx.high.low) {
331 368
332 for (i = 0; i < 0x10000; i++) { 369 if (!ctx.binary_include) {
333 a = (ngx_array_t *) ctx.high->low[i].ranges; 370 for (i = 0; i < 0x10000; i++) {
334 371 a = (ngx_array_t *) ctx.high.low[i];
335 if (a == NULL || a->nelts == 0) { 372
336 continue; 373 if (a == NULL || a->nelts == 0) {
337 } 374 continue;
338 375 }
339 ctx.high->low[i].n = a->nelts; 376
340 377 len = a->nelts * sizeof(ngx_http_geo_range_t);
341 len = a->nelts * sizeof(ngx_http_geo_range_t); 378
342 379 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
343 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); 380 if (ctx.high.low[i] == NULL) {
344 if (ctx.high->low[i].ranges == NULL ){ 381 return NGX_CONF_ERROR;
345 return NGX_CONF_ERROR; 382 }
346 } 383
347 384 p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len);
348 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); 385 *p = NULL;
386 ctx.data_size += len + sizeof(void *);
387 }
388
389 if (ctx.allow_binary_include
390 && !ctx.outside_entries
391 && ctx.entries > 100000
392 && ctx.includes == 1)
393 {
394 ngx_http_geo_create_binary_base(&ctx);
395 }
349 } 396 }
350 397
351 geo->u.high = ctx.high; 398 geo->u.high = ctx.high;
352 399
353 var->get_handler = ngx_http_geo_range_variable; 400 var->get_handler = ngx_http_geo_range_variable;
354 var->data = (uintptr_t) geo; 401 var->data = (uintptr_t) geo;
355 402
403 if (ctx.high.default_value == NULL) {
404 ctx.high.default_value = &ngx_http_variable_null_value;
405 }
406
356 ngx_destroy_pool(ctx.temp_pool); 407 ngx_destroy_pool(ctx.temp_pool);
357 ngx_destroy_pool(pool); 408 ngx_destroy_pool(pool);
358
359 if (ctx.high->default_value == NULL) {
360 ctx.high->default_value = &ngx_http_variable_null_value;
361 }
362 409
363 } else { 410 } else {
364 if (ctx.tree == NULL) { 411 if (ctx.tree == NULL) {
365 ctx.tree = ngx_radix_tree_create(cf->pool, -1); 412 ctx.tree = ngx_radix_tree_create(cf->pool, -1);
366 if (ctx.tree == NULL) { 413 if (ctx.tree == NULL) {
394 441
395 static char * 442 static char *
396 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 443 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
397 { 444 {
398 char *rv; 445 char *rv;
399 ngx_str_t *value, file; 446 ngx_str_t *value;
400 ngx_cidr_t cidr; 447 ngx_cidr_t cidr;
401 ngx_http_geo_conf_ctx_t *ctx; 448 ngx_http_geo_conf_ctx_t *ctx;
402 449
403 ctx = cf->ctx; 450 ctx = cf->ctx;
404 451
413 "the \"ranges\" directive must be " 460 "the \"ranges\" directive must be "
414 "the first directive inside \"geo\" block"); 461 "the first directive inside \"geo\" block");
415 goto failed; 462 goto failed;
416 } 463 }
417 464
418 ctx->high = ngx_pcalloc(ctx->pool, 465 ctx->ranges = 1;
419 sizeof(ngx_http_geo_high_ranges_t));
420 if (ctx->high == NULL) {
421 goto failed;
422 }
423 466
424 rv = NGX_CONF_OK; 467 rv = NGX_CONF_OK;
425 468
426 goto done; 469 goto done;
427 } 470 }
433 goto failed; 476 goto failed;
434 } 477 }
435 478
436 if (ngx_strcmp(value[0].data, "include") == 0) { 479 if (ngx_strcmp(value[0].data, "include") == 0) {
437 480
438 file.len = value[1].len++; 481 rv = ngx_http_geo_include(cf, ctx, &value[1]);
439
440 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
441 if (file.data == NULL) {
442 goto failed;
443 }
444
445 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
446 goto failed;
447 }
448
449 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
450
451 rv = ngx_conf_parse(cf, &file);
452 482
453 goto done; 483 goto done;
454 484
455 } else if (ngx_strcmp(value[0].data, "proxy") == 0) { 485 } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
456 486
461 rv = ngx_http_geo_add_proxy(cf, ctx, &cidr); 491 rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
462 492
463 goto done; 493 goto done;
464 } 494 }
465 495
466 if (ctx->high) { 496 if (ctx->ranges) {
467 rv = ngx_http_geo_range(cf, ctx, value); 497 rv = ngx_http_geo_range(cf, ctx, value);
468 498
469 } else { 499 } else {
470 rv = ngx_http_geo_cidr(cf, ctx, value); 500 rv = ngx_http_geo_cidr(cf, ctx, value);
471 } 501 }
486 516
487 static char * 517 static char *
488 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, 518 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
489 ngx_str_t *value) 519 ngx_str_t *value)
490 { 520 {
491 u_char *p, *last; 521 u_char *p, *last;
492 in_addr_t start, end; 522 in_addr_t start, end;
493 ngx_str_t *net; 523 ngx_str_t *net;
494 ngx_uint_t del; 524 ngx_uint_t del;
495 ngx_http_variable_value_t *old;
496 525
497 if (ngx_strcmp(value[0].data, "default") == 0) { 526 if (ngx_strcmp(value[0].data, "default") == 0) {
498 527
499 old = ctx->high->default_value; 528 if (ctx->high.default_value) {
500 529 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
501 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); 530 "duplicate default geo range value: \"%V\", old value: \"%v\"",
502 if (ctx->high->default_value == NULL) { 531 &value[1], ctx->high.default_value);
532 }
533
534 ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
535 if (ctx->high.default_value == NULL) {
503 return NGX_CONF_ERROR; 536 return NGX_CONF_ERROR;
504 } 537 }
505 538
506 if (old) {
507 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
508 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
509 &value[0], ctx->high->default_value, old);
510 }
511
512 return NGX_CONF_OK; 539 return NGX_CONF_OK;
513 } 540 }
541
542 if (ctx->binary_include) {
543 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
544 "binary geo range base \"%s\" may not be mixed with usual entries",
545 ctx->include_name.data);
546 return NGX_CONF_ERROR;
547 }
548
549 if (ctx->high.low == NULL) {
550 ctx->high.low = ngx_pcalloc(ctx->pool,
551 0x10000 * sizeof(ngx_http_geo_range_t *));
552 if (ctx->high.low == NULL) {
553 return NGX_CONF_ERROR;
554 }
555 }
556
557 ctx->entries++;
558 ctx->outside_entries = 1;
514 559
515 if (ngx_strcmp(value[0].data, "delete") == 0) { 560 if (ngx_strcmp(value[0].data, "delete") == 0) {
516 net = &value[1]; 561 net = &value[1];
517 del = 1; 562 del = 1;
518 563
604 649
605 } else { 650 } else {
606 e = 0xffff; 651 e = 0xffff;
607 } 652 }
608 653
609 a = (ngx_array_t *) ctx->high->low[h].ranges; 654 a = (ngx_array_t *) ctx->high.low[h];
610 655
611 if (a == NULL) { 656 if (a == NULL) {
612 a = ngx_array_create(ctx->temp_pool, 64, 657 a = ngx_array_create(ctx->temp_pool, 64,
613 sizeof(ngx_http_geo_range_t)); 658 sizeof(ngx_http_geo_range_t));
614 if (a == NULL) { 659 if (a == NULL) {
615 return NGX_CONF_ERROR; 660 return NGX_CONF_ERROR;
616 } 661 }
617 662
618 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; 663 ctx->high.low[h] = (ngx_http_geo_range_t *) a;
619 } 664 }
620 665
621 i = a->nelts; 666 i = a->nelts;
622 range = a->elts; 667 range = a->elts;
623 668
801 846
802 } else { 847 } else {
803 e = 0xffff; 848 e = 0xffff;
804 } 849 }
805 850
806 a = (ngx_array_t *) ctx->high->low[h].ranges; 851 a = (ngx_array_t *) ctx->high.low[h];
807 852
808 if (a == NULL) { 853 if (a == NULL) {
809 warn = 1; 854 warn = 1;
810 continue; 855 continue;
811 } 856 }
927 972
928 static ngx_http_variable_value_t * 973 static ngx_http_variable_value_t *
929 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, 974 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
930 ngx_str_t *value) 975 ngx_str_t *value)
931 { 976 {
932 uint32_t hash; 977 uint32_t hash;
933 ngx_http_variable_value_t *val; 978 ngx_http_variable_value_t *val;
934 ngx_http_variable_value_node_t *vvn; 979 ngx_http_geo_variable_value_node_t *gvvn;
935 980
936 hash = ngx_crc32_long(value->data, value->len); 981 hash = ngx_crc32_long(value->data, value->len);
937 982
938 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); 983 gvvn = (ngx_http_geo_variable_value_node_t *)
939 984 ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
940 if (val) { 985
941 return val; 986 if (gvvn) {
987 return gvvn->value;
942 } 988 }
943 989
944 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); 990 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
945 if (val == NULL) { 991 if (val == NULL) {
946 return NULL; 992 return NULL;
954 1000
955 val->valid = 1; 1001 val->valid = 1;
956 val->no_cacheable = 0; 1002 val->no_cacheable = 0;
957 val->not_found = 0; 1003 val->not_found = 0;
958 1004
959 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); 1005 gvvn = ngx_palloc(ctx->temp_pool,
960 if (vvn == NULL) { 1006 sizeof(ngx_http_geo_variable_value_node_t));
1007 if (gvvn == NULL) {
961 return NULL; 1008 return NULL;
962 } 1009 }
963 1010
964 vvn->node.key = hash; 1011 gvvn->sn.node.key = hash;
965 vvn->len = val->len; 1012 gvvn->sn.str.len = val->len;
966 vvn->value = val; 1013 gvvn->sn.str.data = val->data;
967 1014 gvvn->value = val;
968 ngx_rbtree_insert(&ctx->rbtree, &vvn->node); 1015 gvvn->offset = 0;
1016
1017 ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
1018
1019 ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
1020 sizeof(void *));
969 1021
970 return val; 1022 return val;
971 } 1023 }
972 1024
973 1025
1028 cidr->u.in.addr = ntohl(cidr->u.in.addr); 1080 cidr->u.in.addr = ntohl(cidr->u.in.addr);
1029 cidr->u.in.mask = ntohl(cidr->u.in.mask); 1081 cidr->u.in.mask = ntohl(cidr->u.in.mask);
1030 1082
1031 return NGX_OK; 1083 return NGX_OK;
1032 } 1084 }
1085
1086
1087 static char *
1088 ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1089 ngx_str_t *name)
1090 {
1091 char *rv;
1092 ngx_str_t file;
1093
1094 file.len = name->len + 4;
1095 file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
1096 if (file.data == NULL) {
1097 return NGX_CONF_ERROR;
1098 }
1099
1100 ngx_sprintf(file.data, "%V.bin%Z", name);
1101
1102 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
1103 return NGX_CONF_ERROR;
1104 }
1105
1106 if (ctx->ranges) {
1107 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1108
1109 switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
1110 case NGX_OK:
1111 return NGX_CONF_OK;
1112 case NGX_ERROR:
1113 return NGX_CONF_ERROR;
1114 default:
1115 break;
1116 }
1117 }
1118
1119 file.len -= 4;
1120 file.data[file.len] = '\0';
1121
1122 ctx->include_name = file;
1123
1124 if (ctx->outside_entries) {
1125 ctx->allow_binary_include = 0;
1126 }
1127
1128 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1129
1130 rv = ngx_conf_parse(cf, &file);
1131
1132 ctx->includes++;
1133 ctx->outside_entries = 0;
1134
1135 return rv;
1136 }
1137
1138
1139 static ngx_int_t
1140 ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1141 ngx_str_t *name)
1142 {
1143 u_char *base, ch;
1144 time_t mtime;
1145 size_t size, len;
1146 ssize_t n;
1147 uint32_t crc32;
1148 ngx_err_t err;
1149 ngx_int_t rc;
1150 ngx_uint_t i;
1151 ngx_file_t file;
1152 ngx_file_info_t fi;
1153 ngx_http_geo_range_t *range, **ranges;
1154 ngx_http_geo_header_t *header;
1155 ngx_http_variable_value_t *vv;
1156
1157 ngx_memzero(&file, sizeof(ngx_file_t));
1158 file.name = *name;
1159 file.log = cf->log;
1160
1161 file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
1162 if (file.fd == NGX_INVALID_FILE) {
1163 err = ngx_errno;
1164 if (err != NGX_ENOENT) {
1165 ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
1166 ngx_open_file_n " \"%s\" failed", name->data);
1167 }
1168 return NGX_DECLINED;
1169 }
1170
1171 if (ctx->outside_entries) {
1172 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1173 "binary geo range base \"%s\" may not be mixed with usual entries",
1174 name->data);
1175 rc = NGX_ERROR;
1176 goto done;
1177 }
1178
1179 if (ctx->binary_include) {
1180 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1181 "second binary geo range base \"%s\" may not be mixed with \"%s\"",
1182 name->data, ctx->include_name.data);
1183 rc = NGX_ERROR;
1184 goto done;
1185 }
1186
1187 if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
1188 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1189 ngx_fd_info_n " \"%s\" failed", name->data);
1190 goto failed;
1191 }
1192
1193 size = (size_t) ngx_file_size(&fi);
1194 mtime = ngx_file_mtime(&fi);
1195
1196 ch = name->data[name->len - 4];
1197 name->data[name->len - 4] = '\0';
1198
1199 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
1200 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1201 ngx_file_info_n " \"%s\" failed", name->data);
1202 goto failed;
1203 }
1204
1205 name->data[name->len - 4] = ch;
1206
1207 if (mtime < ngx_file_mtime(&fi)) {
1208 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1209 "stale binary geo range base \"%s\"", name->data);
1210 goto failed;
1211 }
1212
1213 base = ngx_palloc(ctx->pool, size);
1214 if (base == NULL) {
1215 goto failed;
1216 }
1217
1218 n = ngx_read_file(&file, base, size, 0);
1219
1220 if (n == NGX_ERROR) {
1221 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1222 ngx_read_file_n " \"%s\" failed", name->data);
1223 goto failed;
1224 }
1225
1226 if ((size_t) n != size) {
1227 ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
1228 ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
1229 name->data, n, size);
1230 goto failed;
1231 }
1232
1233 header = (ngx_http_geo_header_t *) base;
1234
1235 if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
1236 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1237 "incompatible binary geo range base \"%s\"", name->data);
1238 goto failed;
1239 }
1240
1241 ngx_crc32_init(crc32);
1242
1243 vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
1244
1245 while(vv->data) {
1246 len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
1247 sizeof(void *));
1248 ngx_crc32_update(&crc32, (u_char *) vv, len);
1249 vv->data += (size_t) base;
1250 vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
1251 }
1252 ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
1253 vv++;
1254
1255 ranges = (ngx_http_geo_range_t **) vv;
1256
1257 for (i = 0; i < 0x10000; i++) {
1258 ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
1259 if (ranges[i]) {
1260 ranges[i] = (ngx_http_geo_range_t *)
1261 ((u_char *) ranges[i] + (size_t) base);
1262 }
1263 }
1264
1265 range = (ngx_http_geo_range_t *) &ranges[0x10000];
1266
1267 while ((u_char *) range < base + size) {
1268 while (range->value) {
1269 ngx_crc32_update(&crc32, (u_char *) range,
1270 sizeof(ngx_http_geo_range_t));
1271 range->value = (ngx_http_variable_value_t *)
1272 ((u_char *) range->value + (size_t) base);
1273 range++;
1274 }
1275 ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
1276 range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
1277 }
1278
1279 ngx_crc32_final(crc32);
1280
1281 if (crc32 != header->crc32) {
1282 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1283 "CRC32 mismatch in binary geo range base \"%s\"", name->data);
1284 goto failed;
1285 }
1286
1287 ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
1288 "using binary geo range base \"%s\"", name->data);
1289
1290 ctx->include_name = *name;
1291 ctx->binary_include = 1;
1292 ctx->high.low = ranges;
1293 rc = NGX_OK;
1294
1295 goto done;
1296
1297 failed:
1298
1299 rc = NGX_DECLINED;
1300
1301 done:
1302
1303 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
1304 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
1305 ngx_close_file_n " \"%s\" failed", name->data);
1306 }
1307
1308 return rc;
1309 }
1310
1311
1312 static void
1313 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
1314 {
1315 u_char *p;
1316 uint32_t hash;
1317 ngx_str_t s;
1318 ngx_uint_t i;
1319 ngx_file_mapping_t fm;
1320 ngx_http_geo_range_t *r, *range, **ranges;
1321 ngx_http_geo_header_t *header;
1322 ngx_http_geo_variable_value_node_t *gvvn;
1323
1324 fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
1325 if (fm.name == NULL) {
1326 return;
1327 }
1328
1329 ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
1330
1331 fm.size = ctx->data_size;
1332 fm.log = ctx->pool->log;
1333
1334 ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
1335 "creating binary geo range base \"%s\"", fm.name);
1336
1337 if (ngx_create_file_mapping(&fm) != NGX_OK) {
1338 return;
1339 }
1340
1341 p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
1342 sizeof(ngx_http_geo_header_t));
1343
1344 p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
1345 ctx->rbtree.sentinel);
1346
1347 p += sizeof(ngx_http_variable_value_t);
1348
1349 ranges = (ngx_http_geo_range_t **) p;
1350
1351 p += 0x10000 * sizeof(ngx_http_geo_range_t *);
1352
1353 for (i = 0; i < 0x10000; i++) {
1354 r = ctx->high.low[i];
1355 if (r == NULL) {
1356 continue;
1357 }
1358
1359 range = (ngx_http_geo_range_t *) p;
1360 ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
1361
1362 do {
1363 s.len = r->value->len;
1364 s.data = r->value->data;
1365 hash = ngx_crc32_long(s.data, s.len);
1366 gvvn = (ngx_http_geo_variable_value_node_t *)
1367 ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
1368
1369 range->value = (ngx_http_variable_value_t *) gvvn->offset;
1370 range->start = r->start;
1371 range->end = r->end;
1372 range++;
1373
1374 } while ((++r)->value);
1375
1376 range->value = NULL;
1377
1378 p = (u_char *) range + sizeof(void *);
1379 }
1380
1381 header = fm.addr;
1382 header->crc32 = ngx_crc32_long((u_char *) fm.addr
1383 + sizeof(ngx_http_geo_header_t),
1384 fm.size - sizeof(ngx_http_geo_header_t));
1385
1386 ngx_close_file_mapping(&fm);
1387 }
1388
1389
1390 static u_char *
1391 ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
1392 ngx_rbtree_node_t *sentinel)
1393 {
1394 ngx_http_variable_value_t *vv;
1395 ngx_http_geo_variable_value_node_t *gvvn;
1396
1397 if (node == sentinel) {
1398 return p;
1399 }
1400
1401 gvvn = (ngx_http_geo_variable_value_node_t *) node;
1402 gvvn->offset = p - base;
1403
1404 vv = (ngx_http_variable_value_t *) p;
1405 *vv = *gvvn->value;
1406 p += sizeof(ngx_http_variable_value_t);
1407 vv->data = (u_char *) (p - base);
1408
1409 p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
1410
1411 p = ngx_align_ptr(p, sizeof(void *));
1412
1413 p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
1414
1415 return ngx_http_geo_copy_values(base, p, node->right, sentinel);
1416 }