diff options
Diffstat (limited to 'app/backends/common.py')
| -rw-r--r-- | app/backends/common.py | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/app/backends/common.py b/app/backends/common.py index 42faa7e..d5e1b6b 100644 --- a/app/backends/common.py +++ b/app/backends/common.py @@ -150,6 +150,38 @@ def url_with_port(url: str, port: int) -> str: (parts.scheme or "http", f"{host}:{port}", parts.path, "", "")) +def normalize_remote_url(value: str) -> str: + """Normalize a user-supplied remote server URL, or '' for "disabled". + + Accepts a bare ``host[:port]`` (a scheme of ``http`` is assumed), a full + ``http(s)://host[:port][/path]`` URL, or the empty string (no remote + server configured). Returns the normalized URL (bare host:port becomes + ``http://host:port``). Raises ValueError for anything else — a missing + host, a host containing whitespace, or a non-numeric port. + """ + cleaned = value.strip() + if not cleaned: + return "" + parts = urllib.parse.urlsplit(cleaned) + if not parts.scheme: + # Bare host[:port] — add the default scheme so netloc/host/port + # parse cleanly. An explicit scheme is kept as-is (so "http://" + # with no host fails the host check below). + parts = urllib.parse.urlsplit(f"http://{cleaned}") + host = parts.hostname + if not host or any(ch.isspace() for ch in host): + raise ValueError( + "Enter a host:port (e.g. 10.20.30.40:8000) or a full URL " + f"(e.g. http://10.20.30.40:8000); got {value!r}") + try: + parts.port # raises ValueError for a non-numeric port + except ValueError as exc: + raise ValueError( + f"Invalid port in remote URL {value!r}: {exc}") from exc + return urllib.parse.urlunsplit( + (parts.scheme or "http", parts.netloc, parts.path, "", "")) + + def server_running(url: str, timeout: float = 0.3) -> bool: """True when something accepts TCP connections at URL's host:port. |
