$geo variable support

This commit is contained in:
Igor Sysoev 2008-12-11 09:46:45 +00:00
parent 976603a646
commit 9a1d46684c

View File

@ -10,36 +10,48 @@
typedef struct { typedef struct {
u_short start; u_short start;
u_short end; u_short end;
ngx_http_variable_value_t *value; ngx_http_variable_value_t *value;
} ngx_http_geo_range_t; } ngx_http_geo_range_t;
typedef struct { typedef struct {
ngx_http_geo_range_t *ranges; ngx_http_geo_range_t *ranges;
ngx_uint_t n; ngx_uint_t n;
} ngx_http_geo_low_ranges_t; } ngx_http_geo_low_ranges_t;
typedef struct { typedef struct {
ngx_http_geo_low_ranges_t low[0x10000]; ngx_http_geo_low_ranges_t low[0x10000];
ngx_http_variable_value_t *default_value; ngx_http_variable_value_t *default_value;
} ngx_http_geo_high_ranges_t; } ngx_http_geo_high_ranges_t;
typedef struct { typedef struct {
ngx_http_variable_value_t *value; ngx_http_variable_value_t *value;
ngx_str_t *net; ngx_str_t *net;
ngx_http_geo_high_ranges_t *high; ngx_http_geo_high_ranges_t *high;
ngx_radix_tree_t *tree; ngx_radix_tree_t *tree;
ngx_rbtree_t rbtree; ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel; ngx_rbtree_node_t sentinel;
ngx_pool_t *pool; ngx_pool_t *pool;
ngx_pool_t *temp_pool; ngx_pool_t *temp_pool;
} ngx_http_geo_conf_ctx_t; } ngx_http_geo_conf_ctx_t;
typedef struct {
union {
ngx_radix_tree_t *tree;
ngx_http_geo_high_ranges_t *high;
} u;
ngx_int_t index;
} ngx_http_geo_ctx_t;
static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
ngx_http_geo_ctx_t *ctx);
static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 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(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, static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
@ -57,7 +69,7 @@ static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
static ngx_command_t ngx_http_geo_commands[] = { static ngx_command_t ngx_http_geo_commands[] = {
{ ngx_string("geo"), { ngx_string("geo"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
ngx_http_geo_block, ngx_http_geo_block,
NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_MAIN_CONF_OFFSET,
0, 0,
@ -104,23 +116,17 @@ static ngx_int_t
ngx_http_geo_cidr_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) uintptr_t data)
{ {
ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
struct sockaddr_in *sin;
ngx_http_variable_value_t *vv; ngx_http_variable_value_t *vv;
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 *) vv = (ngx_http_variable_value_t *)
ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
*v = *vv; *v = *vv;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo: %V %v", &r->connection->addr_text, v); "http geo: %v", v);
return NGX_OK; return NGX_OK;
} }
@ -130,27 +136,21 @@ static ngx_int_t
ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data) uintptr_t data)
{ {
ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data; ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
in_addr_t addr; in_addr_t addr;
ngx_uint_t i, n; ngx_uint_t i, n;
struct sockaddr_in *sin;
ngx_http_geo_range_t *range; ngx_http_geo_range_t *range;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, *v = *ctx->u.high->default_value;
"http geo started");
sin = (struct sockaddr_in *) r->connection->sockaddr; addr = ngx_http_geo_addr(r, ctx);
*v = *high->default_value; range = ctx->u.high->low[addr >> 16].ranges;
addr = ntohl(sin->sin_addr.s_addr);
range = high->low[addr >> 16].ranges;
n = addr & 0xffff; n = addr & 0xffff;
for (i = 0; i < high->low[addr >> 16].n; i++) { for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
if (n >= (ngx_uint_t) range[i].start if (n >= (ngx_uint_t) range[i].start
&& n <= (ngx_uint_t) range[i].end) && n <= (ngx_uint_t) range[i].end)
{ {
@ -158,29 +158,84 @@ ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
} }
} }
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo: %V %v", &r->connection->addr_text, v); "http geo: %v", v);
return NGX_OK; return NGX_OK;
} }
static in_addr_t
ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
{
struct sockaddr_in *sin;
ngx_http_variable_value_t *v;
if (ctx->index == -1) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo started: %V", &r->connection->addr_text);
sin = (struct sockaddr_in *) r->connection->sockaddr;
return ntohl(sin->sin_addr.s_addr);
}
v = ngx_http_get_flushed_variable(r, ctx->index);
if (v == NULL || v->not_found) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo not found");
return 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo started: %v", v);
return ntohl(ngx_inet_addr(v->data, v->len));
}
static char * static char *
ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ {
char *rv; char *rv;
size_t len; size_t len;
ngx_str_t *value; ngx_str_t *value, name;
ngx_uint_t i; ngx_uint_t i;
ngx_conf_t save; ngx_conf_t save;
ngx_pool_t *pool; ngx_pool_t *pool;
ngx_array_t *a; ngx_array_t *a;
ngx_http_variable_t *var; ngx_http_variable_t *var;
ngx_http_geo_ctx_t *geo;
ngx_http_geo_conf_ctx_t ctx; ngx_http_geo_conf_ctx_t ctx;
value = cf->args->elts; value = cf->args->elts;
var = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE); geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
if (geo == NULL) {
return NGX_CONF_ERROR;
}
name = value[1];
name.len--;
name.data++;
if (cf->args->nelts == 3) {
geo->index = ngx_http_get_variable_index(cf, &name);
if (geo->index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
name = value[2];
name.len--;
name.data++;
} else {
geo->index = -1;
}
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) { if (var == NULL) {
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
@ -233,8 +288,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
} }
geo->u.high = ctx.high;
var->get_handler = ngx_http_geo_range_variable; var->get_handler = ngx_http_geo_range_variable;
var->data = (uintptr_t) ctx.high; var->data = (uintptr_t) geo;
ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool); ngx_destroy_pool(pool);
@ -251,8 +308,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
} }
} }
geo->u.tree = ctx.tree;
var->get_handler = ngx_http_geo_cidr_variable; var->get_handler = ngx_http_geo_cidr_variable;
var->data = (uintptr_t) ctx.tree; var->data = (uintptr_t) geo;
ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool); ngx_destroy_pool(pool);
@ -633,22 +692,29 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
del = 0; del = 0;
} }
rc = ngx_ptocidr(net, &cidrin); if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
cidrin.addr = 0xffffffff;
cidrin.mask = 0xffffffff;
if (rc == NGX_ERROR) { } else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rc = ngx_ptocidr(net, &cidrin);
"invalid network \"%V\"", net);
return NGX_CONF_ERROR; if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 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",
net);
}
cidrin.addr = ntohl(cidrin.addr);
cidrin.mask = ntohl(cidrin.mask);
} }
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless", net);
}
cidrin.addr = ntohl(cidrin.addr);
cidrin.mask = ntohl(cidrin.mask);
if (del) { if (del) {
if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask) if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
!= NGX_OK) != NGX_OK)