diff src/http/modules/ngx_http_geo_module.c @ 430:dac47e9ef0d5 NGINX_0_7_27

nginx 0.7.27 *) Feature: the "try_files" directive. *) Feature: variables support in the "fastcgi_pass" directive. *) Feature: now the $geo variable may get an address from a variable. Thanks to Andrei Nigmatulin. *) Feature: now a location's modifier may be used without space before name. *) Feature: the $upstream_response_length variable. *) Bugfix: now a "add_header" directive does not add an empty value. *) Bugfix: if zero length static file was requested, then nginx just closed connection; the bug had appeared in 0.7.25. *) Bugfix: a MOVE method could not move file in non-existent directory. *) Bugfix: a segmentation fault occurred in worker process, if no one named location was defined in server, but some one was used in an error_page directive. Thanks to Sergey Bochenkov.
author Igor Sysoev <http://sysoev.ru>
date Mon, 15 Dec 2008 00:00:00 +0300
parents e7dbea1ee115
children ce4f9ff90bfa
line wrap: on
line diff
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -10,36 +10,48 @@
 
 
 typedef struct {
-    u_short                      start;
-    u_short                      end;
-    ngx_http_variable_value_t   *value;
+    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_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_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_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;
 
 
+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(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,
@@ -57,7 +69,7 @@ static ngx_http_variable_value_t *ngx_ht
 static ngx_command_t  ngx_http_geo_commands[] = {
 
     { 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_MAIN_CONF_OFFSET,
       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,
     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_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));
+              ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
 
     *v = *vv;
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %v", &r->connection->addr_text, v);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
 
     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,
     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;
     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 = *ctx->u.high->default_value;
 
-    *v = *high->default_value;
+    addr = ngx_http_geo_addr(r, ctx);
 
-    addr = ntohl(sin->sin_addr.s_addr);
-
-    range = high->low[addr >> 16].ranges;
+    range = ctx->u.high->low[addr >> 16].ranges;
 
     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
             && n <= (ngx_uint_t) range[i].end)
         {
@@ -158,13 +158,43 @@ ngx_http_geo_range_variable(ngx_http_req
         }
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %v", &r->connection->addr_text, v);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
 
     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 *
 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -176,19 +206,33 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
     ngx_pool_t               *pool;
     ngx_array_t              *a;
     ngx_http_variable_t      *var;
+    ngx_http_geo_ctx_t       *geo;
     ngx_http_geo_conf_ctx_t   ctx;
 
     value = cf->args->elts;
 
+    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 (name.data[0] != '$') {
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "\"%V\" variable name should start with '$'",
-                           &value[1]);
-    } else {
+    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);
@@ -244,8 +288,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
             ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
         }
 
+        geo->u.high = ctx.high;
+
         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(pool);
@@ -262,8 +308,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
             }
         }
 
+        geo->u.tree = ctx.tree;
+
         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(pool);
@@ -644,22 +692,29 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
             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) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid network \"%V\"", net);
-            return NGX_CONF_ERROR;
-        }
+        } else {
+            rc = ngx_ptocidr(net, &cidrin);
 
-        if (rc == NGX_DONE) {
-            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                               "low address bits of %V are meaningless", net);
+            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);
         }
 
-        cidrin.addr = ntohl(cidrin.addr);
-        cidrin.mask = ntohl(cidrin.mask);
-
         if (del) {
             if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
                 != NGX_OK)