Skip to content

Commit dfc2b6e

Browse files
authored
Merge pull request #155 from RaphaelFakhri/fix/ipv6-node-url
fix(config): enclose IPv6 node hosts in brackets when building the URL
2 parents 8727d2b + c86e612 commit dfc2b6e

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

‎src/typesense/configuration.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,11 @@ def url(self) -> str:
203203
Returns:
204204
str: The URL of the node
205205
"""
206-
return f"{self.protocol}://{self.host}:{self.port}{self.path}"
206+
host = str(self.host)
207+
if ":" in host and not host.startswith("["):
208+
# IPv6 literals must be enclosed in brackets in a URL.
209+
host = f"[{host}]"
210+
return f"{self.protocol}://{host}:{self.port}{self.path}"
207211

208212

209213
class Configuration:

‎tests/api_call_test.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from typesense import exceptions
2424
from typesense.sync.api_call import ApiCall, RequestHandler
2525
from typesense.async_.api_call import AsyncApiCall
26-
from typesense.configuration import Configuration, Node
26+
from typesense.configuration import Configuration, Node, NodeConfigDict
2727
from typesense.logger import logger
2828

2929

@@ -390,6 +390,23 @@ def test_delete_as_json(
390390
assert response == {"key": "value"}
391391

392392

393+
@pytest.mark.parametrize(
394+
"node",
395+
["http://[::1]:8108", {"host": "::1", "port": 8108, "protocol": "http"}],
396+
)
397+
def test_request_to_ipv6_node(node: typing.Union[str, NodeConfigDict]) -> None:
398+
"""Test that a request to an IPv6 node is sent to the bracketed host."""
399+
api_call = ApiCall(Configuration({"api_key": "xyz", "nodes": [node]}))
400+
with respx.mock:
401+
route = respx.get("http://[::1]:8108/test").mock(
402+
return_value=httpx.Response(200, json={"key": "value"}),
403+
)
404+
405+
response = api_call.get("/test", entity_type=typing.Dict[str, str])
406+
assert response == {"key": "value"}
407+
assert route.called
408+
409+
393410
def test_raise_custom_exception_with_header(
394411
fake_api_call: ApiCall,
395412
) -> None:

‎tests/node_test.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,21 @@ def test_node_url() -> None:
6363
"""Test the URL method of the Node class."""
6464
node = Node(host="localhost", port=8108, path="/path", protocol="http")
6565
assert node.url() == "http://localhost:8108/path"
66+
67+
68+
def test_node_url_ipv6_host() -> None:
69+
"""Test that the URL method encloses an IPv6 host in brackets."""
70+
node = Node(host="::1", port=8108, path="/path", protocol="http")
71+
assert node.url() == "http://[::1]:8108/path"
72+
73+
74+
def test_node_url_bracketed_ipv6_host() -> None:
75+
"""Test that the URL method keeps an already bracketed IPv6 host as is."""
76+
node = Node(host="[::1]", port=8108, path="/path", protocol="http")
77+
assert node.url() == "http://[::1]:8108/path"
78+
79+
80+
def test_node_from_url_ipv6() -> None:
81+
"""Test that a node built from an IPv6 URL generates the same URL."""
82+
node = Node.from_url("http://[::1]:8108/path")
83+
assert node.url() == "http://[::1]:8108/path"

0 commit comments

Comments
 (0)