diff --git a/app/application/nettest/__init__.py b/app/application/nettest/__init__.py new file mode 100644 index 000000000..2fec2d96c --- /dev/null +++ b/app/application/nettest/__init__.py @@ -0,0 +1 @@ +"""网络连通性探测规则和目录实现。""" diff --git a/app/application/network/catalogue.py b/app/application/nettest/catalogue.py similarity index 100% rename from app/application/network/catalogue.py rename to app/application/nettest/catalogue.py diff --git a/app/application/network/domain.py b/app/application/nettest/domain.py similarity index 100% rename from app/application/network/domain.py rename to app/application/nettest/domain.py diff --git a/app/application/network/service.py b/app/application/network.py similarity index 98% rename from app/application/network/service.py rename to app/application/network.py index d0dff579f..a64edfc13 100644 --- a/app/application/network/service.py +++ b/app/application/network.py @@ -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, diff --git a/app/application/network/__init__.py b/app/application/network/__init__.py deleted file mode 100644 index a741e9f9b..000000000 --- a/app/application/network/__init__.py +++ /dev/null @@ -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", -] diff --git a/app/startup/composition/network.py b/app/startup/composition/network.py index c1d5fc504..9e2fc4a56 100644 --- a/app/startup/composition/network.py +++ b/app/startup/composition/network.py @@ -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()) diff --git a/tests/fixtures/architecture/dependency-policy.json b/tests/fixtures/architecture/dependency-policy.json index a34fa9e08..278d2758c 100644 --- a/tests/fixtures/architecture/dependency-policy.json +++ b/tests/fixtures/architecture/dependency-policy.json @@ -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"}, diff --git a/tests/test_architecture_egress.py b/tests/test_architecture_egress.py index d020d0ee6..23241f518 100644 --- a/tests/test_architecture_egress.py +++ b/tests/test_architecture_egress.py @@ -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",