diff --git a/links.py b/links.py index 6508431..e506bbd 100644 --- a/links.py +++ b/links.py @@ -8,7 +8,7 @@ troque este arquivo por consultas a um Postgres. import re import secrets from datetime import datetime, timezone -from urllib.parse import urlsplit +from urllib.parse import urlsplit, urlunsplit ALFABETO = "abcdefghijklmnopqrstuvwxyz0123456789" TAMANHO_CODIGO = 6 @@ -44,11 +44,27 @@ def _validar_url(valor) -> str: raise ErroDeValidacao(f"Esse link é comprido demais (máximo de {TAMANHO_MAXIMO_URL} caracteres).") try: partes = urlsplit(texto) + porta = partes.port # porta inválida (ex.: ":abc") levanta ValueError except ValueError: partes = None if not partes or partes.scheme not in ("http", "https") or not partes.hostname or any(c.isspace() for c in texto): raise ErroDeValidacao("Esse não parece um link válido. Use um endereço que comece com http:// ou https://.") - return texto + return _normalizar(partes, porta) + + +def _normalizar(partes, porta) -> str: + """Mesma forma que o `new URL(...).href` dos modelos Node: esquema e host em + minúsculas, sem a porta padrão e com "/" quando o caminho vem vazio.""" + esquema = partes.scheme.lower() + host = partes.hostname.lower() + if ":" in host: # IPv6 volta entre colchetes + host = f"[{host}]" + if porta is not None and porta != {"http": 80, "https": 443}[esquema]: + host = f"{host}:{porta}" + if partes.username is not None: + credenciais = partes.username + (f":{partes.password}" if partes.password is not None else "") + host = f"{credenciais}@{host}" + return urlunsplit((esquema, host, partes.path or "/", partes.query, partes.fragment)) def criar_link(valor) -> dict: