added conopy script proxy
This commit is contained in:
@@ -96,9 +96,12 @@ If direct download is blocked, use a SOCKS5 proxy (`pip install PySocks`):
|
|||||||
python scripts/bootstrap_canopy.py \
|
python scripts/bootstrap_canopy.py \
|
||||||
--bbox 27.3,58.4,35.8,61.4 \
|
--bbox 27.3,58.4,35.8,61.4 \
|
||||||
--output-dir data/canopy \
|
--output-dir data/canopy \
|
||||||
--proxy socks5://user:pass@host:port
|
--proxy socks5h://user:pass@host:port
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Use `socks5h://` (DNS via proxy), same as `curl --proxy socks5h://`. Plain `socks5://`
|
||||||
|
resolves hostnames locally and may hang if local DNS is blocked.
|
||||||
|
|
||||||
The same URL can be passed via `CANOPY_PROXY` env var instead of `--proxy`.
|
The same URL can be passed via `CANOPY_PROXY` env var instead of `--proxy`.
|
||||||
|
|
||||||
The `/api/v1/landcover/path` endpoint samples all `.tif`/`.tiff` files under
|
The `/api/v1/landcover/path` endpoint samples all `.tif`/`.tiff` files under
|
||||||
|
|||||||
@@ -67,13 +67,14 @@ def test_canopy_selects_intersecting_tiles_from_geojson_index() -> None:
|
|||||||
|
|
||||||
def test_canopy_parse_socks5_proxy_url() -> None:
|
def test_canopy_parse_socks5_proxy_url() -> None:
|
||||||
proxy = bootstrap_canopy.parse_socks5_proxy(
|
proxy = bootstrap_canopy.parse_socks5_proxy(
|
||||||
"socks5://proxy_user:secret@194.33.35.46:38599"
|
"socks5h://proxy_user:secret@194.33.35.46:38599"
|
||||||
)
|
)
|
||||||
|
assert proxy.scheme == "socks5h"
|
||||||
assert proxy.host == "194.33.35.46"
|
assert proxy.host == "194.33.35.46"
|
||||||
assert proxy.port == 38599
|
assert proxy.port == 38599
|
||||||
assert proxy.username == "proxy_user"
|
assert proxy.username == "proxy_user"
|
||||||
assert proxy.password == "secret"
|
assert proxy.password == "secret"
|
||||||
assert proxy.endpoint == "socks5://194.33.35.46:38599"
|
assert proxy.endpoint == "socks5h://194.33.35.46:38599"
|
||||||
|
|
||||||
|
|
||||||
def test_canopy_urls_file_ignores_comments(tmp_path: Path) -> None:
|
def test_canopy_urls_file_ignores_comments(tmp_path: Path) -> None:
|
||||||
|
|||||||
@@ -63,10 +63,13 @@ def configure_socks5_proxy(proxy_url: str) -> Socks5Proxy:
|
|||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
proxy = parse_socks5_proxy(proxy_url)
|
proxy = parse_socks5_proxy(proxy_url)
|
||||||
|
# socks5h resolves hostnames on the proxy (like curl --proxy socks5h://).
|
||||||
|
rdns = proxy.scheme == "socks5h"
|
||||||
socks.set_default_proxy(
|
socks.set_default_proxy(
|
||||||
socks.SOCKS5,
|
socks.SOCKS5,
|
||||||
proxy.host,
|
proxy.host,
|
||||||
proxy.port,
|
proxy.port,
|
||||||
|
rdns=rdns,
|
||||||
username=proxy.username,
|
username=proxy.username,
|
||||||
password=proxy.password,
|
password=proxy.password,
|
||||||
)
|
)
|
||||||
@@ -140,11 +143,12 @@ def _asset_url(feature: dict[str, Any], base_url: str = META_CHM_V2_BASE_URL) ->
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def validate_urls(urls: list[str], sample_size: int = 2) -> None:
|
def validate_urls(urls: list[str], sample_size: int = 2, *, timeout_s: int = 60) -> None:
|
||||||
|
print(f"validating {min(sample_size, len(urls))} sample URL(s)")
|
||||||
for url in urls[:sample_size]:
|
for url in urls[:sample_size]:
|
||||||
request = Request(url, headers={"Range": "bytes=0-0", "User-Agent": USER_AGENT})
|
request = Request(url, headers={"Range": "bytes=0-0", "User-Agent": USER_AGENT})
|
||||||
try:
|
try:
|
||||||
with urlopen(request) as response:
|
with urlopen(request, timeout=timeout_s) as response:
|
||||||
if response.status >= 400:
|
if response.status >= 400:
|
||||||
raise SystemExit(f"Canopy URL is not reachable: {url} ({response.status})")
|
raise SystemExit(f"Canopy URL is not reachable: {url} ({response.status})")
|
||||||
except HTTPError as exc:
|
except HTTPError as exc:
|
||||||
@@ -156,13 +160,17 @@ def validate_urls(urls: list[str], sample_size: int = 2) -> None:
|
|||||||
raise SystemExit(f"Canopy URL validation failed: {url}: {exc}") from exc
|
raise SystemExit(f"Canopy URL validation failed: {url}: {exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
def load_index(index_url: str) -> dict[str, Any]:
|
def load_index(index_url: str, *, timeout_s: int = 120) -> dict[str, Any]:
|
||||||
path = Path(index_url)
|
path = Path(index_url)
|
||||||
if path.exists():
|
if path.exists():
|
||||||
|
print(f"loading index from {path}")
|
||||||
return json.loads(path.read_text(encoding="utf-8"))
|
return json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
print(f"loading index from {index_url}")
|
||||||
request = Request(index_url, headers={"User-Agent": USER_AGENT})
|
request = Request(index_url, headers={"User-Agent": USER_AGENT})
|
||||||
with urlopen(request) as response:
|
with urlopen(request, timeout=timeout_s) as response:
|
||||||
return json.loads(response.read().decode("utf-8"))
|
payload = response.read()
|
||||||
|
print(f"index loaded ({_format_mb(len(payload))})")
|
||||||
|
return json.loads(payload.decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
def select_tile_urls(
|
def select_tile_urls(
|
||||||
@@ -294,14 +302,14 @@ def main() -> None:
|
|||||||
if args.urls_file is not None:
|
if args.urls_file is not None:
|
||||||
urls = urls_from_file(args.urls_file)
|
urls = urls_from_file(args.urls_file)
|
||||||
else:
|
else:
|
||||||
index = load_index(args.index_url)
|
index = load_index(args.index_url, timeout_s=max(args.timeout_s, 120))
|
||||||
urls = select_tile_urls(index, args.bbox)
|
urls = select_tile_urls(index, args.bbox)
|
||||||
|
|
||||||
if not urls:
|
if not urls:
|
||||||
raise SystemExit("No canopy tiles intersect requested AOI")
|
raise SystemExit("No canopy tiles intersect requested AOI")
|
||||||
|
|
||||||
if not args.skip_url_check:
|
if not args.skip_url_check:
|
||||||
validate_urls(urls)
|
validate_urls(urls, timeout_s=args.timeout_s)
|
||||||
|
|
||||||
print(f"selected_tiles={len(urls)}")
|
print(f"selected_tiles={len(urls)}")
|
||||||
downloaded, skipped, failed = download_urls(
|
downloaded, skipped, failed = download_urls(
|
||||||
|
|||||||
Reference in New Issue
Block a user