Files
zsglpt/services/proxy.py

82 lines
3.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
import re
import time
from typing import Optional
import requests
from app_logger import get_logger
from app_security import is_safe_outbound_url
logger = get_logger("proxy")
def validate_ip_port(ip_port_str: str) -> bool:
"""验证IP:PORT格式是否有效含范围校验"""
pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):(\d{1,5})$")
match = pattern.match(ip_port_str or "")
if not match:
return False
for i in range(1, 5):
octet = int(match.group(i))
if octet < 0 or octet > 255:
return False
port = int(match.group(5))
return 1 <= port <= 65535
def get_proxy_from_api(api_url: str, max_retries: int = 3) -> Optional[str]:
"""从API获取代理IP支持重试"""
ip_port_pattern = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$")
max_retries = max(1, int(max_retries or 1))
if not is_safe_outbound_url(api_url):
logger.warning("代理API地址不可用或不安全已拒绝请求")
return None
for attempt in range(max_retries):
try:
response = requests.get(api_url, timeout=10)
if response.status_code == 200:
text = response.text.strip()
# 尝试解析JSON响应
try:
import json
data = json.loads(text)
if isinstance(data, dict):
if data.get("status") not in (200, 0, None):
error_msg = data.get("msg", data.get("message", "未知错误"))
logger.warning(f"代理API返回错误: {error_msg} (尝试 {attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
time.sleep(1)
continue
ip_port = data.get("ip") or data.get("proxy") or data.get("data")
if ip_port:
text = str(ip_port).strip()
except Exception:
pass
if ip_port_pattern.match(text) and validate_ip_port(text):
proxy_server = f"http://{text}"
logger.info(f"获取代理成功: {proxy_server} (尝试 {attempt + 1}/{max_retries})")
return proxy_server
logger.warning(f"代理格式或范围无效: {text[:50]} (尝试 {attempt + 1}/{max_retries})")
else:
logger.warning(f"获取代理失败: HTTP {response.status_code} (尝试 {attempt + 1}/{max_retries})")
except Exception as e:
logger.warning(f"获取代理异常: {str(e)} (尝试 {attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
time.sleep(1)
logger.warning(f"获取代理失败,已重试 {max_retries} 次,将不使用代理继续")
return None