Files
dps_lib/include/SqrReg_Dio.hpp
2026-04-22 19:36:06 +08:00

547 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "squirrel.h"
#include "sqstdaux.h"
#include "sqstdblob.h"
#include "sqstdio.h"
#include "sqstdmath.h"
#include "sqstdstring.h"
#include "sqstdsystem.h"
#include <asio.hpp>
#include <asio/ssl.hpp>
#include <asio/basic_socket_streambuf.hpp>
#include <iostream>
#include <thread>
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <memory> // 新增:用于 std::unique_ptr
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <stdint.h>
// 全局变量声明需在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<std::string, std::string> 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<const char*>(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<const char*>(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 VMlock_guard 自动管理锁生命周期)
std::lock_guard<std::recursive_mutex> 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))) {
// 压入函数参数:
// 参数1HttpServerObjectthis
sq_pushobject(v, HttpServerObject);
// 参数2socket 指针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<bool> 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<uint64_t, std::unique_ptr<HttpServerCtx>> g_server_ctxs;
static std::atomic<uint64_t> g_next_server_id{ 1 }; // 自增唯一IDatomic不可拷贝直接用
static std::mutex g_server_mutex; // 保护全局容器
// ====================== 服务器主函数 ======================
void start_server(uint64_t server_id, const std::string& host, const std::string& port) {
// 查找上下文(加锁)
std::unique_lock<std::mutex> 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<HttpServerCtx>(new HttpServerCtx(server_id, v, HttpServerObject, std::string(host), std::string(port)));
sq_addref(v, &ctx_ptr->http_server_obj); // 增加引用
// 4. 加锁存入全局(移动语义,不拷贝)
std::lock_guard<std::mutex> 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<SQInteger>(server_id));
return 1;
}
catch (...) {
// 异常清理
sq_release(v, &HttpServerObject); // 直接释放传入的对象
std::lock_guard<std::mutex> 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<uint64_t>(server_id_int);
std::lock_guard<std::mutex> 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<tcp::socket*>(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"));
}