# HG changeset patch # User Igor Sysoev # Date 1227733200 -10800 # Node ID 88d3e895bdf94af51c38b5da5a986ff942dd52c5 # Parent 10e4013f5f54ace036cabebaebb36b7b2fbb8f5f 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. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,13 @@ +Changes with nginx 0.7.23 27 Nov 2008 + + *) 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. + + Changes with nginx 0.7.22 20 Nov 2008 *) Feature: the "none" parameter in the "smtp_auth" directive. @@ -1765,7 +1774,7 @@ Changes with nginx 0.3.55 *) Bugfix: if the request contained "//" or "/./" and escaped symbols after them, then the proxied request was sent unescaped. - *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now + *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now returns all "Cookie" header lines. *) Bugfix: a segmentation fault occurred if diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,13 @@ +Изменения в nginx 0.7.23 27.11.2008 + + *) Добавление: параметры delete и ranges в директиве geo. + + *) Добавление: ускорение загрузки geo-базы с большим числом значений. + + *) Добавление: уменьшение памяти, необходимой для загрузки geo-базы. + + Изменения в nginx 0.7.22 20.11.2008 *) Добавление: параметр none в директиве smtp_auth. @@ -1812,7 +1821,7 @@ закодированные символы в виде "%XX", то проксируемый запрос передавался незакодированным. - *) Исправление: метод $r->headers_in("Cookie") модуля + *) Исправление: метод $r->header_in("Cookie") модуля ngx_http_perl_module теперь возвращает все строки "Cookie" в заголовке запроса. diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.7.22" +#define NGINX_VERSION "0.7.23" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -98,8 +98,8 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t char *rv; ngx_fd_t fd; ngx_int_t rc; - ngx_buf_t *b; - ngx_conf_file_t *prev; + ngx_buf_t buf; + ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, @@ -125,32 +125,24 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t prev = cf->conf_file; - cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)); - if (cf->conf_file == NULL) { - return NGX_CONF_ERROR; - } + cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } - b = ngx_calloc_buf(cf->pool); - if (b == NULL) { - return NGX_CONF_ERROR; + cf->conf_file->buffer = &buf; + + buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); + if (buf.start == NULL) { + goto failed; } - cf->conf_file->buffer = b; - - b->start = ngx_alloc(NGX_CONF_BUFFER, cf->log); - if (b->start == NULL) { - return NGX_CONF_ERROR; - } - - b->pos = b->start; - b->last = b->start; - b->end = b->last + NGX_CONF_BUFFER; - b->temporary = 1; + buf.pos = buf.start; + buf.last = buf.start; + buf.end = buf.last + NGX_CONF_BUFFER; + buf.temporary = 1; cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; @@ -256,9 +248,9 @@ failed: done: if (filename) { - ngx_free(cf->conf_file->buffer->start); - - cf->conf_file = prev; + if (cf->conf_file->buffer->start) { + ngx_free(cf->conf_file->buffer->start); + } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, @@ -266,6 +258,8 @@ done: cf->conf_file->file.name.data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -834,7 +828,7 @@ ngx_conf_full_name(ngx_cycle_t *cycle, n name->len = len + old.len; name->data = ngx_pnalloc(cycle->pool, name->len + 1); if (name->data == NULL) { - return NGX_ERROR; + return NGX_ERROR; } p = ngx_cpymem(name->data, prefix, len); @@ -959,31 +953,47 @@ void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vsnprintf(errstr, last - errstr, fmt, args); va_end(args); - *buf = '\0'; + if (err) { + + if (p > last - 50) { + + /* leave a space for an error code */ - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + p = last - 50; + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + +#if (NGX_WIN32) + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + p = ngx_snprintf(p, last - p, " (%d: ", err); +#endif + + p = ngx_strerror_r(err, p, last - p); + + *p++ = ')'; } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h --- a/src/core/ngx_conf_file.h +++ b/src/core/ngx_conf_file.h @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -91,6 +91,26 @@ ngx_destroy_pool(ngx_pool_t *pool) } +void +ngx_reset_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + pool->large = NULL; + + for (p = pool; p; p = p->d.next) { + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + } +} + + void * ngx_palloc(ngx_pool_t *pool, size_t size) { diff --git a/src/core/ngx_palloc.h b/src/core/ngx_palloc.h --- a/src/core/ngx_palloc.h +++ b/src/core/ngx_palloc.h @@ -73,6 +73,7 @@ void *ngx_calloc(size_t size, ngx_log_t ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); void ngx_destroy_pool(ngx_pool_t *pool); +void ngx_reset_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size); void *ngx_pnalloc(ngx_pool_t *pool, size_t size); diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -10,14 +10,48 @@ typedef struct { - ngx_radix_tree_t *tree; - ngx_pool_t *pool; - ngx_array_t values; + u_short start; + u_short end; + ngx_http_variable_value_t *value; +} ngx_http_geo_range_t; + + +typedef struct { + ngx_http_geo_range_t *ranges; + ngx_uint_t n; +} ngx_http_geo_low_ranges_t; + + +typedef struct { + ngx_http_geo_low_ranges_t low[0x10000]; + ngx_http_variable_value_t *default_value; +} ngx_http_geo_high_ranges_t; + + +typedef struct { + ngx_http_variable_value_t *value; + ngx_str_t *net; + ngx_http_geo_high_ranges_t *high; + ngx_radix_tree_t *tree; + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; } ngx_http_geo_conf_ctx_t; static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static char *ngx_http_geo_add_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value); +static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static ngx_command_t ngx_http_geo_commands[] = { @@ -67,7 +101,7 @@ ngx_module_t ngx_http_geo_module = { /* AF_INET only */ static ngx_int_t -ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, +ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; @@ -75,11 +109,11 @@ ngx_http_geo_variable(ngx_http_request_t struct sockaddr_in *sin; ngx_http_variable_value_t *vv; - sin = (struct sockaddr_in *) r->connection->sockaddr; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo started"); + sin = (struct sockaddr_in *) r->connection->sockaddr; + vv = (ngx_http_variable_value_t *) ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); @@ -92,14 +126,55 @@ ngx_http_geo_variable(ngx_http_request_t } +static ngx_int_t +ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data; + + in_addr_t addr; + ngx_uint_t i, n; + struct sockaddr_in *sin; + ngx_http_geo_range_t *range; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started"); + + sin = (struct sockaddr_in *) r->connection->sockaddr; + + *v = *high->default_value; + + addr = ntohl(sin->sin_addr.s_addr); + + range = high->low[addr >> 16].ranges; + + n = addr & 0xffff; + + for (i = 0; i < high->low[addr >> 16].n; i++) { + if (n >= (ngx_uint_t) range[i].start + && n <= (ngx_uint_t) range[i].end) + { + *v = *range[i].value; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo: %V %v", &r->connection->addr_text, v); + + return NGX_OK; +} + + static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + size_t len; ngx_str_t *value, name; + ngx_uint_t i; ngx_conf_t save; ngx_pool_t *pool; - ngx_radix_tree_t *tree; + ngx_array_t *a; ngx_http_variable_t *var; ngx_http_geo_conf_ctx_t ctx; @@ -121,29 +196,21 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } - tree = ngx_radix_tree_create(cf->pool, -1); - - if (tree == NULL) { - return NGX_CONF_ERROR; - } - - var->get_handler = ngx_http_geo_variable; - var->data = (uintptr_t) tree; - pool = ngx_create_pool(16384, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } - if (ngx_array_init(&ctx.values, pool, 512, - sizeof(ngx_http_variable_value_t *)) - != NGX_OK) - { - ngx_destroy_pool(pool); + ctx.temp_pool = ngx_create_pool(16384, cf->log); + if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } - ctx.tree = tree; + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, + ngx_http_variable_value_rbtree_insert); + + ctx.high = NULL; + ctx.tree = NULL; ctx.pool = cf->pool; save = *cf; @@ -156,121 +223,456 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c *cf = save; - ngx_destroy_pool(pool); + if (ctx.high) { + + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high->low[i].ranges; + + if (a == NULL || a->nelts == 0) { + continue; + } + + ctx.high->low[i].n = a->nelts; - if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { - return rv; - } + len = a->nelts * sizeof(ngx_http_geo_range_t); + + ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); + if (ctx.high->low[i].ranges == NULL ){ + return NGX_CONF_ERROR; + } + + ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); + } + + var->get_handler = ngx_http_geo_range_variable; + var->data = (uintptr_t) ctx.high; - if (ngx_radix32tree_insert(tree, 0, 0, - (uintptr_t) &ngx_http_variable_null_value) - == NGX_ERROR) - { - return NGX_CONF_ERROR; + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ctx.high->default_value == NULL) { + ctx.high->default_value = &ngx_http_variable_null_value; + } + + } else { + var->get_handler = ngx_http_geo_cidr_variable; + var->data = (uintptr_t) ctx.tree; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { + return rv; + } + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_http_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } } return rv; } -/* AF_INET only */ - static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t rc; - ngx_str_t *value, file; - ngx_uint_t i; - ngx_inet_cidr_t cidrin; - ngx_http_geo_conf_ctx_t *ctx; - ngx_http_variable_value_t *var, *old, **v; + char *rv; + ngx_str_t *value, file; + ngx_http_geo_conf_ctx_t *ctx; ctx = cf->ctx; + value = cf->args->elts; + + if (cf->args->nelts == 1) { + + if (ngx_strcmp(value[0].data, "ranges") == 0) { + + if (ctx->tree) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ranges\" directive must be " + "the first directive inside \"geo\" block"); + goto failed; + } + + ctx->high = ngx_pcalloc(ctx->pool, + sizeof(ngx_http_geo_high_ranges_t)); + if (ctx->high == NULL) { + goto failed; + } + + rv = NGX_CONF_OK; + + goto done; + } + } + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the geo parameters"); - return NGX_CONF_ERROR; + goto failed; } - value = cf->args->elts; + if (ngx_strcmp(value[0].data, "include") == 0) { + + file.len = value[1].len++; - if (ngx_strcmp(value[0].data, "include") == 0) { - file = value[1]; + file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); + if (file.data == NULL) { + goto failed; + } - if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){ - return NGX_CONF_ERROR; + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ + goto failed; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - return ngx_conf_parse(cf, &file); + rv = ngx_conf_parse(cf, &file); + + goto done; + } + + if (ctx->high) { + rv = ngx_http_geo_range(cf, ctx, value); + + } else { + rv = ngx_http_geo_cidr(cf, ctx, value); + } + +done: + + ngx_reset_pool(cf->pool); + + return rv; + +failed: + + ngx_reset_pool(cf->pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; + ngx_http_variable_value_t *old; + + if (ngx_strcmp(value[0].data, "default") == 0) { + + old = ctx->high->default_value; + + ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); + if (ctx->high->default_value == NULL) { + return NGX_CONF_ERROR; + } + + if (old) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + &value[0], ctx->high->default_value, old); + } + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + last = net->data + net->len; + + p = ngx_strlchr(net->data, last, '-'); + + if (p == NULL) { + goto invalid; + } + + start = ngx_inet_addr(net->data, p - net->data); + + if (start == INADDR_NONE) { + goto invalid; + } + + start = ntohl(start); + + p++; + + end = ngx_inet_addr(p, last - p); + + if (end == INADDR_NONE) { + goto invalid; + } + + end = ntohl(end); + + if (start > end) { + goto invalid; + } + + if (del) { + if (ngx_http_geo_delete_range(cf, ctx, start, end)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no address range \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + ctx->value = ngx_http_geo_value(cf, ctx, &value[1]); + + if (ctx->value == NULL) { + return NGX_CONF_ERROR; + } + + ctx->net = net; + + return ngx_http_geo_add_range(cf, ctx, start, end); + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); + + return NGX_CONF_ERROR; +} + + +/* the add procedure is optimized to add a growing up sequence */ + +static char * +ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + for (n = start; n < end; n += 0x10000) { + + h = n >> 16; + s = n & 0xffff; + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + a = ngx_array_create(ctx->temp_pool, 64, + sizeof(ngx_http_geo_range_t)); + if (a == NULL) { + return NGX_CONF_ERROR; + } + + ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; + } + + i = a->nelts; + range = a->elts; + + while (i) { + + i--; + + if (e < (ngx_uint_t) range[i].start) { + continue; + } + + if (s > (ngx_uint_t) range[i].end) { + + /* add after the range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memcpy(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); + + range = &range[i + 1]; + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + ctx->net, ctx->value, range[i].value); + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "overlapped range \"%V\"", ctx->net); + + return NGX_CONF_ERROR; + } + + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + next: + + range->start = (u_short) s; + range->end = (u_short) e; + range->value = ctx->value; + } + + return NGX_CONF_OK; +} + + +static ngx_uint_t +ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e, warn; + ngx_array_t *a; + ngx_http_geo_range_t *range; + + warn = 0; + + for (n = start; n < end; n += 0x10000) { + + h = n >> 16; + s = n & 0xffff; + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high->low[h].ranges; + + if (a == NULL) { + warn = 1; + continue; + } + + range = a->elts; + for (i = 0; i < a->nelts; i++) { + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_memcpy(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + break; + } + + if (s != (ngx_uint_t) range[i].start + && e != (ngx_uint_t) range[i].end) + { + continue; + } + + warn = 1; + } + } + + return warn; +} + + +static char * +ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + ngx_int_t rc, del; + ngx_str_t *net; + ngx_uint_t i; + ngx_inet_cidr_t cidrin; + ngx_http_variable_value_t *val, *old; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { + return NGX_CONF_ERROR; + } } if (ngx_strcmp(value[0].data, "default") == 0) { cidrin.addr = 0; cidrin.mask = 0; + net = &value[0]; } else { - rc = ngx_ptocidr(&value[0], &cidrin); + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + rc = ngx_ptocidr(net, &cidrin); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[0]); + "invalid network \"%V\"", net); return NGX_CONF_ERROR; } if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "low address bits of %V are meaningless", - &value[0]); + "low address bits of %V are meaningless", net); } cidrin.addr = ntohl(cidrin.addr); cidrin.mask = ntohl(cidrin.mask); - } - var = NULL; - v = ctx->values.elts; - - for (i = 0; i < ctx->values.nelts; i++) { - if ((size_t) v[i]->len != value[1].len) { - continue; - } - - if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) { - var = v[i]; - break; + if (del) { + if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + return NGX_CONF_OK; + } } } - if (var == NULL) { - var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); - if (var == NULL) { - return NGX_CONF_ERROR; - } + val = ngx_http_geo_value(cf, ctx, &value[1]); - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->pool, &value[1]); - if (var->data == NULL) { - return NGX_CONF_ERROR; - } - - var->valid = 1; - var->no_cacheable = 0; - var->not_found = 0; - - v = ngx_array_push(&ctx->values); - if (v == NULL) { - return NGX_CONF_ERROR; - } - - *v = var; + if (val == NULL) { + return NGX_CONF_ERROR; } for (i = 2; i; i--) { rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, - (uintptr_t) var); + (uintptr_t) val); if (rc == NGX_OK) { return NGX_CONF_OK; } @@ -282,18 +684,65 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command /* rc == NGX_BUSY */ old = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); + ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", - &value[0], var, old); + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); return NGX_CONF_ERROR; } } return NGX_CONF_ERROR; } + + +static ngx_http_variable_value_t * +ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + uint32_t hash; + ngx_http_variable_value_t *val; + ngx_http_variable_value_node_t *vvn; + + hash = ngx_crc32_long(value->data, value->len); + + val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); + + if (val) { + return val; + } + + val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); + if (val == NULL) { + return NULL; + } + + val->len = value->len; + val->data = ngx_pstrdup(ctx->pool, value); + if (val->data == NULL) { + return NULL; + } + + val->valid = 1; + val->no_cacheable = 0; + val->not_found = 0; + + vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); + if (vvn == NULL) { + return NULL; + } + + vvn->node.key = hash; + vvn->len = val->len; + vvn->value = val; + + ngx_rbtree_insert(&ctx->rbtree, &vvn->node); + + return val; +} diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.7.22'; +our $VERSION = '0.7.23'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -1633,3 +1633,87 @@ ngx_http_variables_init_vars(ngx_conf_t return NGX_OK; } + + +void +ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_variable_value_node_t *vvn, *vvt; + + for ( ;; ) { + + vvn = (ngx_http_variable_value_node_t *) node; + vvt = (ngx_http_variable_value_node_t *) temp; + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (vvn->len != vvt->len) { + + p = (vvn->len < vvt->len) ? &temp->left : &temp->right; + + } else { + p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +ngx_http_variable_value_t * +ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_variable_value_node_t *vvn; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + vvn = (ngx_http_variable_value_node_t *) node; + + if (hash != node->key) { + node = (hash < node->key) ? node->left : node->right; + continue; + } + + if (val->len != vvn->len) { + node = (val->len < vvn->len) ? node->left : node->right; + continue; + } + + rc = ngx_memcmp(val->data, vvn->value->data, val->len); + + if (rc < 0) { + node = node->left; + continue; + } + + if (rc > 0) { + node = node->right; + continue; + } + + return vvn->value; + } + + return NULL; +} diff --git a/src/http/ngx_http_variables.h b/src/http/ngx_http_variables.h --- a/src/http/ngx_http_variables.h +++ b/src/http/ngx_http_variables.h @@ -63,6 +63,19 @@ ngx_int_t ngx_http_variables_add_core_va ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf); +typedef struct { + ngx_rbtree_node_t node; + size_t len; + ngx_http_variable_value_t *value; +} ngx_http_variable_value_node_t; + + +void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, + ngx_str_t *name, uint32_t hash); + + extern ngx_http_variable_value_t ngx_http_variable_null_value; extern ngx_http_variable_value_t ngx_http_variable_true_value; diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c --- a/src/os/unix/ngx_errno.c +++ b/src/os/unix/ngx_errno.c @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char * /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0';