- nebula create mod/key/list-templates 模组脚手架 - nebula dev 开发模式热重载 - manifest permissions.imports 权限白名单机制 - system-monitor 系统监控仪表盘插件 - 默认端口统一为 10086 - 修复 _init_nbpf 误读 Ed25519 私钥为 RSA 的 bug - 更新 README.md 文档
127 lines
4.4 KiB
Python
127 lines
4.4 KiB
Python
"""HTTP 服务器核心"""
|
|
import threading
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
from typing import Any
|
|
from oss.config import get_config
|
|
from oss.logger.logger import Log
|
|
|
|
|
|
class Request:
|
|
"""请求对象"""
|
|
def __init__(self, method, path, headers, body):
|
|
self.method = method
|
|
self.path = path
|
|
self.headers = headers
|
|
self.body = body
|
|
|
|
|
|
class Response:
|
|
"""响应对象"""
|
|
def __init__(self, status=200, headers=None, body=""):
|
|
self.status = status
|
|
self.headers = headers or {}
|
|
self.body = body
|
|
|
|
|
|
class HttpServer:
|
|
"""HTTP 服务器"""
|
|
|
|
def __init__(self, router, middleware, host=None, port=None):
|
|
config = get_config()
|
|
self.host = host or config.get("HOST", "127.0.0.1")
|
|
self.port = port or config.get("HTTP_API_PORT", 10086)
|
|
self.router = router
|
|
self.middleware = middleware
|
|
self._server = None
|
|
self._thread = None
|
|
|
|
def start(self):
|
|
"""启动服务器"""
|
|
handler = self._create_handler()
|
|
self._server = HTTPServer((self.host, self.port), handler)
|
|
self._thread = threading.Thread(target=self._server.serve_forever, daemon=True)
|
|
self._thread.start()
|
|
Log.info("Core", f"HTTP 服务器启动: {self.host}:{self.port}")
|
|
|
|
def stop(self):
|
|
"""停止服务器"""
|
|
if self._server:
|
|
self._server.shutdown()
|
|
Log.info("Core", "HTTP 服务器已停止")
|
|
|
|
def _create_handler(self):
|
|
"""创建请求处理器"""
|
|
router = self.router
|
|
middleware = self.middleware
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
self._handle("GET")
|
|
|
|
def do_POST(self):
|
|
self._handle("POST")
|
|
|
|
def do_PUT(self):
|
|
self._handle("PUT")
|
|
|
|
def do_DELETE(self):
|
|
self._handle("DELETE")
|
|
|
|
def do_OPTIONS(self):
|
|
"""处理 CORS 预检请求"""
|
|
config = get_config()
|
|
allowed_origins = config.get("CORS_ALLOWED_ORIGINS", ["http://localhost:3000", "http://127.0.0.1:3000"])
|
|
origin = self.headers.get("Origin", "")
|
|
|
|
if origin in allowed_origins or "*" in allowed_origins:
|
|
self.send_response(200)
|
|
self.send_header("Access-Control-Allow-Origin", origin if origin else "*")
|
|
self.send_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
self.send_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
self.send_header("Access-Control-Allow-Credentials", "true")
|
|
else:
|
|
self.send_response(204)
|
|
self.end_headers()
|
|
|
|
def _handle(self, method):
|
|
content_length = int(self.headers.get("Content-Length", 0))
|
|
body = self.rfile.read(content_length) if content_length else b""
|
|
|
|
req = Request(
|
|
method=method,
|
|
path=self.path,
|
|
headers=dict(self.headers),
|
|
body=body.decode("utf-8")
|
|
)
|
|
|
|
# 执行中间件
|
|
ctx = {"request": req, "response": None}
|
|
result = middleware.run(ctx)
|
|
if result:
|
|
self._send_response(result, ctx)
|
|
return
|
|
|
|
# 路由匹配
|
|
resp = router.handle(req)
|
|
self._send_response(resp, ctx)
|
|
|
|
def _send_response(self, resp: Response, ctx: dict = None):
|
|
try:
|
|
self.send_response(resp.status)
|
|
extra_headers = (ctx or {}).get("response_headers", {})
|
|
merged = {**extra_headers, **resp.headers}
|
|
for k, v in merged.items():
|
|
self.send_header(k, v)
|
|
self.end_headers()
|
|
if isinstance(resp.body, str):
|
|
self.wfile.write(resp.body.encode("utf-8"))
|
|
else:
|
|
self.wfile.write(resp.body)
|
|
except (BrokenPipeError, ConnectionAbortedError, ConnectionResetError):
|
|
pass
|
|
|
|
def log_message(self, format, *args):
|
|
Log.debug("Core", format % args)
|
|
|
|
return Handler
|