Mercurial > hg > nginx
comparison src/http/modules/ngx_http_geo_module.c @ 3652:1eea82e36053
binary geo ranges base cache
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 29 Jun 2010 16:06:20 +0000 |
parents | c12b0dd5bd1c |
children | be3f716ba546 |
comparison
equal
deleted
inserted
replaced
3651:515d50917016 | 3652:1eea82e36053 |
---|---|
15 u_short end; | 15 u_short end; |
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 *low[0x10000]; | 20 ngx_http_geo_range_t **low; |
21 ngx_http_variable_value_t *default_value; | 21 ngx_http_variable_value_t *default_value; |
22 } ngx_http_geo_high_ranges_t; | 22 } ngx_http_geo_high_ranges_t; |
23 | 23 |
24 | 24 |
25 typedef struct { | 25 typedef struct { |
26 ngx_str_node_t sn; | 26 ngx_str_node_t sn; |
27 ngx_http_variable_value_t *value; | 27 ngx_http_variable_value_t *value; |
28 size_t offset; | |
28 } ngx_http_geo_variable_value_node_t; | 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) |
149 | 182 |
150 in_addr_t addr; | 183 in_addr_t addr; |
151 ngx_uint_t 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]; | 191 range = ctx->u.high.low[addr >> 16]; |
159 | 192 |
160 if (range) { | 193 if (range) { |
161 n = addr & 0xffff; | 194 n = addr & 0xffff; |
162 do { | 195 do { |
163 if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end) | 196 if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end) |
302 pool = ngx_create_pool(16384, cf->log); | 335 pool = ngx_create_pool(16384, cf->log); |
303 if (pool == NULL) { | 336 if (pool == NULL) { |
304 return NGX_CONF_ERROR; | 337 return NGX_CONF_ERROR; |
305 } | 338 } |
306 | 339 |
340 ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t)); | |
341 | |
307 ctx.temp_pool = ngx_create_pool(16384, cf->log); | 342 ctx.temp_pool = ngx_create_pool(16384, cf->log); |
308 if (ctx.temp_pool == NULL) { | 343 if (ctx.temp_pool == NULL) { |
309 return NGX_CONF_ERROR; | 344 return NGX_CONF_ERROR; |
310 } | 345 } |
311 | 346 |
312 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); | 347 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); |
313 | 348 |
314 ctx.high = NULL; | |
315 ctx.tree = NULL; | |
316 ctx.proxies = NULL; | |
317 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; | |
318 | 354 |
319 save = *cf; | 355 save = *cf; |
320 cf->pool = pool; | 356 cf->pool = pool; |
321 cf->ctx = &ctx; | 357 cf->ctx = &ctx; |
322 cf->handler = ngx_http_geo; | 358 cf->handler = ngx_http_geo; |
326 | 362 |
327 *cf = save; | 363 *cf = save; |
328 | 364 |
329 geo->proxies = ctx.proxies; | 365 geo->proxies = ctx.proxies; |
330 | 366 |
331 if (ctx.high) { | 367 if (ctx.high.low) { |
332 | 368 |
333 for (i = 0; i < 0x10000; i++) { | 369 if (!ctx.binary_include) { |
334 a = (ngx_array_t *) ctx.high->low[i]; | 370 for (i = 0; i < 0x10000; i++) { |
335 | 371 a = (ngx_array_t *) ctx.high.low[i]; |
336 if (a == NULL || a->nelts == 0) { | 372 |
337 continue; | 373 if (a == NULL || a->nelts == 0) { |
338 } | 374 continue; |
339 | 375 } |
340 len = a->nelts * sizeof(ngx_http_geo_range_t); | 376 |
341 | 377 len = a->nelts * sizeof(ngx_http_geo_range_t); |
342 ctx.high->low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); | 378 |
343 if (ctx.high->low[i] == NULL) { | 379 ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); |
344 return NGX_CONF_ERROR; | 380 if (ctx.high.low[i] == NULL) { |
345 } | 381 return NGX_CONF_ERROR; |
346 | 382 } |
347 p = (void **) ngx_cpymem(ctx.high->low[i], a->elts, len); | 383 |
348 *p = NULL; | 384 p = (void **) ngx_cpymem(ctx.high.low[i], 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 } |
493 ngx_str_t *net; | 523 ngx_str_t *net; |
494 ngx_uint_t del; | 524 ngx_uint_t del; |
495 | 525 |
496 if (ngx_strcmp(value[0].data, "default") == 0) { | 526 if (ngx_strcmp(value[0].data, "default") == 0) { |
497 | 527 |
498 if (ctx->high->default_value) { | 528 if (ctx->high.default_value) { |
499 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | 529 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, |
500 "duplicate default geo range value: \"%V\", old value: \"%v\"", | 530 "duplicate default geo range value: \"%V\", old value: \"%v\"", |
501 &value[1], ctx->high->default_value); | 531 &value[1], ctx->high.default_value); |
502 } | 532 } |
503 | 533 |
504 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); | 534 ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]); |
505 if (ctx->high->default_value == NULL) { | 535 if (ctx->high.default_value == NULL) { |
506 return NGX_CONF_ERROR; | 536 return NGX_CONF_ERROR; |
507 } | 537 } |
508 | 538 |
509 return NGX_CONF_OK; | 539 return NGX_CONF_OK; |
510 } | 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; | |
511 | 559 |
512 if (ngx_strcmp(value[0].data, "delete") == 0) { | 560 if (ngx_strcmp(value[0].data, "delete") == 0) { |
513 net = &value[1]; | 561 net = &value[1]; |
514 del = 1; | 562 del = 1; |
515 | 563 |
601 | 649 |
602 } else { | 650 } else { |
603 e = 0xffff; | 651 e = 0xffff; |
604 } | 652 } |
605 | 653 |
606 a = (ngx_array_t *) ctx->high->low[h]; | 654 a = (ngx_array_t *) ctx->high.low[h]; |
607 | 655 |
608 if (a == NULL) { | 656 if (a == NULL) { |
609 a = ngx_array_create(ctx->temp_pool, 64, | 657 a = ngx_array_create(ctx->temp_pool, 64, |
610 sizeof(ngx_http_geo_range_t)); | 658 sizeof(ngx_http_geo_range_t)); |
611 if (a == NULL) { | 659 if (a == NULL) { |
612 return NGX_CONF_ERROR; | 660 return NGX_CONF_ERROR; |
613 } | 661 } |
614 | 662 |
615 ctx->high->low[h] = (ngx_http_geo_range_t *) a; | 663 ctx->high.low[h] = (ngx_http_geo_range_t *) a; |
616 } | 664 } |
617 | 665 |
618 i = a->nelts; | 666 i = a->nelts; |
619 range = a->elts; | 667 range = a->elts; |
620 | 668 |
798 | 846 |
799 } else { | 847 } else { |
800 e = 0xffff; | 848 e = 0xffff; |
801 } | 849 } |
802 | 850 |
803 a = (ngx_array_t *) ctx->high->low[h]; | 851 a = (ngx_array_t *) ctx->high.low[h]; |
804 | 852 |
805 if (a == NULL) { | 853 if (a == NULL) { |
806 warn = 1; | 854 warn = 1; |
807 continue; | 855 continue; |
808 } | 856 } |
962 | 1010 |
963 gvvn->sn.node.key = hash; | 1011 gvvn->sn.node.key = hash; |
964 gvvn->sn.str.len = val->len; | 1012 gvvn->sn.str.len = val->len; |
965 gvvn->sn.str.data = val->data; | 1013 gvvn->sn.str.data = val->data; |
966 gvvn->value = val; | 1014 gvvn->value = val; |
1015 gvvn->offset = 0; | |
967 | 1016 |
968 ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node); | 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 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
1107 | |
1108 switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) { | |
1109 case NGX_OK: | |
1110 return NGX_CONF_OK; | |
1111 case NGX_ERROR: | |
1112 return NGX_CONF_ERROR; | |
1113 default: | |
1114 break; | |
1115 } | |
1116 | |
1117 file.len -= 4; | |
1118 file.data[file.len] = '\0'; | |
1119 | |
1120 ctx->include_name = file; | |
1121 | |
1122 if (ctx->outside_entries) { | |
1123 ctx->allow_binary_include = 0; | |
1124 } | |
1125 | |
1126 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
1127 | |
1128 rv = ngx_conf_parse(cf, &file); | |
1129 | |
1130 ctx->includes++; | |
1131 ctx->outside_entries = 0; | |
1132 | |
1133 return rv; | |
1134 } | |
1135 | |
1136 | |
1137 static ngx_int_t | |
1138 ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, | |
1139 ngx_str_t *name) | |
1140 { | |
1141 u_char *base; | |
1142 size_t size, len; | |
1143 ssize_t n; | |
1144 uint32_t crc32; | |
1145 ngx_err_t err; | |
1146 ngx_int_t rc; | |
1147 ngx_uint_t i; | |
1148 ngx_file_t file; | |
1149 ngx_file_info_t fi; | |
1150 ngx_http_geo_range_t *range, **ranges; | |
1151 ngx_http_geo_header_t *header; | |
1152 ngx_http_variable_value_t *vv; | |
1153 | |
1154 ngx_memzero(&file, sizeof(ngx_file_t)); | |
1155 file.name = *name; | |
1156 file.log = cf->log; | |
1157 | |
1158 file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0); | |
1159 if (file.fd == NGX_INVALID_FILE) { | |
1160 err = ngx_errno; | |
1161 if (err != NGX_ENOENT) { | |
1162 ngx_conf_log_error(NGX_LOG_CRIT, cf, err, | |
1163 ngx_open_file_n " \"%s\" failed", name->data); | |
1164 } | |
1165 return NGX_DECLINED; | |
1166 } | |
1167 | |
1168 if (ctx->outside_entries) { | |
1169 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1170 "binary geo range base \"%s\" may not be mixed with usual entries", | |
1171 name->data); | |
1172 rc = NGX_ERROR; | |
1173 goto done; | |
1174 } | |
1175 | |
1176 if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { | |
1177 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, | |
1178 ngx_fd_info_n " \"%s\" failed", name->data); | |
1179 goto failed; | |
1180 } | |
1181 | |
1182 size = (size_t) ngx_file_size(&fi); | |
1183 | |
1184 base = ngx_palloc(ctx->pool, size); | |
1185 if (base == NULL) { | |
1186 goto failed; | |
1187 } | |
1188 | |
1189 n = ngx_read_file(&file, base, size, 0); | |
1190 | |
1191 if (n == NGX_ERROR) { | |
1192 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, | |
1193 ngx_read_file_n " \"%s\" failed", name->data); | |
1194 goto failed; | |
1195 } | |
1196 | |
1197 if ((size_t) n != size) { | |
1198 ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, | |
1199 ngx_read_file_n " \"%s\" returned only %z bytes instead of %z", | |
1200 name->data, n, size); | |
1201 goto failed; | |
1202 } | |
1203 | |
1204 header = (ngx_http_geo_header_t *) base; | |
1205 | |
1206 if (ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) { | |
1207 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
1208 "incompatible binary geo range base \"%s\"", name->data); | |
1209 goto failed; | |
1210 } | |
1211 | |
1212 ngx_crc32_init(crc32); | |
1213 | |
1214 vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t)); | |
1215 | |
1216 while(vv->data) { | |
1217 len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len, | |
1218 sizeof(void *)); | |
1219 ngx_crc32_update(&crc32, (u_char *) vv, len); | |
1220 vv->data += (size_t) base; | |
1221 vv = (ngx_http_variable_value_t *) ((u_char *) vv + len); | |
1222 } | |
1223 ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t)); | |
1224 vv++; | |
1225 | |
1226 ranges = (ngx_http_geo_range_t **) vv; | |
1227 | |
1228 for (i = 0; i < 0x10000; i++) { | |
1229 ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); | |
1230 if (ranges[i]) { | |
1231 ranges[i] = (ngx_http_geo_range_t *) | |
1232 ((u_char *) ranges[i] + (size_t) base); | |
1233 } | |
1234 } | |
1235 | |
1236 range = (ngx_http_geo_range_t *) &ranges[0x10000]; | |
1237 | |
1238 while ((u_char *) range < base + size) { | |
1239 while (range->value) { | |
1240 ngx_crc32_update(&crc32, (u_char *) range, | |
1241 sizeof(ngx_http_geo_range_t)); | |
1242 range->value = (ngx_http_variable_value_t *) | |
1243 ((u_char *) range->value + (size_t) base); | |
1244 range++; | |
1245 } | |
1246 ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); | |
1247 range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *)); | |
1248 } | |
1249 | |
1250 ngx_crc32_final(crc32); | |
1251 | |
1252 if (crc32 != header->crc32) { | |
1253 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
1254 "CRC32 mismatch in binary geo range base \"%s\"", name->data); | |
1255 goto failed; | |
1256 } | |
1257 | |
1258 ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, | |
1259 "using binary geo range base \"%s\"", name->data); | |
1260 | |
1261 ctx->include_name = *name; | |
1262 ctx->binary_include = 1; | |
1263 ctx->high.low = ranges; | |
1264 rc = NGX_OK; | |
1265 | |
1266 goto done; | |
1267 | |
1268 failed: | |
1269 | |
1270 rc = NGX_DECLINED; | |
1271 | |
1272 done: | |
1273 | |
1274 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { | |
1275 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, | |
1276 ngx_close_file_n " \"%s\" failed", name->data); | |
1277 } | |
1278 | |
1279 return rc; | |
1280 } | |
1281 | |
1282 | |
1283 static void | |
1284 ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx) | |
1285 { | |
1286 u_char *p; | |
1287 uint32_t hash; | |
1288 ngx_str_t s; | |
1289 ngx_uint_t i; | |
1290 ngx_file_mapping_t fm; | |
1291 ngx_http_geo_range_t *r, *range, **ranges; | |
1292 ngx_http_geo_header_t *header; | |
1293 ngx_http_geo_variable_value_node_t *gvvn; | |
1294 | |
1295 fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5); | |
1296 if (fm.name == NULL) { | |
1297 return; | |
1298 } | |
1299 | |
1300 ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name); | |
1301 | |
1302 fm.size = ctx->data_size; | |
1303 fm.log = ctx->pool->log; | |
1304 | |
1305 ngx_log_error(NGX_LOG_NOTICE, fm.log, 0, | |
1306 "creating binary geo range base \"%s\"", fm.name); | |
1307 | |
1308 if (ngx_create_file_mapping(&fm) != NGX_OK) { | |
1309 return; | |
1310 } | |
1311 | |
1312 p = ngx_cpymem(fm.addr, &ngx_http_geo_header, | |
1313 sizeof(ngx_http_geo_header_t)); | |
1314 | |
1315 p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root, | |
1316 ctx->rbtree.sentinel); | |
1317 | |
1318 p += sizeof(ngx_http_variable_value_t); | |
1319 | |
1320 ranges = (ngx_http_geo_range_t **) p; | |
1321 | |
1322 p += 0x10000 * sizeof(ngx_http_geo_range_t *); | |
1323 | |
1324 for (i = 0; i < 0x10000; i++) { | |
1325 r = ctx->high.low[i]; | |
1326 if (r == NULL) { | |
1327 continue; | |
1328 } | |
1329 | |
1330 range = (ngx_http_geo_range_t *) p; | |
1331 ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr); | |
1332 | |
1333 do { | |
1334 s.len = r->value->len; | |
1335 s.data = r->value->data; | |
1336 hash = ngx_crc32_long(s.data, s.len); | |
1337 gvvn = (ngx_http_geo_variable_value_node_t *) | |
1338 ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash); | |
1339 | |
1340 range->value = (ngx_http_variable_value_t *) gvvn->offset; | |
1341 range->start = r->start; | |
1342 range->end = r->end; | |
1343 range++; | |
1344 | |
1345 } while ((++r)->value); | |
1346 | |
1347 range->value = NULL; | |
1348 | |
1349 p = (u_char *) range + sizeof(void *); | |
1350 } | |
1351 | |
1352 header = fm.addr; | |
1353 header->crc32 = ngx_crc32_long((u_char *) fm.addr | |
1354 + sizeof(ngx_http_geo_header_t), | |
1355 fm.size - sizeof(ngx_http_geo_header_t)); | |
1356 | |
1357 ngx_close_file_mapping(&fm); | |
1358 } | |
1359 | |
1360 | |
1361 static u_char * | |
1362 ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, | |
1363 ngx_rbtree_node_t *sentinel) | |
1364 { | |
1365 ngx_http_variable_value_t *vv; | |
1366 ngx_http_geo_variable_value_node_t *gvvn; | |
1367 | |
1368 if (node == sentinel) { | |
1369 return p; | |
1370 } | |
1371 | |
1372 gvvn = (ngx_http_geo_variable_value_node_t *) node; | |
1373 gvvn->offset = p - base; | |
1374 | |
1375 vv = (ngx_http_variable_value_t *) p; | |
1376 *vv = *gvvn->value; | |
1377 p += sizeof(ngx_http_variable_value_t); | |
1378 vv->data = (u_char *) (p - base); | |
1379 | |
1380 p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len); | |
1381 | |
1382 p = ngx_align_ptr(p, sizeof(void *)); | |
1383 | |
1384 p = ngx_http_geo_copy_values(base, p, node->left, sentinel); | |
1385 | |
1386 return ngx_http_geo_copy_values(base, p, node->right, sentinel); | |
1387 } |