#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace beast = boost::beast; namespace http = beast::http; namespace websocket = beast::websocket; namespace net = boost::asio; namespace ssl = net::ssl; using tcp = boost::asio::ip::tcp; // 获取 MIME 类型 std::string get_mime_type(const std::string& path) { if (path.ends_with(".html") || path.ends_with(".htm")) return "text/html"; if (path.ends_with(".css")) return "text/css"; if (path.ends_with(".js")) return "application/javascript"; if (path.ends_with(".json")) return "application/json"; if (path.ends_with(".png")) return "image/png"; if (path.ends_with(".jpg") || path.ends_with(".jpeg")) return "image/jpeg"; if (path.ends_with(".gif")) return "image/gif"; if (path.ends_with(".svg")) return "image/svg+xml"; if (path.ends_with(".ico")) return "image/x-icon"; return "text/plain"; } // 读取文件内容 std::string read_file(const std::string& path) { try { std::ifstream file(path, std::ios::binary); if (!file) return ""; std::string content; file.seekg(0, std::ios::end); content.resize(file.tellg()); file.seekg(0, std::ios::beg); file.read(&content[0], content.size()); return content; } catch (...) { return ""; } } // WebSocket 客户端,用于连接LLM后端 class WebSocketClient : public std::enable_shared_from_this { public: WebSocketClient(net::io_context& ioc, const std::string& host, const std::string& port) : resolver_(net::make_strand(ioc)) , ws_(net::make_strand(ioc)) , host_(host) , port_(port) , connected_(false) {} void connect() { resolver_.async_resolve(host_, port_, beast::bind_front_handler(&WebSocketClient::on_resolve, shared_from_this())); } void send(const std::string& message) { if (!connected_) { std::cerr << "Cannot send: not connected to backend" << std::endl; return; } // 将消息放入队列 { std::lock_guard lock(queue_mutex_); send_queue_.push(message); } // 如果当前没有在发送,开始发送 if (!sending_) { send_next(); } } void set_message_handler(std::function handler) { message_handler_ = handler; } bool is_connected() const { return connected_; } private: void on_resolve(beast::error_code ec, tcp::resolver::results_type results) { if (ec) { std::cerr << "Resolve error: " << ec.message() << std::endl; return; } beast::get_lowest_layer(ws_).async_connect(results, beast::bind_front_handler(&WebSocketClient::on_connect, shared_from_this())); } void on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type) { if (ec) { std::cerr << "Connect error: " << ec.message() << std::endl; return; } ws_.async_handshake(host_, "/", beast::bind_front_handler(&WebSocketClient::on_handshake, shared_from_this())); } void on_handshake(beast::error_code ec) { if (ec) { std::cerr << "Handshake error: " << ec.message() << std::endl; return; } std::cout << "Connected to LLM backend at " << host_ << ":" << port_ << std::endl; connected_ = true; // 开始接收消息 do_read(); // 发送欢迎消息 send_welcome(); } void send_welcome() { std::string welcome_msg = R"({"type": "welcome", "message": "Connected to PMC LLM Backend"})"; send(welcome_msg); } void do_read() { ws_.async_read(buffer_, beast::bind_front_handler(&WebSocketClient::on_read, shared_from_this())); } void on_read(beast::error_code ec, std::size_t bytes_transferred) { if (ec == websocket::error::closed) { std::cout << "WebSocket connection closed" << std::endl; connected_ = false; return; } if (ec) { std::cerr << "Read error: " << ec.message() << std::endl; connected_ = false; return; } std::string message = beast::buffers_to_string(buffer_.data()); buffer_.consume(buffer_.size()); // 处理接收到的消息 if (message_handler_) { message_handler_(message); } // 继续读取 do_read(); } void send_next() { std::lock_guard lock(queue_mutex_); if (send_queue_.empty()) { sending_ = false; return; } sending_ = true; std::string message = send_queue_.front(); send_queue_.pop(); ws_.async_write(net::buffer(message), beast::bind_front_handler(&WebSocketClient::on_write, shared_from_this())); } void on_write(beast::error_code ec, std::size_t bytes_transferred) { if (ec) { std::cerr << "Write error: " << ec.message() << std::endl; connected_ = false; return; } // 发送下一个消息 send_next(); } tcp::resolver resolver_; websocket::stream ws_; beast::flat_buffer buffer_; std::string host_; std::string port_; std::atomic connected_; std::queue send_queue_; std::mutex queue_mutex_; std::atomic sending_{false}; std::function message_handler_; }; // HTTP 请求处理器 class HttpSession : public std::enable_shared_from_this { public: HttpSession(tcp::socket socket, const std::string& static_dir, std::shared_ptr ws_client) : socket_(std::move(socket)), static_dir_(static_dir), ws_client_(ws_client) {} void start() { read_request(); } private: void read_request() { auto self = shared_from_this(); http::async_read(socket_, buffer_, request_, [self](beast::error_code ec, std::size_t bytes_transferred) { if (!ec) { self->process_request(); } }); } void process_request() { response_.version(request_.version()); response_.keep_alive(false); // 检查是否为 WebSocket 升级请求 if (websocket::is_upgrade(request_)) { handle_websocket_upgrade(); return; } // 只处理 GET 请求 if (request_.method() != http::verb::get) { response_.result(http::status::bad_request); response_.set(http::field::content_type, "text/plain"); response_.body() = "Invalid method\n"; write_response(); return; } // 获取请求路径 std::string target = request_.target(); if (target.empty() || target == "/") { target = "/llm_chat.html"; // 默认加载聊天界面 } // 防止路径遍历攻击 if (target.find("..") != std::string::npos) { response_.result(http::status::forbidden); response_.set(http::field::content_type, "text/plain"); response_.body() = "Forbidden\n"; write_response(); return; } // 构建文件路径 std::string file_path = static_dir_ + target; // 读取文件 std::string content = read_file(file_path); if (content.empty()) { // 文件不存在,返回 404 if (target == "/llm_chat.html") { // 如果聊天界面不存在,返回默认页面 response_.result(http::status::ok); response_.set(http::field::content_type, "text/html"); response_.body() = "

PMC LLM Chat Interface

Chat interface not found. Please check static directory.

"; } else { response_.result(http::status::not_found); response_.set(http::field::content_type, "text/plain"); response_.body() = "File not found: " + target + "\n"; } } else { // 文件存在,返回内容 response_.result(http::status::ok); response_.set(http::field::content_type, get_mime_type(file_path)); response_.body() = content; } write_response(); } void handle_websocket_upgrade() { try { // 创建 WebSocket 会话 auto ws = std::make_shared>(std::move(socket_)); // 接受 WebSocket 握手 ws->async_accept(request_, [self = shared_from_this(), ws](beast::error_code ec) { if (!ec) { self->handle_websocket_session(ws); } }); } catch (const std::exception& e) { std::cerr << "WebSocket upgrade error: " << e.what() << std::endl; } } void handle_websocket_session(std::shared_ptr> ws) { std::cout << "New WebSocket connection established" << std::endl; // 发送欢迎消息 std::string welcome_msg = R"({"type": "welcome", "message": "Welcome to PMC LLM Chat Interface!"})"; ws->write(net::buffer(welcome_msg)); // 开始读取消息 do_websocket_read(ws); } void do_websocket_read(std::shared_ptr> ws) { auto buffer = std::make_shared(); ws->async_read(*buffer, [self = shared_from_this(), ws, buffer](beast::error_code ec, std::size_t bytes_transferred) { if (ec == websocket::error::closed) { std::cout << "WebSocket connection closed by client" << std::endl; return; } if (ec) { std::cerr << "WebSocket read error: " << ec.message() << std::endl; return; } // 处理接收到的消息 std::string message = beast::buffers_to_string(buffer->data()); self->handle_websocket_message(ws, message); // 继续读取 self->do_websocket_read(ws); }); } void handle_websocket_message(std::shared_ptr> ws, const std::string& message) { try { std::cout << "Received WebSocket message: " << message << std::endl; // 解析 JSON 消息 // 这里简化处理,实际应该使用 JSON 库 if (message.find("\"type\":\"hello\"") != std::string::npos || message.find("hello") != std::string::npos) { // 发送欢迎消息 std::string response = R"({"type": "welcome", "message": "Hello! I'm PMC LLM Assistant. How can I help you today?"})"; ws->write(net::buffer(response)); } else if (message.find("\"type\":\"message\"") != std::string::npos || message.find("\"type\":\"chat\"") != std::string::npos) { // 聊天消息,转发到 LLM 后端 if (ws_client_ && ws_client_->is_connected()) { // 发送思考指示 std::string thinking_msg = R"({"type": "thinking"})"; ws->write(net::buffer(thinking_msg)); // 转发到 LLM 后端 ws_client_->send(message); // 设置消息处理器,将后端响应转发回客户端 ws_client_->set_message_handler([this, ws](const std::string& backend_response) { // 将后端响应转发给 WebSocket 客户端 std::string response = R"({"type": "response", "content": ")" + escape_json(backend_response) + "\"}"; ws->write(net::buffer(response)); }); } else { // LLM 后端未连接,返回模拟响应 std::string response = R"({"type": "response", "content": "LLM backend is not connected. This is a simulated response."})"; ws->write(net::buffer(response)); } } else if (message.find("\"type\":\"change_model\"") != std::string::npos) { // 处理模型切换请求 std::string response = R"({"type": "system", "message": "Model change request received"})"; ws->write(net::buffer(response)); } else { // 默认处理:直接回复 std::string response = R"({"type": "response", "content": "I received your message: )" + escape_json(message) + "\"}"; ws->write(net::buffer(response)); } } catch (const std::exception& e) { std::cerr << "Error handling WebSocket message: " << e.what() << std::endl; std::string error_msg = R"({"type": "error", "message": "Error processing message"})"; ws->write(net::buffer(error_msg)); } } std::string escape_json(const std::string& input) { std::string output; output.reserve(input.length()); for (char c : input) { switch (c) { case '"': output += "\\\""; break; case '\\': output += "\\\\"; break; case '\b': output += "\\b"; break; case '\f': output += "\\f"; break; case '\n': output += "\\n"; break; case '\r': output += "\\r"; break; case '\t': output += "\\t"; break; default: output += c; break; } } return output; } void write_response() { auto self = shared_from_this(); response_.content_length(response_.body().size()); http::async_write(socket_, response_, [self](beast::error_code ec, std::size_t bytes_transferred) { // 连接关闭后自动清理 }); } tcp::socket socket_; beast::flat_buffer buffer_; http::request request_; http::response response_; std::string static_dir_; std::shared_ptr ws_client_; }; // HTTP 服务器 class HttpServer { public: HttpServer(net::io_context& ioc, tcp::endpoint endpoint, const std::string& static_dir, const std::string& backend_host, const std::string& backend_port) : acceptor_(ioc) , static_dir_(static_dir) , backend_host_(backend_host) , backend_port_(backend_port) { beast::error_code ec; // 打开 acceptor acceptor_.open(endpoint.protocol(), ec); if (ec) { std::cerr << "Open error: " << ec.message() << std::endl; return; } // 设置地址重用 acceptor_.set_option(net::socket_base::reuse_address(true), ec); if (ec) { std::cerr << "Set option error: " << ec.message() << std::endl; return; } // 绑定到地址 acceptor_.bind(endpoint, ec); if (ec) { std::cerr << "Bind error: " << ec.message() << std::endl; return; } // 开始监听 acceptor_.listen(net::socket_base::max_listen_connections, ec); if (ec) { std::cerr << "Listen error: " << ec.message() << std::endl; return; } std::cout << "HTTP server listening on " << endpoint.address().to_string() << ":" << endpoint.port() << std::endl; // 创建 WebSocket 客户端连接到 LLM 后端 ws_client_ = std::make_shared(ioc, backend_host_, backend_port_); // 尝试连接到后端 std::cout << "Connecting to LLM backend at " << backend_host_ << ":" << backend_port_ << std::endl; ws_client_->connect(); // 开始接受连接 do_accept(); } private: void do_accept() { acceptor_.async_accept( [this](beast::error_code ec, tcp::socket socket) { if (!ec) { std::make_shared(std::move(socket), static_dir_, ws_client_)->start(); } // 继续接受新连接 do_accept(); }); } tcp::acceptor acceptor_; std::string static_dir_; std::string backend_host_; std::string backend_port_; std::shared_ptr ws_client_; }; int main(int argc, char* argv[]) { try { // 默认配置 std::string host = "0.0.0.0"; unsigned short port = 8080; std::string static_dir = "./static"; std::string backend_host = "localhost"; std::string backend_port = "8081"; // 解析命令行参数 for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--host" && i + 1 < argc) { host = argv[++i]; } else if (arg == "--port" && i + 1 < argc) { port = static_cast(std::stoi(argv[++i])); } else if (arg == "--static" && i + 1 < argc) { static_dir = argv[++i]; } else if (arg == "--backend-host" && i + 1 < argc) { backend_host = argv[++i]; } else if (arg == "--backend-port" && i + 1 < argc) { backend_port = argv[++i]; } else if (arg == "--help") { std::cout << "Usage: " << argv[0] << " [options]\n" << "Options:\n" << " --host
HTTP server host (default: 0.0.0.0)\n" << " --port HTTP server port (default: 8080)\n" << " --static Static files directory (default: ./static)\n" << " --backend-host LLM backend host (default: localhost)\n" << " --backend-port LLM backend port (default: 8081)\n" << " --help Show this help message\n"; return 0; } } // 检查静态目录 if (!std::filesystem::exists(static_dir)) { std::cout << "Static directory '" << static_dir << "' does not exist. Creating it..." << std::endl; std::filesystem::create_directories(static_dir); // 创建默认的 llm_chat.html 文件 std::string chat_html_path = static_dir + "/llm_chat.html"; if (!std::filesystem::exists(chat_html_path)) { std::cout << "Creating default chat interface..." << std::endl; // 这里可以添加默认的 HTML 内容 } } std::cout << "PMC LLM Frontend Server\n"; std::cout << "=======================\n"; std::cout << "HTTP Server: " << host << ":" << port << "\n"; std::cout << "Static Directory: " << static_dir << "\n"; std::cout << "LLM Backend: " << backend_host << ":" << backend_port << "\n"; std::cout << "==============================\n\n"; // 创建 I/O 上下文 net::io_context ioc; // 创建 HTTP 服务器 tcp::endpoint endpoint(net::ip::make_address(host), port); HttpServer server(ioc, endpoint, static_dir, backend_host, backend_port); // 运行 I/O 上下文 ioc.run(); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }