#pragma once #include "squirrel.h" #include "sqstdaux.h" #include "sqstdblob.h" #include "sqstdio.h" #include "sqstdmath.h" #include "sqstdstring.h" #include "sqstdsystem.h" #include #include #include #include #include #include #include #include #include // 新增:用于 std::unique_ptr #include #include #include #include #include // 全局变量声明(需在cpp文件中定义) extern HSQUIRRELVM v; extern std::recursive_mutex SqMtx; using tcp = asio::ip::tcp; // ====================== 工具函数 ====================== // 字符串转小写 std::string to_lower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); return s; } // 修剪字符串两端空格 std::string trim(std::string s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isspace(c); })); s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char c) { return !std::isspace(c); }).base(), s.end()); return s; } // 解析 URL,分离路径和查询参数 void parse_url(const std::string& url, std::string& path, std::string& query) { size_t query_pos = url.find('?'); if (query_pos == std::string::npos) { path = url; query = ""; } else { path = url.substr(0, query_pos); query = url.substr(query_pos + 1); } } // ====================== HTTP 头解析结构体 ====================== struct HttpRequestHeader { std::string method; // GET/POST/PUT/DELETE 等 std::string path; // 请求路径(如 /api/test) std::string version; // HTTP/1.1 / HTTP/1.0 std::string query_string; // URL 查询参数(如 ?id=1&name=test) // 头部键值对(统一转小写,避免大小写问题) std::unordered_map headers; // 便捷获取头部值(兼容大小写) std::string get_header(const std::string& key) const { std::string lower_key = to_lower(key); auto it = headers.find(lower_key); return it != headers.end() ? it->second : ""; } }; // ====================== HTTP 头解析函数 ====================== HttpRequestHeader parse_http_header(const std::string& raw_header) { HttpRequestHeader result; std::istringstream iss(raw_header); std::string line; bool first_line_parsed = false; // 第一步:解析请求首行(仅解析第一行,避免重复) while (std::getline(iss, line)) { // 移除行尾的 \r if (!line.empty() && line.back() == '\r') { line.pop_back(); } line = trim(line); // 跳过空行 if (line.empty()) { continue; } // 仅解析第一个非空行作为请求首行 if (!first_line_parsed) { std::string url; std::istringstream line_iss(line); line_iss >> result.method >> url >> result.version; parse_url(url, result.path, result.query_string); std::transform(result.method.begin(), result.method.end(), result.method.begin(), [](unsigned char c) { return std::toupper(c); }); first_line_parsed = true; continue; } // 解析头部键值对 size_t colon_pos = line.find(':'); if (colon_pos == std::string::npos) { continue; } std::string key = trim(line.substr(0, colon_pos)); std::string value = trim(line.substr(colon_pos + 1)); result.headers[to_lower(key)] = value; } return result; } // ====================== 读取完整原始请求 ====================== std::string read_full_raw_request(tcp::socket& socket, HttpRequestHeader& header_out) { asio::streambuf buf; std::string full_request; asio::error_code ec; try { // 第一步:读取头部(直到 \r\n\r\n) size_t header_bytes = asio::read_until(socket, buf, "\r\n\r\n", ec); if (ec) { throw asio::system_error(ec); } // 提取头部内容 std::string header_str(asio::buffer_cast(buf.data()), header_bytes); full_request = header_str; // 安全清理头部末尾的 \r\n\r\n(核心修复:先判断是否找到) size_t end_header_pos = header_str.find("\r\n\r\n"); // 用 find 而非 find_last_of,精准匹配结束符 if (end_header_pos != std::string::npos) { // 只保留头部内容,截断后面的空行 header_str = header_str.substr(0, end_header_pos); } // 解析头部 header_out = parse_http_header(header_str); // 第二步:读取请求体(严格按 Content-Length 读取) size_t content_len = 0; std::string cl_str = header_out.get_header("Content-Length"); if (!cl_str.empty()) { try { content_len = std::stoul(cl_str); } catch (...) { content_len = 0; } } // 清空缓冲区中已读取的头部数据 buf.consume(header_bytes); // 读取请求体(防越界) if (content_len > 0) { // 先检查剩余缓冲区是否足够,避免读取超限 size_t buf_available = buf.size(); if (buf_available < content_len) { // 读取剩余需要的字节 asio::read(socket, buf, asio::transfer_exactly(content_len - buf_available), ec); if (ec && ec != asio::error::eof) { throw asio::system_error(ec); } } // 提取请求体(防缓冲区数据不足) size_t actual_read = std::min(buf.size(), content_len); std::string body_str(asio::buffer_cast(buf.data()), actual_read); full_request += body_str; // 清理缓冲区 buf.consume(actual_read); } } catch (...) { throw; } return full_request; } // ====================== 将 HttpRequestHeader 转为 Squirrel Table ====================== void push_http_header_to_squirrel(HSQUIRRELVM v, const HttpRequestHeader& header) { // 1. 创建空 Table sq_newtable(v); // 2. 设置基础字段(method/path/version/query_string) // method sq_pushstring(v, _SC("method"), -1); sq_pushstring(v, header.method.c_str(), -1); sq_rawset(v, -3); // path sq_pushstring(v, _SC("path"), -1); sq_pushstring(v, header.path.c_str(), -1); sq_rawset(v, -3); // version sq_pushstring(v, _SC("version"), -1); sq_pushstring(v, header.version.c_str(), -1); sq_rawset(v, -3); // query_string sq_pushstring(v, _SC("query_string"), -1); sq_pushstring(v, header.query_string.c_str(), -1); sq_rawset(v, -3); // 3. 创建 headers 子 Table sq_pushstring(v, _SC("headers"), -1); sq_newtable(v); // 遍历头部键值对,写入子 Table for (const auto& pair : header.headers) { sq_pushstring(v, pair.first.c_str(), -1); // 键(小写) sq_pushstring(v, pair.second.c_str(), -1); // 值 sq_rawset(v, -3); } // 将 headers 子 Table 写入主 Table sq_rawset(v, -3); } // ====================== 处理客户端请求 ====================== void handle_client(tcp::socket* socket, HSQOBJECT HttpServerObject) { if (!socket || !socket->is_open()) { std::cerr << "无效的 socket 连接" << std::endl; return; } try { // 读取完整请求并解析头部 HttpRequestHeader header; std::string full_request = read_full_raw_request(*socket, header); // 加锁操作 Squirrel VM(lock_guard 自动管理锁生命周期) std::lock_guard lock(SqMtx); // 执行 Squirrel 的 Event 函数 SQInteger top = sq_gettop(v); // 保存栈状态 sq_pushobject(v, HttpServerObject); sq_pushstring(v, _SC("Event"), -1); if (SQ_SUCCEEDED(sq_get(v, -2))) { // 压入函数参数: // 参数1:HttpServerObject(this) sq_pushobject(v, HttpServerObject); // 参数2:socket 指针(userpointer) sq_pushuserpointer(v, socket); // 参数3:解析后的 header table push_http_header_to_squirrel(v, header); // 参数4:完整原始请求字符串 size_t body_start_pos = full_request.find("\r\n\r\n"); std::string request_body = (body_start_pos != std::string::npos) ? full_request.substr(body_start_pos + 4) : ""; sq_pushstring(v, request_body.c_str(), request_body.size()); // 调用函数:参数数量=4,无返回值,开启错误捕获 sq_call(v, 4, SQFalse, SQTrue); } sq_settop(v, top); // 恢复栈状态 } catch (const asio::system_error& e) { std::cerr << "网络错误: " << e.what() << " (错误码: " << e.code() << ")" << std::endl; } catch (const std::exception& e) { std::cerr << "处理请求错误: " << e.what() << std::endl; } catch (...) { std::cerr << "未知错误" << std::endl; } } // ====================== 服务器上下文(支持多实例) ====================== struct HttpServerCtx { uint64_t server_id; // 唯一服务器ID(多实例核心) std::atomic running{ true }; // 退出标志(atomic不可拷贝) HSQUIRRELVM vm; // Squirrel VM HSQOBJECT http_server_obj; // 保存的对象 asio::io_context* io_context = nullptr; // IO上下文(用于停止) std::string host; // 记录主机(调试用) std::string port; // 记录端口(调试用) // 禁用拷贝构造和赋值(显式声明,避免误拷贝) HttpServerCtx(const HttpServerCtx&) = delete; HttpServerCtx& operator=(const HttpServerCtx&) = delete; // 允许移动构造(用于unique_ptr) HttpServerCtx(HttpServerCtx&&) = default; HttpServerCtx& operator=(HttpServerCtx&&) = default; // 构造函数 HttpServerCtx() = default; HttpServerCtx(uint64_t id, HSQUIRRELVM vm_ptr, HSQOBJECT obj, const std::string& h, const std::string& p) : server_id(id), vm(vm_ptr), http_server_obj(obj), host(h), port(p) { } }; // ====================== 全局服务器管理(核心修复:用unique_ptr存储,避免拷贝) ====================== static std::unordered_map> g_server_ctxs; static std::atomic g_next_server_id{ 1 }; // 自增唯一ID(atomic不可拷贝,直接用) static std::mutex g_server_mutex; // 保护全局容器 // ====================== 服务器主函数 ====================== void start_server(uint64_t server_id, const std::string& host, const std::string& port) { // 查找上下文(加锁) std::unique_lock lock(g_server_mutex); auto it = g_server_ctxs.find(server_id); if (it == g_server_ctxs.end()) { std::cerr << "服务器上下文不存在 (ID: " << server_id << ")" << std::endl; return; } HttpServerCtx* ctx = it->second.get(); // 获取指针,不拷贝 lock.unlock(); // 手动解锁 asio::io_context io_context; ctx->io_context = &io_context; // 关联IO上下文 try { tcp::acceptor acceptor(io_context, tcp::endpoint(asio::ip::make_address(host), std::stoi(port))); std::cout << "Server [" << server_id << "] listening on " << host << ":" << port << std::endl; // 循环监听(支持退出) while (ctx->running.load(std::memory_order_relaxed)) { // 原子操作读取 tcp::socket* socket = new tcp::socket(io_context); try { acceptor.accept(*socket); // 启动客户端处理线程 std::thread(handle_client, std::move(socket), ctx->http_server_obj).detach(); } catch (const asio::system_error& e) { delete socket; // 中断时释放Socket if (e.code() != asio::error::operation_aborted && ctx->running.load(std::memory_order_relaxed)) { std::cerr << "Server [" << server_id << "] Accept error: " << e.what() << std::endl; } } } } catch (const std::exception& e) { std::cerr << "Server [" << server_id << "] error: " << e.what() << std::endl; } // 停止IO上下文 io_context.stop(); std::cout << "Server [" << server_id << "] stopped" << std::endl; } // ====================== Squirrel 绑定函数 - 创建HTTP客户端 ====================== static SQInteger CreateHttp(HSQUIRRELVM v) { const SQChar* Host; sq_getstring(v, 2, &Host); const SQChar* Service; sq_getstring(v, 3, &Service); const SQChar* Content; sq_getstring(v, 4, &Content); try { asio::io_context ioContext; asio::ip::tcp::resolver resolver(ioContext); auto endpoints = resolver.resolve(Host, Service); asio::ip::tcp::socket socket(ioContext); asio::connect(socket, endpoints); // 发送HTTP请求 std::string request = Content; asio::write(socket, asio::buffer(request)); // 读取响应 asio::streambuf response; asio::error_code error; while (asio::read(socket, response, asio::transfer_at_least(1), error)) {} if (error && error != asio::error::eof) { throw asio::system_error(error); } // 将响应内容返回 std::istream responseStream(&response); std::ostringstream oss; oss << responseStream.rdbuf(); sq_pushstring(v, oss.str().c_str(), -1); return 1; } catch (const std::exception& e) { return sq_throwerror(v, e.what()); } catch (...) { return sq_throwerror(v, _SC("Unknown error occurred")); } } // ====================== Squirrel 绑定函数 - 创建HTTP服务器(核心修复:避免拷贝atomic) ====================== static SQInteger CreateHttpServer(HSQUIRRELVM v) { const SQChar* host = nullptr; const SQChar* port = nullptr; HSQOBJECT HttpServerObject; // 1. 参数校验 if (SQ_FAILED(sq_getstring(v, 2, &host)) || SQ_FAILED(sq_getstring(v, 3, &port)) || SQ_FAILED(sq_getstackobj(v, 4, &HttpServerObject))) { sq_pushstring(v, _SC("param error: need (host:str, port:str, obj:HSQOBJECT)"), -1); return SQ_ERROR; } // 2. 生成唯一服务器ID uint64_t server_id = g_next_server_id.fetch_add(1, std::memory_order_relaxed); try { // 3. 动态创建上下文(避免拷贝) auto ctx_ptr = std::unique_ptr(new HttpServerCtx(server_id, v, HttpServerObject, std::string(host), std::string(port))); sq_addref(v, &ctx_ptr->http_server_obj); // 增加引用 // 4. 加锁存入全局(移动语义,不拷贝) std::lock_guard lock(g_server_mutex); g_server_ctxs[server_id] = std::move(ctx_ptr); // 移动unique_ptr,不拷贝atomic // 5. 启动服务器线程 std::thread server_thread(start_server, server_id, std::string(host), std::string(port)); server_thread.detach(); // 6. 返回唯一ID给Squirrel sq_pushinteger(v, static_cast(server_id)); return 1; } catch (...) { // 异常清理 sq_release(v, &HttpServerObject); // 直接释放传入的对象 std::lock_guard lock(g_server_mutex); g_server_ctxs.erase(server_id); sq_pushbool(v, false); return 1; } } // ====================== Squirrel 绑定函数 - 停止HTTP服务器 ====================== static SQInteger StopHttpServer(HSQUIRRELVM v) { SQInteger server_id_int; if (SQ_FAILED(sq_getinteger(v, 2, &server_id_int)) || server_id_int <= 0) { sq_pushstring(v, _SC("invalid server ID"), -1); return SQ_ERROR; } uint64_t server_id = static_cast(server_id_int); std::lock_guard lock(g_server_mutex); auto it = g_server_ctxs.find(server_id); if (it == g_server_ctxs.end()) { // 修正:拼接错误信息中的 server ID std::string err_msg = "server instance not found (ID: " + std::to_string(server_id) + ")"; sq_pushstring(v, err_msg.c_str(), -1); return SQ_ERROR; } HttpServerCtx* ctx = it->second.get(); // 1. 标记停止(原子操作) ctx->running.store(false, std::memory_order_relaxed); // 2. 强制停止 IO 上下文(中断所有异步操作) if (ctx->io_context) { ctx->io_context->stop(); } // 3. 释放 Squirrel 引用 sq_release(ctx->vm, &ctx->http_server_obj); // 4. 移除上下文(unique_ptr 自动析构) g_server_ctxs.erase(it); sq_pushbool(v, true); return 1; } // ====================== Squirrel 绑定函数 - 发送HTTP响应 ====================== static SQInteger HttpServerResponse_Write(HSQUIRRELVM v) { SQUserPointer P; if (SQ_FAILED(sq_getuserpointer(v, 2, &P))) { return sq_throwerror(v, _SC("invalid socket pointer")); } tcp::socket* socket = static_cast(P); const SQChar* Content; if (SQ_FAILED(sq_getstring(v, 3, &Content))) { return sq_throwerror(v, _SC("invalid response content")); } try { if (socket && socket->is_open()) { std::string response = Content; asio::write(*socket, asio::buffer(response)); } } catch (const std::exception& e) { // 清理资源 if (socket) { asio::error_code ec; socket->close(ec); delete socket; } return sq_throwerror(v, e.what()); } catch (...) { if (socket) { asio::error_code ec; socket->close(ec); delete socket; } return sq_throwerror(v, _SC("Unknown error occurred")); } // 最后清理socket if (socket) { asio::error_code ec; socket->close(ec); delete socket; } return 0; } // ====================== 辅助函数 - 注册Squirrel函数 ====================== static SQInteger register_Dio_func(HSQUIRRELVM v, SQFUNCTION f, const char* fname) { sq_pushroottable(v); sq_pushstring(v, fname, -1); sq_newclosure(v, f, 0); sq_newslot(v, -3, SQFalse); sq_pop(v, 1); // 弹出根表 return 0; } // ====================== 注册所有Dio函数 ====================== static void RegisterDio(HSQUIRRELVM v) { // 创建HTTP客户端 register_Dio_func(v, CreateHttp, _SC("Sq_CreateHttp")); // HTTP服务器相关 register_Dio_func(v, CreateHttpServer, _SC("Sq_CreateHttpServer")); register_Dio_func(v, StopHttpServer, _SC("Sq_StopHttpServer")); // 发送HTTP响应 register_Dio_func(v, HttpServerResponse_Write, _SC("Sq_HttpServerResponse_Write")); }