feat(libhttp,gateway): add admin-only WebSocket reverse proxy

libhttp v3.1:
- add ws_proxy(path, handshake) API with WsProxyHandshakeHandler callback
- implement full-duplex async bidirectional bridging (text+binary frames)
- support ws:// and wss:// backends with SNI, write queues for ordering
- proxy routes take priority over regular ws() routes
- 403 response on handshake rejection, graceful close propagation

gateway v0.6.0:
- add [WsProxy] config section (routes = /path=ws://backend;...)
- handshake callback verifies Token (Authorization/Cookie) is_admin=true
- non-admin / expired / missing token → 403, admin → transparent bridge
- fix LocalTime.hpp C++20 tzdb incompatibility on Apple clang (use strftime/get_time/mktime)
- update example.ini and README docs for both modules
This commit is contained in:
qing committed 2026-08-01 05:20:14 +08:00
1 parent 6ed88feaa4
commit 12ece9020c
7 files changed
+662 -22

No files matched your search

+19 -16
View File
@@ -1,20 +1,23 @@
#pragma once
#include <chrono>
#include <string>
#include <format>
#include <optional>
#include <sstream>
#include <iomanip>
#include <ctime>
namespace qing {
class LocalTime {
public:
// 获取当前本地时间字符串
static std::string getCurrent(const std::string& format = "%Y-%m-%d %H:%M:%S") {
using namespace std::chrono;
auto now = system_clock::now();
auto zone = current_zone();
zoned_time zt{zone, now};
return std::vformat("{:" + format + "}", std::make_format_args(zt));
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm tm_buf{};
localtime_r(&t, &tm_buf);
char buf[128];
std::strftime(buf, sizeof(buf), format.c_str(), &tm_buf);
return std::string(buf);
}
// 解析时间字符串为 system_clock::time_point(本地时间 → UTC)
@@ -22,29 +25,29 @@ public:
const std::string& str,
const std::string& format = "%Y-%m-%d %H:%M:%S"
) {
using namespace std::chrono;
std::tm tm_buf{};
std::istringstream iss{str};
local_time<seconds> lt;
iss >> std::chrono::parse(format, lt); // ✅ 修复
iss >> std::get_time(&tm_buf, format.c_str());
if (iss.fail()) {
return std::nullopt;
}
auto zone = current_zone();
zoned_time zt{zone, lt};
return zt.get_sys_time();
// mktime 将本地时间转换为 time_t(UTC)
std::time_t t = std::mktime(&tm_buf);
if (t == -1) {
return std::nullopt;
}
return std::chrono::system_clock::from_time_t(t);
}
// ----- 过期判断 -----
static bool isExpired(const std::chrono::system_clock::time_point& tp) {
using namespace std::chrono;
return system_clock::now() > tp;
return std::chrono::system_clock::now() > tp;
}
template <typename Rep, typename Period>
static bool isExpired(const std::chrono::system_clock::time_point& tp,
const std::chrono::duration<Rep, Period>& gracePeriod) {
using namespace std::chrono;
return system_clock::now() > tp + gracePeriod;
return std::chrono::system_clock::now() > tp + gracePeriod;
}
static bool isExpired(const std::string& timeStr,