You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/11/10 01:58:03 UTC

[apisix] 02/18: fix(balancer): quote ipv6 address (#7594)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch release/2.15
in repository https://gitbox.apache.org/repos/asf/apisix.git

commit 5d991fd1fa2bc544898ee9dd2a616068191b03f0
Author: jinhua luo <ho...@163.com>
AuthorDate: Thu Aug 4 19:37:40 2022 +0800

    fix(balancer): quote ipv6 address (#7594)
    
    Fixes #7563
---
 apisix/balancer.lua         |   7 ++
 docs/en/latest/admin-api.md |   2 +-
 docs/zh/latest/admin-api.md |   2 +-
 t/node/upstream-ipv6.t      | 184 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 193 insertions(+), 2 deletions(-)

diff --git a/apisix/balancer.lua b/apisix/balancer.lua
index e0cecdf06..d96b7a2a0 100644
--- a/apisix/balancer.lua
+++ b/apisix/balancer.lua
@@ -26,6 +26,7 @@ local set_more_tries   = balancer.set_more_tries
 local get_last_failure = balancer.get_last_failure
 local set_timeouts     = balancer.set_timeouts
 local ngx_now          = ngx.now
+local str_byte         = string.byte
 
 
 local module_name = "balancer"
@@ -195,6 +196,12 @@ local function pick_server(route, ctx)
     core.log.info("ctx: ", core.json.delay_encode(ctx, true))
     local up_conf = ctx.upstream_conf
 
+    for _, node in ipairs(up_conf.nodes) do
+        if core.utils.parse_ipv6(node.host) and str_byte(node.host, 1) ~= str_byte("[") then
+            node.host = '[' .. node.host .. ']'
+        end
+    end
+
     local nodes_count = #up_conf.nodes
     if nodes_count == 1 then
         local node = up_conf.nodes[1]
diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md
index deaf1c4c7..cb551cd15 100644
--- a/docs/en/latest/admin-api.md
+++ b/docs/en/latest/admin-api.md
@@ -524,7 +524,7 @@ In addition to the equalization algorithm selections, Upstream also supports pas
 | Name                        | Optional                                    | Description                                                                                                                                                                                                                                                                                                                                                                                                                    [...]
 | --------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...]
 | type                        | required                                    | Load balancing algorithm to be used.                                                                                                                                                                                                                                                                                                                                                                                           [...]
-| nodes                       | required, can't be used with `service_name` | IP addresses (with optional ports) of the Upstream nodes represented as a hash table or an array. In the hash table, the key is the IP address and the value is the weight of the node for the load balancing algorithm. In the array, each item is a hash table with keys `host`, `weight`, and the optional `port` and `priority`. Empty nodes are treated as placeholders and clients trying to access this Upstream will r [...]
+| nodes                       | required, can't be used with `service_name` | IP addresses (with optional ports) of the Upstream nodes represented as a hash table or an array. In the hash table, the key is the IP address and the value is the weight of the node for the load balancing algorithm. For hash table case, if the key is IPv6 address with port, then the IPv6 address must be quoted with square brackets. In the array, each item is a hash table with keys `host`, `weight`, and the opt [...]
 | service_name                | required, can't be used with `nodes`        | Service name used for [service discovery](discovery.md).                                                                                                                                                                                                                                                                                                                                                                       [...]
 | discovery_type              | required, if `service_name` is used         | The type of service [discovery](discovery.md).                                                                                                                                                                                                                                                                                                                                                                                 [...]
 | hash_on                     | optional                                    | Only valid if the `type` is `chash`. Supports Nginx variables (`vars`), custom headers (`header`), `cookie` and `consumer`. Defaults to `vars`.                                                                                                                                                                                                                                                                                [...]
diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md
index e5dc14202..0eea807f2 100644
--- a/docs/zh/latest/admin-api.md
+++ b/docs/zh/latest/admin-api.md
@@ -532,7 +532,7 @@ APISIX 的 Upstream 除了基本的负载均衡算法选择外,还支持对上
 | 名字           | 可选项                             | 类型           | 说明                                                                                                                                                                                                                                                                                                                                                        | 示例                                             |
 | -------------- | ---------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
 | type           | 必需                               | 枚举           | 负载均衡算法                                                                                                                                                                                                                                                                                                                                                            |                                      |     |
-| nodes          | 必需,不能和 `service_name` 一起用 | Node           | 哈希表或数组。当它是哈希表时,内部元素的 key 是上游机器地址列表,格式为`地址 +(可选的)端口`,其中地址部分可以是 IP 也可以是域名,比如 `192.168.1.100:80`、`foo.com:80`等。value 则是节点的权重。当它是数组时,数组中每个元素都是一个哈希表,其中包含 `host`、`weight` 以及可选的 `port`、`priority`。`nodes` 可以为空,这通常用作占位符。客户端命中这样的上游会返回 502。                                        | `192.168.1.100:80`                               |
+| nodes          | 必需,不能和 `service_name` 一起用 | Node           | 哈希表或数组。当它是哈希表时,内部元素的 key 是上游机器地址列表,格式为`地址 +(可选的)端口`,其中地址部分可以是 IP 也可以是域名,比如 `192.168.1.100:80`、`foo.com:80`等。对于哈希表的情况,如果 key 是 IPv6 地址加端口,则必须用中括号将 IPv6 地址括起来。value 则是节点的权重。当它是数组时,数组中每个元素都是一个哈希表,其中包含 `host`、`weight` 以及可选的 `port`、`priority`。`nodes` 可以为空,这通常用作占位符。客户端命中这样的上游会返回 502。                                        | `192.168.1.100:80`, `[::1]:80`                               |
 | service_name   | 必需,不能和 `nodes` 一起用        | string         | 服务发现时使用的服务名,见[集成服务发现注册中心](./discovery.md)                                                                                                                                                                                                                                                                                            | `a-bootiful-client`                              |
 | discovery_type | 必需,如果设置了 `service_name`    | string         | 服务发现类型,见 [集成服务发现注册中心](./discovery.md)                                                                                                                                                                                                                                                                                                      | `eureka`                                         |
 | key            | 条件必需                           | 匹配类型       | 该选项只有类型是 `chash` 才有效。根据 `key` 来查找对应的 node `id`,相同的 `key` 在同一个对象中,永远返回相同 id,目前支持的 Nginx 内置变量有 `uri, server_name, server_addr, request_uri, remote_port, remote_addr, query_string, host, hostname, arg_***`,其中 `arg_***` 是来自 URL 的请求参数,[Nginx 变量列表](http://nginx.org/en/docs/varindex.html) |                                                  |
diff --git a/t/node/upstream-ipv6.t b/t/node/upstream-ipv6.t
index 51d2e8b84..8aa39f6cf 100644
--- a/t/node/upstream-ipv6.t
+++ b/t/node/upstream-ipv6.t
@@ -108,3 +108,187 @@ GET /hello
 hello world
 --- no_error_log
 [error]
+
+
+
+=== TEST 5: set upstream(id: 1)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/upstreams/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "nodes": [
+                        {
+                            "weight": 100,
+                            "priority": 0,
+                            "host": "::1",
+                            "port": 1980
+                        }
+                    ],
+                    "type": "roundrobin",
+                    "desc": "new upstream"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: hit routes
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: set upstream, one array item to specify node
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/upstreams/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "nodes": [
+                        {
+                            "weight": 100,
+                            "priority": 0,
+                            "host": "[::1]",
+                            "port": 1980
+                        }
+                    ],
+                    "type": "roundrobin",
+                    "desc": "new upstream"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 8: hit routes
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 9: set upstream, one hash key to specify node, in wrong format
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/upstreams/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "nodes": {
+                        "::1:1980": 1
+                    },
+                    "type": "roundrobin",
+                    "desc": "new upstream"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: hit routes
+--- request
+GET /hello
+--- error_code: 502
+--- error_log
+connect() to [::0.1.25.128]:80 failed
+
+
+
+=== TEST 11: set upstream, two array items to specify nodes
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/upstreams/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "nodes": [
+                        {
+                            "weight": 100,
+                            "priority": 0,
+                            "host": "::1",
+                            "port": 1980
+                        },
+                        {
+                            "weight": 100,
+                            "priority": 0,
+                            "host": "::1",
+                            "port": 1980
+                        }
+                    ],
+                    "type": "roundrobin",
+                    "desc": "new upstream"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 12: hit routes
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]