diff --git a/README.md b/README.md index a65b542..0936ee9 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ pop_addr_infos_interleave(addr_info, 1) # Remove all matching address from addr_info remove_addr_infos(addr_info, "dead::beef::") +# Convert a local_addr to local_addr_infos +local_addr_infos = addr_to_addr_infos(("127.0.0.1",0)) ``` ## Credits diff --git a/docs/usage.md b/docs/usage.md index ec2e596..b79e1ec 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -22,4 +22,7 @@ aiohappyeyeballs.pop_addr_infos_interleave(addr_info, 1) # Remove all matching address from addr_info aiohappyeyeballs.remove_addr_infos(addr_info, "dead::beef::") + +# Convert a local_addr to local_addr_infos +local_addr_infos = addr_to_addr_infos(("127.0.0.1",0)) ``` diff --git a/src/aiohappyeyeballs/__init__.py b/src/aiohappyeyeballs/__init__.py index c5abf6a..9dec521 100644 --- a/src/aiohappyeyeballs/__init__.py +++ b/src/aiohappyeyeballs/__init__.py @@ -2,11 +2,12 @@ from .impl import start_connection from .types import AddrInfoType -from .utils import pop_addr_infos_interleave, remove_addr_infos +from .utils import addr_to_addr_infos, pop_addr_infos_interleave, remove_addr_infos __all__ = ( "start_connection", "AddrInfoType", "remove_addr_infos", "pop_addr_infos_interleave", + "addr_to_addr_infos", ) diff --git a/src/aiohappyeyeballs/utils.py b/src/aiohappyeyeballs/utils.py index 9c368c1..f99a3cf 100644 --- a/src/aiohappyeyeballs/utils.py +++ b/src/aiohappyeyeballs/utils.py @@ -1,11 +1,39 @@ """Utility functions for aiohappyeyeballs.""" import ipaddress -from typing import Dict, List, Tuple, Union +import socket +from typing import Dict, List, Optional, Tuple, Union from .types import AddrInfoType +def addr_to_addr_infos( + addr: Optional[ + Union[Tuple[str, int, int, int], Tuple[str, int, int], Tuple[str, int]] + ] +) -> Optional[List[AddrInfoType]]: + """Convert an address tuple to a list of addr_info tuples.""" + if addr is None: + return None + host = addr[0] + port = addr[1] + is_ipv6 = ":" in host + if is_ipv6: + flowinfo = 0 + scopeid = 0 + addr_len = len(addr) + if addr_len >= 4: + scopeid = addr[3] # type: ignore[misc] + if addr_len >= 3: + flowinfo = addr[2] # type: ignore[misc] + addr = (host, port, flowinfo, scopeid) + family = socket.AF_INET6 + else: + addr = (host, port) + family = socket.AF_INET + return [(family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr)] + + def pop_addr_infos_interleave(addr_infos: List[AddrInfoType], interleave: int) -> None: """ Pop addr_info from the list of addr_infos by family up to interleave times. diff --git a/tests/test_utils.py b/tests/test_utils.py index 3e5dcda..6c74992 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,7 +3,12 @@ import pytest -from aiohappyeyeballs import AddrInfoType, pop_addr_infos_interleave, remove_addr_infos +from aiohappyeyeballs import ( + AddrInfoType, + addr_to_addr_infos, + pop_addr_infos_interleave, + remove_addr_infos, +) def test_pop_addr_infos_interleave(): @@ -114,3 +119,64 @@ def test_remove_addr_infos_slow_path(): ): remove_addr_infos(addr_info_copy, ("107.6.106.2", 80)) assert addr_info_copy == [ipv4_addr_info] + + +def test_addr_to_addr_infos(): + """Test addr_to_addr_infos.""" + assert addr_to_addr_infos(("1.2.3.4", 43)) == [ + ( + socket.AF_INET, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("1.2.3.4", 43), + ) + ] + assert addr_to_addr_infos( + ("dead:aaaa:0000:0000:0000:0000:0000:0000", 80, 0, 0) + ) == [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("dead:aaaa:0000:0000:0000:0000:0000:0000", 80, 0, 0), + ) + ] + assert addr_to_addr_infos(("dead:aaaa::", 80, 0, 0)) == [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("dead:aaaa::", 80, 0, 0), + ) + ] + assert addr_to_addr_infos(("dead:aaaa::", 80)) == [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("dead:aaaa::", 80, 0, 0), + ) + ] + assert addr_to_addr_infos(("dead:aaaa::", 80, 1)) == [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("dead:aaaa::", 80, 1, 0), + ) + ] + assert addr_to_addr_infos(("dead:aaaa::", 80, 1, 1)) == [ + ( + socket.AF_INET6, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + "", + ("dead:aaaa::", 80, 1, 1), + ) + ] + assert addr_to_addr_infos(None) is None