/
liquid-g
/
liquid-code
Обзор
Документация
Войти
/
liquid-g
/
liquid-code
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop-0.4
src/liquidcode/base_server.py
136 строк
6 KB
User
v0.4.17: рефакторинг статики и архитектура middleware
07 июл 2026, 12:05
07 июл 2026, 12:05
706b43e
Код
Авторство
О чём код?
""" HTTP-сервер для LiquidCode. Принимает запросы, создаёт HttpRequest, передаёт в Kernel и отправляет ответ. Архитектура статики: 1. HTTP-сервер проверяет путь на префикс /static/ 2. Если есть — пытается отдать файл из public/ (голая статика) 3. Если файл не найден — передаёт запрос в kernel.handle() (StaticFilesMiddleware обработает файл из static/) """ import json import logging from http.server import HTTPServer, BaseHTTPRequestHandler from .http import HttpRequest, HttpResponse, try_serve_static_file from .kernel import Kernel logger = logging.getLogger(__name__) class LiquidHTTPServer(HTTPServer): """Сервер с доступом к ядру приложения.""" def __init__(self, server_address, handler_class, kernel: Kernel): super().__init__(server_address, handler_class) self.kernel = kernel class RequestHandler(BaseHTTPRequestHandler): """Обработчик HTTP-запросов.""" def _handle_request(self, method: str) -> None: # 1. Читаем тело и заголовки content_length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_length) if content_length else b"" headers = {k: v for k, v in self.headers.items()} # 2. Создаём HttpRequest request = HttpRequest( path=self.path, method=method, headers=headers, body=body, query_params=None, # парсится внутри HttpRequest ) # 3. Попытка отдать статику напрямую (без обращения к ядру) # Получаем настройки статики из kernel (через application) app = self.server.kernel.container.get("application") if self.server.kernel.container.has("application") else None static_dir = "public" static_prefix = "/static" if app: static_dir = getattr(app, "_static_dir", "public") static_prefix = getattr(app, "_static_prefix", "/static") static_success, static_response = try_serve_static_file( self.path, static_dir, static_prefix ) if static_success: # Файл найден, отдаём его напрямую content = static_response.get_content() status = static_response.get_status() headers = static_response.get_headers() try: if isinstance(content, bytes): self.send_response(status) for key, value in headers.items(): self.send_header(key, value) self.end_headers() self.wfile.write(content) else: self._send_json(content, status, headers) except (ConnectionAbortedError, BrokenPipeError) as e: logger.debug(f"Соединение разорвано клиентом: {e}") return # 4. Вызываем ядро (если статика не найдена) try: response = self.server.kernel.handle(request) except Exception: logger.exception(f"Необработанное исключение: {method} {self.path}") self._send_json({"error": "Внутренняя ошибка сервера"}, 500, {}) return # 5. Отправляем ответ content = response.get_content() status = response.get_status() headers = response.get_headers() try: if isinstance(content, bytes): # Статические файлы или бинарные данные self.send_response(status) for key, value in headers.items(): self.send_header(key, value) self.end_headers() self.wfile.write(content) else: # JSON-ответ self._send_json(content, status, headers) except (ConnectionAbortedError, BrokenPipeError) as e: logger.debug(f"Соединение разорвано клиентом: {e}") # 6. Вызываем terminate (после отправки ответа) self.server.kernel.terminate(request, response) def _send_json(self, data, status, headers): """Отправляет JSON-ответ.""" try: response_json = json.dumps(data, ensure_ascii=False) except TypeError: logger.error("Данные ответа не сериализуются в JSON") response_json = json.dumps({"error": "Некорректный формат ответа"}) status = 500 try: self.send_response(status) for key, value in headers.items(): self.send_header(key, value) self.send_header("Content-Type", "application/json; charset=utf-8") self.end_headers() self.wfile.write(response_json.encode("utf-8")) except (ConnectionAbortedError, BrokenPipeError) as e: logger.debug(f"Соединение разорвано клиентом при отправке JSON: {e}") def log_message(self, format: str, *args) -> None: """Логирование запросов через стандартный логгер.""" logger.info(f"{self.address_string()} - {format % args}") def create_server(host: str, port: int, kernel: Kernel) -> LiquidHTTPServer: """Создаёт и возвращает настроенный HTTP-сервер.""" server_address = (host, port) return LiquidHTTPServer(server_address, RequestHandler, kernel)