|
24 | 24 | import json
|
25 | 25 | import sys
|
26 | 26 | import typing
|
| 27 | +import urllib |
27 | 28 |
|
28 | 29 | from .opener import Opener, FileOpener
|
29 | 30 |
|
@@ -614,6 +615,20 @@ def subname(self) -> str:
|
614 | 615 | def hostnames(self) -> list[str]:
|
615 | 616 | return list(self._cfg.get("hostnames", []))
|
616 | 617 |
|
| 618 | + @property |
| 619 | + def host_ports(self) -> list[tuple[str, int]]: |
| 620 | + default_port = self.port |
| 621 | + out: list[tuple[str, int]] = [] |
| 622 | + for host in self.hostnames: |
| 623 | + host, port = _safe_split_host_port(host) |
| 624 | + if port <= 0 and default_port <= 0: |
| 625 | + raise ValueError("no host port and no default port") |
| 626 | + elif port <= 0: |
| 627 | + out.append((host, default_port)) |
| 628 | + else: |
| 629 | + out.append((host, int(port))) |
| 630 | + return out |
| 631 | + |
617 | 632 | @property
|
618 | 633 | def port(self) -> int:
|
619 | 634 | return int(self._cfg.get("port", -1))
|
@@ -772,3 +787,13 @@ def _globals_data(gconfig: GlobalConfig, iconfig: dict) -> list:
|
772 | 787 | except KeyError:
|
773 | 788 | return []
|
774 | 789 | return [gconfig.data["globals"][n] for n in gnames]
|
| 790 | + |
| 791 | + |
| 792 | +def _safe_split_host_port(host: str, scheme: str = "https") -> tuple[str, int]: |
| 793 | + if host.count(":") > 1 and "[" not in host: |
| 794 | + # assume this is an undecorated ipv6 and pass it thru w/o port |
| 795 | + return (host, -1) |
| 796 | + # otherwise let urllib do the work for us |
| 797 | + parsed = urllib.parse.urlparse(f"{scheme}://{host}") |
| 798 | + assert parsed.hostname, "invalid hostname" # should be impossible |
| 799 | + return parsed.hostname, int(parsed.port or -1) |
0 commit comments