#include #include #include #include #include #include #include #include #include #include #include namespace beast = boost::beast; namespace http = beast::http; namespace net = boost::asio; using tcp = boost::asio::ip::tcp; namespace fs = std::filesystem; // 获取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(".txt")) return "text/plain"; if (path.ends_with(".ico")) return "image/x-icon"; return "application/octet-stream"; } // 读取文件内容 std::string read_file(const std::string& path) { std::ifstream file(path, std::ios::binary); if (!file.is_open()) { std::cerr << "无法打开文件: " << path << std::endl; return ""; } std::stringstream buffer; buffer << file.rdbuf(); return buffer.str(); } // 处理HTTP请求 class http_connection : public std::enable_shared_from_this { public: http_connection(tcp::socket socket, const std::string& static_dir) : socket_(std::move(socket)), static_dir_(static_dir) {} 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); std::string target = std::string(request_.target()); // 防止路径遍历攻击 if (target.find("..") != std::string::npos) { response_.result(http::status::forbidden); response_.set(http::field::content_type, "text/plain"); response_.body() = "禁止访问"; send_response(); return; } // 处理根路径 if (target == "/" || target == "/index.html") { serve_file("dashun_index.html"); return; } // 处理健康检查 if (target == "/health") { response_.result(http::status::ok); response_.set(http::field::content_type, "text/plain"); response_.body() = "大顺脚本前端服务运行正常"; send_response(); return; } // 处理API请求 if (target.starts_with("/api/")) { handle_api_request(target); return; } // 处理静态文件 serve_file(target.substr(1)); // 移除开头的'/' } void handle_api_request(const std::string& target) { response_.result(http::status::ok); response_.set(http::field::content_type, "application/json"); if (target == "/api/status") { response_.body() = R"({ "status": "running", "version": "1.0.0", "uptime": "0", "requests_served": 0 })"; } else if (target == "/api/info") { response_.body() = R"({ "name": "大顺脚本前端服务", "description": "为pmc项目提供Web界面", "author": "大顺", "repository": "https://github.com/your-repo/pmc" })"; } else { response_.result(http::status::not_found); response_.set(http::field::content_type, "application/json"); response_.body() = R"({"error": "API endpoint not found"})"; } send_response(); } void serve_file(const std::string& filename) { std::string filepath = static_dir_ + "/" + filename; // 安全检查:确保文件在静态目录内 fs::path requested_path = fs::absolute(filepath); fs::path static_path = fs::absolute(static_dir_); if (!fs::equivalent(requested_path.parent_path(), static_path) && !requested_path.string().starts_with(static_path.string() + "/")) { response_.result(http::status::forbidden); response_.set(http::field::content_type, "text/plain"); response_.body() = "禁止访问"; send_response(); return; } if (!fs::exists(filepath)) { response_.result(http::status::not_found); response_.set(http::field::content_type, "text/plain"); response_.body() = "文件不存在: " + filename; send_response(); return; } std::string content = read_file(filepath); if (content.empty()) { response_.result(http::status::internal_server_error); response_.set(http::field::content_type, "text/plain"); response_.body() = "读取文件失败"; send_response(); return; } response_.result(http::status::ok); response_.set(http::field::content_type, get_mime_type(filename)); response_.body() = content; send_response(); } void send_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) { self->socket_.shutdown(tcp::socket::shutdown_send, ec); }); } tcp::socket socket_; beast::flat_buffer buffer_{8192}; http::request request_; http::response response_; std::string static_dir_; }; // HTTP服务器 class http_server { public: http_server(net::io_context& ioc, tcp::endpoint endpoint, const std::string& static_dir) : acceptor_(ioc), static_dir_(static_dir) { beast::error_code ec; // 打开acceptor acceptor_.open(endpoint.protocol(), ec); if (ec) { throw beast::system_error{ec}; } // 设置地址重用 acceptor_.set_option(net::socket_base::reuse_address(true), ec); if (ec) { throw beast::system_error{ec}; } // 绑定到端点 acceptor_.bind(endpoint, ec); if (ec) { throw beast::system_error{ec}; } // 开始监听 acceptor_.listen(net::socket_base::max_listen_connections, ec); if (ec) { throw beast::system_error{ec}; } } void start_accept() { acceptor_.async_accept( [this](beast::error_code ec, tcp::socket socket) { if (!ec) { std::make_shared(std::move(socket), static_dir_)->start(); } start_accept(); }); } private: tcp::acceptor acceptor_; std::string static_dir_; }; int main() { try { // 静态文件目录 std::string static_dir = "static"; // 确保静态目录存在 if (!fs::exists(static_dir)) { std::cerr << "警告: 静态目录 '" << static_dir << "' 不存在" << std::endl; if (!fs::create_directory(static_dir)) { std::cerr << "错误: 无法创建静态目录" << std::endl; return 1; } } // 检查主页文件是否存在 std::string homepage = static_dir + "/dashun_index.html"; if (!fs::exists(homepage)) { std::cerr << "警告: 主页文件 '" << homepage << "' 不存在" << std::endl; std::cerr << "请确保 dashun_index.html 文件在 static/ 目录中" << std::endl; } // 创建IO上下文 net::io_context ioc{1}; // 创建服务器端点(监听所有地址的8080端口) tcp::endpoint endpoint{tcp::v4(), 8080}; // 创建HTTP服务器 http_server server{ioc, endpoint, static_dir}; server.start_accept(); // 显示启动信息 std::cout << "==========================================" << std::endl; std::cout << "大顺脚本前端服务已启动" << std::endl; std::cout << "监听地址: http://0.0.0.0:8080" << std::endl; std::cout << "静态文件目录: " << static_dir << std::endl; std::cout << "按 Ctrl+C 停止服务" << std::endl; std::cout << "==========================================" << std::endl; ioc.run(); } catch (const std::exception& e) { std::cerr << "错误: " << e.what() << std::endl; return 1; } return 0; }