Skip to content

Commit bcad146

Browse files
authored
Merge pull request #2327 from omarbounawarapy/bugfix/http-proxy-credentials-encoding
Percent-encode proxy credentials in the HTTP crawler proxy URL
2 parents 4268340 + 8b3a75d commit bcad146

3 files changed

Lines changed: 33 additions & 3 deletions

File tree

‎crawl4ai/async_configs.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import importlib
44
import os
55
import warnings
6+
from urllib.parse import unquote
67
import requests
78
from .config import (
89
DEFAULT_PROVIDER,
@@ -711,8 +712,8 @@ def from_string(proxy_str: str) -> "ProxyConfig":
711712
username, password = credentials.split(":", 1)
712713
return ProxyConfig(
713714
server=f"{protocol}://{server_part}",
714-
username=username,
715-
password=password,
715+
username=unquote(username),
716+
password=unquote(password),
716717
)
717718
# URL without credentials (keep scheme)
718719
if "://" in s and "@" not in s:

‎crawl4ai/async_crawler_strategy.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import aiohttp
2929
import chardet
3030
from aiohttp.client import ClientTimeout
31-
from urllib.parse import urlparse
31+
from urllib.parse import quote, urlparse
3232
from types import MappingProxyType
3333
import contextlib
3434
from functools import partial
@@ -2642,6 +2642,8 @@ def _format_proxy_url(self, proxy_config) -> str:
26422642

26432643
if username and password:
26442644
# Insert credentials into URL: http://user:pass@host:port
2645+
username = quote(username, safe='')
2646+
password = quote(password, safe='')
26452647
if '://' in server:
26462648
protocol, rest = server.split('://', 1)
26472649
return f"{protocol}://{username}:{password}@{rest}"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pytest
2+
from yarl import URL
3+
4+
from crawl4ai.async_configs import ProxyConfig
5+
from crawl4ai.async_crawler_strategy import AsyncHTTPCrawlerStrategy
6+
7+
8+
@pytest.mark.parametrize(
9+
"password", ["plainpass", "p@ss", "p#ss", "pa/ss", "p?ss", "p%41ss"]
10+
)
11+
def test_proxy_url_roundtrips_credentials(password):
12+
strategy = AsyncHTTPCrawlerStrategy()
13+
config = ProxyConfig(server="http://host:8080", username="us:er", password=password)
14+
url = URL(strategy._format_proxy_url(config))
15+
assert url.host == "host"
16+
assert url.port == 8080
17+
assert url.user == "us:er"
18+
assert url.password == password
19+
20+
21+
def test_encoded_proxy_string_is_sent_once_encoded():
22+
strategy = AsyncHTTPCrawlerStrategy()
23+
config = ProxyConfig.from_string("http://user:p%40ss@host:8080")
24+
assert config.password == "p@ss"
25+
url = URL(strategy._format_proxy_url(config))
26+
assert url.user == "user"
27+
assert url.password == "p@ss"

0 commit comments

Comments
 (0)