Skip to content

Commit 2311768

Browse files
authored
fix(balancer): quote ipv6 address (#7594)
Fixes #7563
1 parent 0c90081 commit 2311768

File tree

4 files changed

+193
-2
lines changed

4 files changed

+193
-2
lines changed

apisix/balancer.lua

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ local set_more_tries = balancer.set_more_tries
2626
local get_last_failure = balancer.get_last_failure
2727
local set_timeouts = balancer.set_timeouts
2828
local ngx_now = ngx.now
29+
local str_byte = string.byte
2930

3031

3132
local module_name = "balancer"
@@ -195,6 +196,12 @@ local function pick_server(route, ctx)
195196
core.log.info("ctx: ", core.json.delay_encode(ctx, true))
196197
local up_conf = ctx.upstream_conf
197198

199+
for _, node in ipairs(up_conf.nodes) do
200+
if core.utils.parse_ipv6(node.host) and str_byte(node.host, 1) ~= str_byte("[") then
201+
node.host = '[' .. node.host .. ']'
202+
end
203+
end
204+
198205
local nodes_count = #up_conf.nodes
199206
if nodes_count == 1 then
200207
local node = up_conf.nodes[1]

docs/en/latest/admin-api.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ In addition to the equalization algorithm selections, Upstream also supports pas
524524
| Name | Optional | Description | Example |
525525
| --------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
526526
| type | required | Load balancing algorithm to be used. | |
527-
| 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 receive a 502 response. | `192.168.1.100:80` |
527+
| 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 optional `port` and `priority`. Empty nodes are treated as placeholders and clients trying to access this Upstream will receive a 502 response. | `192.168.1.100:80`, `[::1]:80` |
528528
| service_name | required, can't be used with `nodes` | Service name used for [service discovery](discovery.md). | `a-bootiful-client` |
529529
| discovery_type | required, if `service_name` is used | The type of service [discovery](discovery.md). | `eureka` |
530530
| hash_on | optional | Only valid if the `type` is `chash`. Supports Nginx variables (`vars`), custom headers (`header`), `cookie` and `consumer`. Defaults to `vars`. | |

docs/zh/latest/admin-api.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ APISIX 的 Upstream 除了基本的负载均衡算法选择外,还支持对上
532532
| 名字 | 可选项 | 类型 | 说明 | 示例 |
533533
| -------------- | ---------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
534534
| type | 必需 | 枚举 | 负载均衡算法 | | |
535-
| 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` |
535+
| 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` |
536536
| service_name | 必需,不能和 `nodes` 一起用 | string | 服务发现时使用的服务名,见[集成服务发现注册中心](./discovery.md) | `a-bootiful-client` |
537537
| discovery_type | 必需,如果设置了 `service_name` | string | 服务发现类型,见 [集成服务发现注册中心](./discovery.md) | `eureka` |
538538
| 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) | |

t/node/upstream-ipv6.t

+184
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,187 @@ GET /hello
108108
hello world
109109
--- no_error_log
110110
[error]
111+
112+
113+
114+
=== TEST 5: set upstream(id: 1)
115+
--- config
116+
location /t {
117+
content_by_lua_block {
118+
local t = require("lib.test_admin").test
119+
local code, body = t('/apisix/admin/upstreams/1',
120+
ngx.HTTP_PUT,
121+
[[{
122+
"nodes": [
123+
{
124+
"weight": 100,
125+
"priority": 0,
126+
"host": "::1",
127+
"port": 1980
128+
}
129+
],
130+
"type": "roundrobin",
131+
"desc": "new upstream"
132+
}]]
133+
)
134+
135+
if code >= 300 then
136+
ngx.status = code
137+
end
138+
ngx.say(body)
139+
}
140+
}
141+
--- request
142+
GET /t
143+
--- response_body
144+
passed
145+
--- no_error_log
146+
[error]
147+
148+
149+
150+
=== TEST 6: hit routes
151+
--- request
152+
GET /hello
153+
--- response_body
154+
hello world
155+
--- no_error_log
156+
[error]
157+
158+
159+
160+
=== TEST 7: set upstream, one array item to specify node
161+
--- config
162+
location /t {
163+
content_by_lua_block {
164+
local t = require("lib.test_admin").test
165+
local code, body = t('/apisix/admin/upstreams/1',
166+
ngx.HTTP_PUT,
167+
[[{
168+
"nodes": [
169+
{
170+
"weight": 100,
171+
"priority": 0,
172+
"host": "[::1]",
173+
"port": 1980
174+
}
175+
],
176+
"type": "roundrobin",
177+
"desc": "new upstream"
178+
}]]
179+
)
180+
181+
if code >= 300 then
182+
ngx.status = code
183+
end
184+
ngx.say(body)
185+
}
186+
}
187+
--- request
188+
GET /t
189+
--- response_body
190+
passed
191+
--- no_error_log
192+
[error]
193+
194+
195+
196+
=== TEST 8: hit routes
197+
--- request
198+
GET /hello
199+
--- response_body
200+
hello world
201+
--- no_error_log
202+
[error]
203+
204+
205+
206+
=== TEST 9: set upstream, one hash key to specify node, in wrong format
207+
--- config
208+
location /t {
209+
content_by_lua_block {
210+
local t = require("lib.test_admin").test
211+
local code, body = t('/apisix/admin/upstreams/1',
212+
ngx.HTTP_PUT,
213+
[[{
214+
"nodes": {
215+
"::1:1980": 1
216+
},
217+
"type": "roundrobin",
218+
"desc": "new upstream"
219+
}]]
220+
)
221+
222+
if code >= 300 then
223+
ngx.status = code
224+
end
225+
ngx.say(body)
226+
}
227+
}
228+
--- request
229+
GET /t
230+
--- response_body
231+
passed
232+
--- no_error_log
233+
[error]
234+
235+
236+
237+
=== TEST 10: hit routes
238+
--- request
239+
GET /hello
240+
--- error_code: 502
241+
--- error_log
242+
connect() to [::0.1.25.128]:80 failed
243+
244+
245+
246+
=== TEST 11: set upstream, two array items to specify nodes
247+
--- config
248+
location /t {
249+
content_by_lua_block {
250+
local t = require("lib.test_admin").test
251+
local code, body = t('/apisix/admin/upstreams/1',
252+
ngx.HTTP_PUT,
253+
[[{
254+
"nodes": [
255+
{
256+
"weight": 100,
257+
"priority": 0,
258+
"host": "::1",
259+
"port": 1980
260+
},
261+
{
262+
"weight": 100,
263+
"priority": 0,
264+
"host": "::1",
265+
"port": 1980
266+
}
267+
],
268+
"type": "roundrobin",
269+
"desc": "new upstream"
270+
}]]
271+
)
272+
273+
if code >= 300 then
274+
ngx.status = code
275+
end
276+
ngx.say(body)
277+
}
278+
}
279+
--- request
280+
GET /t
281+
--- response_body
282+
passed
283+
--- no_error_log
284+
[error]
285+
286+
287+
288+
=== TEST 12: hit routes
289+
--- request
290+
GET /hello
291+
--- response_body
292+
hello world
293+
--- no_error_log
294+
[error]

0 commit comments

Comments
 (0)