Skip to content

Commit f262bdd

Browse files
committed
Tidy up IPv6 handling.
1 parent 332d1ac commit f262bdd

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

lib/async/redis/endpoint.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ def self.local_endpoint(**options)
1919

2020
# Represents a way to connect to a remote Redis server.
2121
class Endpoint < ::IO::Endpoint::Generic
22-
LOCALHOST = URI.parse("redis://localhost").freeze
22+
LOCALHOST = URI::Generic.build(scheme: "redis", host: "localhost").freeze
2323

2424
def self.local(**options)
2525
self.new(LOCALHOST, **options)
2626
end
2727

2828
def self.remote(host, port = 6379, **options)
29-
self.new(URI.parse("redis://#{host}:#{port}"), **options)
29+
# URI::Generic.build automatically handles IPv6 addresses correctly:
30+
self.new(URI::Generic.build(scheme: "redis", host: host, port: port), **options)
3031
end
3132

3233
SCHEMES = {
@@ -45,7 +46,7 @@ def self.parse(string, endpoint = nil, **options)
4546
# @parameter scheme [String] The scheme to use, e.g. "redis" or "rediss".
4647
# @parameter hostname [String] The hostname to connect to (or bind to).
4748
# @parameter options [Hash] Additional options, passed to {#initialize}.
48-
def self.for(scheme, hostname, credentials: nil, port: nil, database: nil, **options)
49+
def self.for(scheme, host, credentials: nil, port: nil, database: nil, **options)
4950
uri_klass = SCHEMES.fetch(scheme.downcase) do
5051
raise ArgumentError, "Unsupported scheme: #{scheme.inspect}"
5152
end
@@ -54,8 +55,10 @@ def self.for(scheme, hostname, credentials: nil, port: nil, database: nil, **opt
5455
path = "/#{database}"
5556
end
5657

58+
host = host.include?(":") ? "[#{host}]" : host
59+
5760
self.new(
58-
uri_klass.new(scheme, credentials&.join(":"), hostname, port, nil, path, nil, nil, nil).normalize,
61+
uri_klass.new(scheme, credentials&.join(":"), host, port, nil, path, nil, nil, nil).normalize,
5962
**options
6063
)
6164
end

test/async/redis/endpoint.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,94 @@
5353
end
5454
end
5555
end
56+
57+
with ".remote" do
58+
it "handles IPv4 addresses correctly" do
59+
endpoint = Async::Redis::Endpoint.remote("127.0.0.1", 6380)
60+
expect(endpoint.url.to_s).to be == "redis://127.0.0.1:6380"
61+
expect(endpoint.url.host).to be == "127.0.0.1"
62+
expect(endpoint.url.hostname).to be == "127.0.0.1"
63+
end
64+
65+
it "handles IPv6 addresses correctly" do
66+
endpoint = Async::Redis::Endpoint.remote("::1", 6380)
67+
expect(endpoint.url.to_s).to be == "redis://[::1]:6380"
68+
expect(endpoint.url.host).to be == "[::1]"
69+
expect(endpoint.url.hostname).to be == "::1"
70+
end
71+
72+
it "handles expanded IPv6 addresses correctly" do
73+
ipv6 = "2600:1f28:372:c404:5c2d:ce68:3620:cc4b"
74+
endpoint = Async::Redis::Endpoint.remote(ipv6, 6380)
75+
expect(endpoint.url.to_s).to be == "redis://[#{ipv6}]:6380"
76+
expect(endpoint.url.host).to be == "[#{ipv6}]"
77+
expect(endpoint.url.hostname).to be == ipv6
78+
end
79+
end
80+
81+
with ".for" do
82+
it "handles IPv4 addresses correctly" do
83+
endpoint = Async::Redis::Endpoint.for("redis", "127.0.0.1", port: 6380)
84+
expect(endpoint.url.to_s).to be == "redis://127.0.0.1:6380/"
85+
expect(endpoint.url.host).to be == "127.0.0.1"
86+
expect(endpoint.url.hostname).to be == "127.0.0.1"
87+
expect(endpoint.port).to be == 6380
88+
end
89+
90+
it "handles IPv6 addresses correctly" do
91+
endpoint = Async::Redis::Endpoint.for("redis", "::1", port: 6380)
92+
expect(endpoint.url.to_s).to be == "redis://[::1]:6380/"
93+
expect(endpoint.url.host).to be == "[::1]"
94+
expect(endpoint.url.hostname).to be == "::1"
95+
expect(endpoint.port).to be == 6380
96+
end
97+
98+
it "handles expanded IPv6 addresses correctly" do
99+
ipv6 = "2600:1f28:372:c404:5c2d:ce68:3620:cc4b"
100+
endpoint = Async::Redis::Endpoint.for("redis", ipv6, port: 6380)
101+
expect(endpoint.url.to_s).to be == "redis://[#{ipv6}]:6380/"
102+
expect(endpoint.url.host).to be == "[#{ipv6}]"
103+
expect(endpoint.url.hostname).to be == ipv6
104+
expect(endpoint.port).to be == 6380
105+
end
106+
107+
it "handles credentials correctly" do
108+
endpoint = Async::Redis::Endpoint.for("redis", "localhost", credentials: ["user", "pass"], port: 6380)
109+
expect(endpoint.url.to_s).to be == "redis://user:pass@localhost:6380/"
110+
expect(endpoint.url.userinfo).to be == "user:pass"
111+
expect(endpoint.credentials).to be == ["user", "pass"]
112+
end
113+
114+
it "handles database selection correctly" do
115+
endpoint = Async::Redis::Endpoint.for("redis", "localhost", database: 2)
116+
expect(endpoint.url.to_s).to be == "redis://localhost/2"
117+
expect(endpoint.url.path).to be == "/2"
118+
expect(endpoint.database).to be == 2
119+
end
120+
121+
it "handles secure connections correctly" do
122+
endpoint = Async::Redis::Endpoint.for("rediss", "localhost")
123+
expect(endpoint.url.to_s).to be == "rediss://localhost/"
124+
expect(endpoint).to be(:secure?)
125+
end
126+
127+
it "handles all parameters together correctly" do
128+
ipv6 = "2600:1f28:372:c404:5c2d:ce68:3620:cc4b"
129+
endpoint = Async::Redis::Endpoint.for("rediss", ipv6,
130+
credentials: ["user", "pass"],
131+
port: 6380,
132+
database: 3
133+
)
134+
expect(endpoint.url.to_s).to be == "rediss://user:pass@[#{ipv6}]:6380/3"
135+
expect(endpoint.url.scheme).to be == "rediss"
136+
expect(endpoint.url.host).to be == "[#{ipv6}]"
137+
expect(endpoint.url.hostname).to be == ipv6
138+
expect(endpoint.url.userinfo).to be == "user:pass"
139+
expect(endpoint.url.port).to be == 6380
140+
expect(endpoint.url.path).to be == "/3"
141+
expect(endpoint).to be(:secure?)
142+
expect(endpoint.credentials).to be == ["user", "pass"]
143+
expect(endpoint.database).to be == 3
144+
end
145+
end
56146
end

0 commit comments

Comments
 (0)