network_utils.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import socket
  2. from contextlib import closing
  3. from functools import lru_cache
  4. from typing import Optional, Tuple, Union
  5. from ray._raylet import (
  6. build_address as _build_address,
  7. is_ipv6 as _is_ipv6,
  8. node_ip_address_from_perspective as _node_ip_address_from_perspective,
  9. parse_address as _parse_address,
  10. )
  11. def parse_address(address: str) -> Optional[Tuple[str, str]]:
  12. """Parse a network address string into host and port.
  13. Args:
  14. address: The address string to parse (e.g., "localhost:8000", "[::1]:8000").
  15. Returns:
  16. Tuple with (host, port) if port found, None if no colon separator.
  17. """
  18. return _parse_address(address)
  19. def build_address(host: str, port: Union[int, str]) -> str:
  20. """Build a network address string from host and port.
  21. Args:
  22. host: The hostname or IP address.
  23. port: The port number (int or string).
  24. Returns:
  25. Formatted address string (e.g., "localhost:8000" or "[::1]:8000").
  26. """
  27. return _build_address(host, port)
  28. def node_ip_address_from_perspective(address: Optional[str] = None) -> str:
  29. """IP address by which the local node can be reached *from* the `address`.
  30. If no address is given, defaults to public DNS servers for detection.
  31. Args:
  32. address: The IP address and port of any known live service on the
  33. network you care about.
  34. Returns:
  35. The IP address by which the local node can be reached from the address.
  36. """
  37. return _node_ip_address_from_perspective(address)
  38. def is_ipv6(host: str) -> bool:
  39. """Check if a host is resolved to IPv6.
  40. Args:
  41. host: The IP or domain name to check (must be without port).
  42. Returns:
  43. True if the host is resolved to IPv6, False if IPv4.
  44. """
  45. return _is_ipv6(host)
  46. @lru_cache(maxsize=1)
  47. def get_localhost_ip() -> str:
  48. """Get localhost loopback ip with IPv4/IPv6 support.
  49. Returns:
  50. The localhost loopback IP.
  51. """
  52. # Try IPv4 first, then IPv6 localhost resolution
  53. for family in [socket.AF_INET, socket.AF_INET6]:
  54. try:
  55. dns_result = socket.getaddrinfo(
  56. "localhost", None, family, socket.SOCK_STREAM
  57. )
  58. return dns_result[0][4][0]
  59. except Exception:
  60. continue
  61. # Final fallback to IPv4 loopback
  62. return "127.0.0.1"
  63. def is_localhost(host: str) -> bool:
  64. """Check if the given host string represents a localhost address.
  65. Args:
  66. host: The hostname or IP address to check.
  67. Returns:
  68. True if the host is a localhost address, False otherwise.
  69. """
  70. return host in ("localhost", "127.0.0.1", "::1")
  71. def find_free_port(family: socket.AddressFamily = socket.AF_INET) -> int:
  72. """Find a free port on the local machine.
  73. Args:
  74. family: The socket address family (AF_INET for IPv4, AF_INET6 for IPv6).
  75. Defaults to AF_INET.
  76. Returns:
  77. An available port number.
  78. """
  79. with closing(socket.socket(family, socket.SOCK_STREAM)) as s:
  80. s.bind(("", 0))
  81. return s.getsockname()[1]