mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-22 02:53:09 +00:00
[httpc] 解决几个ipv6下面的请求规范的问题 (#2149)
* 按照RFC 3986的规范处理ipv6 host的格式化问题,ipv6地址需要用 []括起来 参考: https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2 * 解决ipv6格式不对的问题 * 只有域名的情况下才 set_ext_host_name
This commit is contained in:
@@ -33,11 +33,11 @@ local function parse_host(host)
|
|||||||
|
|
||||||
if host:find(":", colon1 + 1, true) then
|
if host:find(":", colon1 + 1, true) then
|
||||||
-- two or more colons: ipv6
|
-- two or more colons: ipv6
|
||||||
local ipv6, port = host:match("^%[(.-)%]:(%d+)$")
|
local ipv6, port = host:match("^%[(.-)%]:?(%d*)$")
|
||||||
if ipv6 then
|
if ipv6 then
|
||||||
return ipv6, tonumber(port), "ipv6"
|
return ipv6, port ~= "" and tonumber(port) or nil, "ipv6"
|
||||||
end
|
end
|
||||||
return host, nil, "ipv6"
|
error(string.format("Invalid host: bare IPv6 address '%s', use '[%s]' instead", host, host))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- single colon: host:port
|
-- single colon: host:port
|
||||||
@@ -57,9 +57,14 @@ local function parse_url(host)
|
|||||||
protocol = "http"
|
protocol = "http"
|
||||||
hostname = host
|
hostname = host
|
||||||
end
|
end
|
||||||
local htype
|
local hostheader = hostname
|
||||||
|
local htype, port
|
||||||
hostname, port, htype = parse_host(hostname)
|
hostname, port, htype = parse_host(hostname)
|
||||||
return protocol, hostname, port or default_port[protocol] or error("Invalid protocol: " .. protocol), htype
|
port = port or default_port[protocol]
|
||||||
|
if not port then
|
||||||
|
error("Invalid protocol: " .. protocol)
|
||||||
|
end
|
||||||
|
return protocol, hostname, port, htype, hostheader
|
||||||
end
|
end
|
||||||
|
|
||||||
local SSLCTX_CLIENT = nil
|
local SSLCTX_CLIENT = nil
|
||||||
@@ -91,7 +96,7 @@ local function gen_interface(protocol, fd, hostname)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function connect(host, timeout)
|
local function connect(host, timeout)
|
||||||
local protocol, hostname, port, htype = parse_url(host)
|
local protocol, hostname, port, htype, hostheader = parse_url(host)
|
||||||
local hostaddr = hostname
|
local hostaddr = hostname
|
||||||
if htype == "hostname" then
|
if htype == "hostname" then
|
||||||
if async_dns then
|
if async_dns then
|
||||||
@@ -116,9 +121,9 @@ local function connect(host, timeout)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
if interface.init then
|
if interface.init then
|
||||||
interface.init(host)
|
interface.init(htype == "hostname" and hostname or nil)
|
||||||
end
|
end
|
||||||
return fd, interface, host
|
return fd, interface, hostheader
|
||||||
end
|
end
|
||||||
|
|
||||||
local function close_interface(interface, fd)
|
local function close_interface(interface, fd)
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ function tlshelper.init_requestfunc(fd, tls_ctx)
|
|||||||
local readfunc = socket.readfunc(fd)
|
local readfunc = socket.readfunc(fd)
|
||||||
local writefunc = socket.writefunc(fd)
|
local writefunc = socket.writefunc(fd)
|
||||||
return function (hostname)
|
return function (hostname)
|
||||||
tls_ctx:set_ext_host_name(hostname)
|
if hostname then
|
||||||
|
tls_ctx:set_ext_host_name(hostname)
|
||||||
|
end
|
||||||
local ds1 = tls_ctx:handshake()
|
local ds1 = tls_ctx:handshake()
|
||||||
writefunc(ds1)
|
writefunc(ds1)
|
||||||
while not tls_ctx:finished() do
|
while not tls_ctx:finished() do
|
||||||
|
|||||||
@@ -99,6 +99,25 @@ local function test_connect_formats()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- verify that invalid input is properly rejected
|
||||||
|
local function try_error(desc, host, pattern)
|
||||||
|
local ok, err = pcall(httpc.get, host, "/", {})
|
||||||
|
total = total + 1
|
||||||
|
if ok then
|
||||||
|
print(string.format(" FAIL: %-35s expected error but succeeded", desc))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
err = tostring(err)
|
||||||
|
if err:find(pattern) then
|
||||||
|
passed = passed + 1
|
||||||
|
print(string.format(" PASS: %-35s rejected as expected", desc))
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
print(string.format(" FAIL: %-35s wrong error=%s", desc, err))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- hostname variants
|
-- hostname variants
|
||||||
try_get("bare hostname", "baidu.com")
|
try_get("bare hostname", "baidu.com")
|
||||||
try_get("hostname:port", "baidu.com:80")
|
try_get("hostname:port", "baidu.com:80")
|
||||||
@@ -127,16 +146,34 @@ local function test_connect_formats()
|
|||||||
print(" SKIP: https tests (no ltls module)")
|
print(" SKIP: https tests (no ltls module)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ipv6 variants (parsing verification, connection may fail without ipv6 network)
|
-- bare ipv6 should be rejected (RFC 3986: brackets required)
|
||||||
try_parse("bare ipv6 ::1", "::1")
|
try_error("bare ipv6 ::1 (reject)", "::1", "bare IPv6")
|
||||||
|
try_error("bare full ipv6 (reject)", "2001:db8::1", "bare IPv6")
|
||||||
|
|
||||||
|
-- bracketed ipv6 (parsing verification, connection may fail without ipv6 network)
|
||||||
|
try_parse("[ipv6] no port", "[::1]")
|
||||||
try_parse("[ipv6]:port", "[::1]:8080")
|
try_parse("[ipv6]:port", "[::1]:8080")
|
||||||
try_parse("bare full ipv6", "2001:db8::1")
|
try_parse("[full ipv6] no port", "[2001:db8::1]")
|
||||||
try_parse("[full ipv6]:port", "[2001:db8::1]:80")
|
try_parse("[full ipv6]:port", "[2001:db8::1]:80")
|
||||||
|
try_parse("http://[ipv6]", "http://[::1]")
|
||||||
try_parse("http://[ipv6]:port", "http://[::1]:80")
|
try_parse("http://[ipv6]:port", "http://[::1]:80")
|
||||||
if pcall(require, "ltls.c") then
|
if pcall(require, "ltls.c") then
|
||||||
try_parse("https://[ipv6]:port", "https://[::1]:443")
|
try_parse("https://[ipv6]:port", "https://[::1]:443")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ipv6 real connection test via AAAA DNS resolution
|
||||||
|
local ok_aaaa, ipv6 = pcall(dns.resolve, "ipv6.google.com", true)
|
||||||
|
if ok_aaaa and ipv6 then
|
||||||
|
local ipv6_host = string.format("[%s]", ipv6)
|
||||||
|
print(string.format(" resolved ipv6.google.com AAAA -> %s", ipv6))
|
||||||
|
try_parse("[ipv6] from AAAA", ipv6_host)
|
||||||
|
try_parse("[ipv6]:80 from AAAA", ipv6_host .. ":80")
|
||||||
|
try_parse("http://[ipv6] from AAAA", "http://" .. ipv6_host)
|
||||||
|
try_parse("http://[ipv6]:80 from AAAA", "http://" .. ipv6_host .. ":80")
|
||||||
|
else
|
||||||
|
print(" SKIP: ipv6 AAAA tests (dns resolve failed for ipv6.google.com)")
|
||||||
|
end
|
||||||
|
|
||||||
-- k8s internal domain variants (parsing verification, connection will fail outside k8s)
|
-- k8s internal domain variants (parsing verification, connection will fail outside k8s)
|
||||||
-- domains ending with digits must not be mistaken for ipv4
|
-- domains ending with digits must not be mistaken for ipv4
|
||||||
try_parse("k8s svc short", "my-svc:8080")
|
try_parse("k8s svc short", "my-svc:8080")
|
||||||
|
|||||||
Reference in New Issue
Block a user