Files
MoviePilot/app/application/network.py
T

89 lines
3.8 KiB
Python

"""受控外部网络探测应用服务。"""
from datetime import datetime
from typing import Any, Callable, Optional
from urllib.parse import urljoin, urlparse
class NetworkTestService:
"""执行服务端预定义目标的 HTTPS 连通性测试。"""
def __init__(
self,
request_utils_cls: Callable[..., Any],
settings_getter: Callable[[], Any],
logger: Any,
redirect_checker: Callable[[str, dict[str, Any]], bool],
close_response: Callable[[Any], Any],
):
"""注入网络客户端、配置和安全边界,方便隔离测试。"""
self.request_utils_cls = request_utils_cls
self.settings_getter = settings_getter
self.logger = logger
self.redirect_checker = redirect_checker
self.close_response = close_response
async def execute(
self,
target: dict[str, Any],
include: Optional[str] = None,
) -> tuple[bool, Optional[str], Optional[dict[str, int]]]:
"""请求目标并处理受控重定向,返回旧端点使用的结果三元组。"""
start_time = datetime.now()
url = target["url"]
parsed = urlparse(url)
if parsed.scheme.lower() != "https" or not parsed.netloc or parsed.username or parsed.password:
return False, "测试地址无效", {"time": 0}
if include:
self.logger.debug("nettest include 参数已忽略,改为服务端固定校验")
settings = self.settings_getter()
request_utils = self.request_utils_cls(
proxies=settings.get("PROXY") if target.get("proxy") else None,
headers=target.get("headers"),
timeout=10,
ua=settings.get("NORMAL_USER_AGENT"),
verify=True,
follow_redirects=False,
)
result = None
current_url = url
redirect_count = 0
while redirect_count <= 3:
result = await request_utils.get_res(current_url, allow_redirects=False)
if result is None or result.status_code not in {301, 302, 303, 307, 308}:
break
location = result.headers.get("location")
if not location:
break
next_url = urljoin(current_url, location)
if not self.redirect_checker(next_url, target):
await self.close_response(result)
self.logger.warning(f"拦截网络测试重定向: {current_url} -> {next_url}")
return False, "测试目标发生了未授权跳转", None
await self.close_response(result)
current_url = next_url
redirect_count += 1
elapsed = round((datetime.now() - start_time).total_seconds() * 1000)
timing = {"time": elapsed}
if redirect_count > 3:
return False, "测试目标重定向次数过多", None
if result is None:
return False, f"{target.get('proxy_name') or target.get('name')}无法连接", timing
if result.status_code == 200:
expected_text = target.get("expected_text")
if expected_text and expected_text.lower() not in (result.text or "").lower():
return False, target.get("invalid_message") or "无效响应", timing
return True, None, timing
if target.get("proxy_name"):
message = f"{target['proxy_name']}已失效,错误码:{result.status_code}"
else:
message = f"错误码:{result.status_code}"
if "github" in url:
if result.status_code == 401:
message = "Github Token已失效,请检查配置"
elif result.status_code in {403, 429}:
message = "触发限流,请配置Github Token"
return False, message, timing