Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 11 additions & 40 deletions bases/ecoindex/backend/routers/tasks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Annotated
from urllib.parse import urlparse, urlunparse

import idna
import requests
from pydantic import TypeAdapter
from pydantic.networks import AnyHttpUrl
from ecoindex.backend.dependencies.validation import validate_api_key_batch
from ecoindex.backend.models.dependencies_parameters.id import IdParameter
from ecoindex.backend.utils import check_quota
Expand Down Expand Up @@ -42,42 +42,8 @@ def convert_url_to_punycode(url: str) -> str:
"""
Convert an URL with emoji domain (or any Unicode domain) to Punycode.
This makes the URL compatible with requests library.

Args:
url: The URL string that may contain Unicode characters in the domain

Returns:
The URL with the domain converted to Punycode
"""
parsed = urlparse(url)

# Extract the hostname (netloc may contain port, so we need to handle that)
hostname = parsed.hostname
if not hostname:
return url

try:
# Convert the hostname to Punycode
hostname_punycode = idna.encode(hostname).decode("ascii")

# Reconstruct the netloc with the converted hostname
if parsed.port:
netloc = f"{hostname_punycode}:{parsed.port}"
else:
netloc = hostname_punycode

# Reconstruct the URL with the converted hostname
return urlunparse((
parsed.scheme,
netloc,
parsed.path,
parsed.params,
parsed.query,
parsed.fragment,
))
except (idna.IDNAError, UnicodeError):
# If conversion fails, return the original URL
return url
return str(TypeAdapter(AnyHttpUrl).validate_python(url))


def _enqueue_settings(*, with_retry: bool = True) -> dict[str, object]:
Expand Down Expand Up @@ -157,16 +123,21 @@ async def add_ecoindex_analysis_task(
)
r.raise_for_status()
except requests.exceptions.RequestException as e:
error_detail = (
str(e.response.status_code)
if e.response is not None
else str(e)
)
raise HTTPException(
status_code=e.response.status_code
if e.response
if e.response is not None
else status.HTTP_400_BAD_REQUEST,
detail=f"The URL {web_page.url} is unreachable. Are you really sure of this url? 🤔 ({e.response.status_code if e.response else ''})",
detail=f"The URL {web_page.url} is unreachable. Are you really sure of this url? 🤔 ({error_detail})",
)

job = ecoindex_queue.enqueue(
ecoindex_task,
url=str(web_page.url),
url=url_for_request,
width=web_page.width,
height=web_page.height,
custom_headers=headers,
Expand Down
16 changes: 16 additions & 0 deletions test/bases/ecoindex/backend/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from ecoindex.backend.routers.tasks import convert_url_to_punycode
from ecoindex.models import WebPage


def test_convert_url_to_punycode_from_idna_url() -> None:
assert convert_url_to_punycode("https://xn--3s8h30f.ws/") == "https://xn--3s8h30f.ws/"


def test_convert_url_to_punycode_from_unicode_domain() -> None:
assert convert_url_to_punycode("https://🦊💻.ws/") == "https://xn--3s8h30f.ws/"


def test_convert_url_to_punycode_from_webpage_validation() -> None:
web_page = WebPage(url="https://xn--3s8h30f.ws/")

assert convert_url_to_punycode(web_page.url) == "https://xn--3s8h30f.ws/"
Loading