一切皆为插件的开发者工具运行时框架
🧩 核心特性:
- 插件热插拔 (importlib 动态加载)
- 依赖自动解析 (拓扑排序 + 循环检测)
- 企业级稳定 (熔断/降级/重试/隔离)
- 事件驱动 (发布/订阅事件总线)
- 完整配置 (YAML 配置 + 热重载)
194 lines
6.3 KiB
Python
194 lines
6.3 KiB
Python
"""TCP HTTP 服务器核心"""
|
|
import socket
|
|
import threading
|
|
import re
|
|
from typing import Any, Callable, Optional
|
|
from .events import TcpEvent, EVENT_CONNECT, EVENT_DISCONNECT, EVENT_DATA, EVENT_REQUEST, EVENT_RESPONSE
|
|
|
|
|
|
class TcpClient:
|
|
"""TCP 客户端连接"""
|
|
|
|
def __init__(self, conn: socket.socket, address: tuple):
|
|
self.conn = conn
|
|
self.address = address
|
|
self.id = f"{address[0]}:{address[1]}"
|
|
|
|
def send(self, data: bytes):
|
|
"""发送数据"""
|
|
self.conn.sendall(data)
|
|
|
|
def close(self):
|
|
"""关闭连接"""
|
|
self.conn.close()
|
|
|
|
|
|
class TcpHttpServer:
|
|
"""TCP HTTP 服务器"""
|
|
|
|
def __init__(self, router, middleware, event_bus=None, host="0.0.0.0", port=8082):
|
|
self.host = host
|
|
self.port = port
|
|
self.router = router
|
|
self.middleware = middleware
|
|
self.event_bus = event_bus
|
|
self._server = None
|
|
self._thread = None
|
|
self._running = False
|
|
self._clients: dict[str, TcpClient] = {}
|
|
|
|
def start(self):
|
|
"""启动服务器"""
|
|
self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self._server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
self._server.bind((self.host, self.port))
|
|
self._server.listen(128)
|
|
self._running = True
|
|
self._thread = threading.Thread(target=self._accept_loop, daemon=True)
|
|
self._thread.start()
|
|
print(f"[http-tcp] 服务器启动: {self.host}:{self.port}")
|
|
|
|
def _accept_loop(self):
|
|
"""接受连接循环"""
|
|
while self._running:
|
|
try:
|
|
conn, address = self._server.accept()
|
|
client = TcpClient(conn, address)
|
|
self._clients[client.id] = client
|
|
|
|
# 触发连接事件
|
|
if self.event_bus:
|
|
self.event_bus.emit(TcpEvent(type=EVENT_CONNECT, client=client))
|
|
|
|
# 启动处理线程
|
|
t = threading.Thread(target=self._handle_client, args=(client,), daemon=True)
|
|
t.start()
|
|
except Exception as e:
|
|
if self._running:
|
|
print(f"[http-tcp] 接受连接失败: {e}")
|
|
|
|
def _handle_client(self, client: TcpClient):
|
|
"""处理客户端请求"""
|
|
buffer = b""
|
|
try:
|
|
while self._running:
|
|
data = client.conn.recv(4096)
|
|
if not data:
|
|
break
|
|
buffer += data
|
|
|
|
# 检查 HTTP 请求是否完整
|
|
if b"\r\n\r\n" in buffer:
|
|
request = self._parse_request(buffer)
|
|
if request:
|
|
# 触发请求事件
|
|
if self.event_bus:
|
|
self.event_bus.emit(TcpEvent(
|
|
type=EVENT_REQUEST,
|
|
client=client,
|
|
context={"request": request}
|
|
))
|
|
|
|
# 路由处理
|
|
response = self.router.handle(request)
|
|
|
|
# 发送响应
|
|
response_bytes = self._format_response(response)
|
|
client.send(response_bytes)
|
|
|
|
# 触发响应事件
|
|
if self.event_bus:
|
|
self.event_bus.emit(TcpEvent(
|
|
type=EVENT_RESPONSE,
|
|
client=client,
|
|
data=response_bytes
|
|
))
|
|
|
|
buffer = b""
|
|
|
|
except Exception as e:
|
|
if self.event_bus:
|
|
self.event_bus.emit(TcpEvent(type=EVENT_ERROR, client=client, context={"error": str(e)}))
|
|
finally:
|
|
del self._clients[client.id]
|
|
client.close()
|
|
if self.event_bus:
|
|
self.event_bus.emit(TcpEvent(type=EVENT_DISCONNECT, client=client))
|
|
|
|
def _parse_request(self, data: bytes) -> Optional[dict]:
|
|
"""解析 HTTP 请求"""
|
|
try:
|
|
text = data.decode("utf-8", errors="replace")
|
|
lines = text.split("\r\n")
|
|
if not lines:
|
|
return None
|
|
|
|
# 解析请求行
|
|
match = re.match(r'(\w+)\s+(\S+)\s+HTTP/(\d\.\d)', lines[0])
|
|
if not match:
|
|
return None
|
|
|
|
method, path, version = match.groups()
|
|
|
|
# 解析头
|
|
headers = {}
|
|
body_start = 0
|
|
for i, line in enumerate(lines[1:], 1):
|
|
if line == "":
|
|
body_start = i + 1
|
|
break
|
|
if ":" in line:
|
|
key, value = line.split(":", 1)
|
|
headers[key.strip()] = value.strip()
|
|
|
|
# 解析体
|
|
content_length = int(headers.get("Content-Length", 0))
|
|
body = "\r\n".join(lines[body_start:]) if body_start else ""
|
|
|
|
return {
|
|
"method": method,
|
|
"path": path,
|
|
"version": version,
|
|
"headers": headers,
|
|
"body": body,
|
|
}
|
|
except Exception:
|
|
return None
|
|
|
|
def _format_response(self, response: dict) -> bytes:
|
|
"""格式化 HTTP 响应"""
|
|
status = response.get("status", 200)
|
|
headers = response.get("headers", {})
|
|
body = response.get("body", "")
|
|
|
|
status_text = {200: "OK", 404: "Not Found", 500: "Internal Server Error"}.get(status, "OK")
|
|
|
|
response_lines = [
|
|
f"HTTP/1.1 {status} {status_text}",
|
|
]
|
|
|
|
if "Content-Type" not in headers:
|
|
headers["Content-Type"] = "text/plain; charset=utf-8"
|
|
headers["Content-Length"] = str(len(body))
|
|
|
|
for key, value in headers.items():
|
|
response_lines.append(f"{key}: {value}")
|
|
|
|
response_lines.append("")
|
|
response_lines.append(body)
|
|
|
|
return "\r\n".join(response_lines).encode("utf-8")
|
|
|
|
def stop(self):
|
|
"""停止服务器"""
|
|
self._running = False
|
|
for client in self._clients.values():
|
|
client.close()
|
|
if self._server:
|
|
self._server.close()
|
|
print("[http-tcp] 服务器已停止")
|
|
|
|
def get_clients(self) -> list[TcpClient]:
|
|
"""获取所有客户端"""
|
|
return list(self._clients.values())
|