fix(system): preserve network probe service contracts

This commit is contained in:
jxxghp
2026-09-24 12:41:17 +08:00
parent 1d84d17dc4
commit 027b4fe75f
8 changed files with 38 additions and 50 deletions

View File

@@ -0,0 +1 @@
"""网络连通性探测规则和目录实现。"""

View File

@@ -1,12 +1,12 @@
"""执行受限网络连通性探测并管理其应用服务生命周期。"""
"""受控外部网络探测应用服务。"""
from collections.abc import Collection, Mapping
from time import monotonic
from typing import Any, Callable, Optional
from urllib.parse import urljoin, urlparse
from .catalogue import build_network_rules
from .domain import (
from app.application.nettest.catalogue import build_network_rules
from app.application.nettest.domain import (
NetworkTestLogger,
NetworkTestResponse,
NetworkTestResult,

View File

@@ -1,29 +0,0 @@
"""网络探测应用服务及其公开类型。"""
from .domain import (
NetworkTestLogger,
NetworkTestResponse,
NetworkTestResult,
NetworkTestRule,
NetworkTestTarget,
NetworkTestTransport,
)
from .service import (
NetworkTestService,
configure_network_test_service,
get_configured_network_test_service,
reset_network_test_service,
)
__all__ = [
"NetworkTestLogger",
"NetworkTestResponse",
"NetworkTestResult",
"NetworkTestRule",
"NetworkTestService",
"NetworkTestTarget",
"NetworkTestTransport",
"configure_network_test_service",
"get_configured_network_test_service",
"reset_network_test_service",
]

View File

@@ -71,13 +71,15 @@ class _NetworkTestTransportAdapter:
user_agent: Optional[str] = None,
) -> Optional[NetworkTestResponse]:
"""使用固定超时、证书校验和手动重定向策略请求目标。"""
return await self.request(
"GET",
url,
proxy=proxy,
headers=headers,
user_agent=user_agent,
)
response = await AsyncRequestUtils(
proxies=proxy,
headers=dict(headers) if headers else None,
timeout=10,
ua=user_agent or "",
verify=True,
follow_redirects=False,
).get_res(url, allow_redirects=False)
return cast(Optional[NetworkTestResponse], response)
async def request(
self,
@@ -90,22 +92,33 @@ class _NetworkTestTransportAdapter:
json_body: Optional[Mapping[str, Any]] = None,
) -> Optional[NetworkTestResponse]:
"""按请求规则发送 HTTP 请求并保持证书校验与重定向关闭。"""
request_kwargs: dict[str, Any] = {}
if json_body is not None:
request_kwargs["json"] = dict(json_body)
response = await AsyncRequestUtils(
if method.upper() == "GET":
return await self.get(
url,
proxy=proxy,
headers=headers,
user_agent=user_agent,
)
request = AsyncRequestUtils(
proxies=proxy,
headers=dict(headers) if headers else None,
timeout=10,
ua=user_agent or "",
verify=True,
follow_redirects=False,
).request(
method=method,
url=url,
follow_redirects=False,
**request_kwargs,
)
if method.upper() == "POST":
response = await request.post_res(
url,
json=dict(json_body) if json_body is not None else None,
allow_redirects=False,
)
else:
response = await request.request(
method=method,
url=url,
follow_redirects=False,
)
return cast(Optional[NetworkTestResponse], response)
async def websocket(
@@ -154,7 +167,7 @@ def _read_network_test_setting(key: str, default: Any = None) -> Any:
def _list_enabled_network_module_ids() -> tuple[str, ...]:
"""按模块运行时的当前配置投影网络相关的已启用宿主模块。"""
from app.runtime.extensions.module.manager import ModuleManager
from app.application.module import ModuleManager
return tuple(spec.id for spec in ModuleManager().list_enabled_specs())

View File

@@ -156,8 +156,9 @@
"classification": "approved_exception",
"reason_code": "streaming_protocol",
"owner": "$source",
"reason": "产品长连接或 SCGI/XML-RPC transport 由该协议模块精确持有。",
"reason": "产品长连接与 WSS 连通探测由协议适配器精确持有,SCGI/XML-RPC transport 由对应模块持有。",
"facts": [
{"source": "app.startup.composition.network", "target": "websocket", "kind": "raw_transport", "fingerprint": "09e1909ac750fe40b145dde8fee52eedf84aaf2123f6a1bc38109252c89d6000"},
{"source": "app.modules.qqbot.gateway", "target": "websocket", "kind": "raw_transport", "fingerprint": "6f6b7d61f3a95e620e67c450d544f0aa077f087a188d98f32e4501d94c6b37ae"},
{"source": "app.modules.rtorrent.rtorrent", "target": "socket.create_connection", "kind": "protocol_operation", "fingerprint": "878114d6b1bda091bd3b3d8aa819831d84381682feba78267592d2593295af90"},
{"source": "app.modules.rtorrent.rtorrent", "target": "xmlrpc.client.ServerProxy", "kind": "protocol_operation", "fingerprint": "2ac64b5670c930bc28bb2135cc4b73891c1cae01a74a9586f74e302878c45c85"},

View File

@@ -52,6 +52,7 @@ FROZEN_EGRESS_EDGES_BY_REASON = {
("app.modules.webpush", "pywebpush"),
},
"streaming_protocol": {
("app.startup.composition.network", "websocket"),
("app.modules.qqbot.gateway", "websocket"),
("app.modules.rtorrent.rtorrent", "socket.create_connection"),
("app.modules.rtorrent.rtorrent", "xmlrpc.client.ServerProxy"),
@@ -137,6 +138,7 @@ FROZEN_EGRESS_FINGERPRINT_BY_EDGE = {
("app.modules.transmission.transmission", "transmission_rpc"): "1652e661cb17dbadb039fdc4ab73d6d06e47eb118292ba3693313e45834959cf",
("app.modules.webpush", "pywebpush"): "389c73b06150e3d5bcaf31f35a25178873d2ed38ef9a28354cb9bab691eeab76",
("app.modules.wechat.wechatbot", "websocket"): "1bae78270eadce0571e2caaa111a5c0a9065ba2da97ebb26b8a5b76d3ed5eef6",
("app.startup.composition.network", "websocket"): "09e1909ac750fe40b145dde8fee52eedf84aaf2123f6a1bc38109252c89d6000",
("app.modules.zspace.zspace", "requests"): "9df3fd27b9696d45a72e7c8f67b5a9ad79a7371d1fe690bbaa17485bd1960d51",
("app.startup.lifecycle", "urllib3"): "cb6f0a314aeb1e2d3e76c240aa20460ac0c36d9f5c18c1a6ea3170f64dd3366b",
("app.testing.network", "socket.getaddrinfo"): "ebf33718b54c81e1f575401da4a0bef5aa0e1ddc284e201f557897feeca71c6d",