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 }