重构:模块化 RRPP Lite 并更新入口
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
"""节点订阅的认证请求协议。"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
|
||||
class NodeAuthError(Exception):
|
||||
"""订阅服务拒绝请求时携带的 HTTP 状态和用户可读原因。"""
|
||||
|
||||
def __init__(self, message, code=0):
|
||||
super().__init__(message)
|
||||
self.code = code
|
||||
|
||||
|
||||
def b64url(value: str) -> str:
|
||||
"""SSO 查询参数使用的无 padding URL-safe Base64。"""
|
||||
return base64.urlsafe_b64encode(str(value).encode("utf-8")).decode("ascii").rstrip("=")
|
||||
|
||||
|
||||
def fetch_nodes_auth(url, user, password, timeout=10):
|
||||
"""直连拉取需 SSO 查询参数认证的 nodes.json。"""
|
||||
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||
separator = "&" if "?" in url else "?"
|
||||
request_url = f"{url}{separator}user={b64url(user)}&password={b64url(password)}"
|
||||
try:
|
||||
with opener.open(request_url, timeout=timeout) as response:
|
||||
return json.loads(response.read().decode("utf-8"))
|
||||
except urllib.error.HTTPError as error:
|
||||
body = ""
|
||||
try:
|
||||
body = error.read().decode("utf-8", "replace").strip()
|
||||
except Exception:
|
||||
pass
|
||||
raise NodeAuthError(body or f"请求被拒绝 (HTTP {error.code})", error.code) from None
|
||||
Reference in New Issue
Block a user