This commit is contained in:
UnknownObject
2026-06-30 17:49:51 +08:00
commit 292c09619e
200 changed files with 23718 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
#include <set>
#include <string>
#include "Export.h"
class UNSWSC_DLL_EXPORT CORSConfig
{
private:
int max_age;
bool allow_cookie;
std::set<std::string> validate_urls;
std::set<std::string> validate_hosts;
std::set<std::string> validate_headers;
std::set<std::string> validate_methods;
public:
CORSConfig();
public:
int GetMaxAge() const;
bool AllowCookie() const;
std::string GetValidateMethods() const;
bool UrlValidate(const std::string& url) const;
bool HostValidate(const std::string& host) const;
bool IsMethodAllowed(const std::string method) const;
std::string GetValidateHeaders(const std::set<std::string>& headers = {}) const;
public:
void SetMaxAge(int max_age);
void SetAllowCookie(bool allow);
void ClearValidateUrls();
void ClearValidateHosts();
void ClearValidateMethods();
void ClearValidateHeaders();
void AddValidateUrl(const std::string& url);
void AddValidateHost(const std::string& host);
void AddValidateMethod(const std::string& method);
void AddValidateHeader(const std::string& header);
void AddValidateUrls(const std::set<std::string>& urls);
void AddValidateHosts(const std::set<std::string>& hosts);
void AddValidateMethods(const std::set<std::string>& methods);
void AddValidateHeaders(const std::set<std::string>& headers);
};
extern UNSWSC_DLL_EXPORT CORSConfig GlobalCORSConfig;
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <map>
#include <string>
#include "Export.h"
class UNSWSC_DLL_EXPORT DataTransfer
{
private:
std::string temp_root;
std::map<std::string, bool> files;
public:
DataTransfer(const std::string& tr = "");
public:
void Init(const std::string& tr);
bool ItemExist(const std::string& file);
bool InsertItem(const std::string& file);
bool RemoveItem(const std::string& file);
bool ItemValidate(const std::string& file);
void DeactivateItem(const std::string& file);
bool CopyItemTo(const std::string& file, const std::string& dest_path);
bool CopyItemAS(const std::string& file, const std::string& dest);
public:
bool RemoveAllCacheFiles();
std::string MakePath(const std::string& file);
};
extern UNSWSC_DLL_EXPORT DataTransfer GlobalDataTransfer;
#define GDT_INIT(__tr__) GlobalDataTransfer.Init(__tr__)
#define GDT_ADDITEM(__file__) GlobalDataTransfer.InsertItem(__file__)
#define GDT_DELETEITEM(__file__) GlobalDataTransfer.RemoveItem(__file__)
#define GDT_ITEMEXIST(__file__) GlobalDataTransfer.ItemExist(__file__)
#define GDT_ITEMVALIDATE(__file__) GlobalDataTransfer.ItemValidate(__file__)
#define GDT_DEACTIVEITEM(__file__) GlobalDataTransfer.DeactivateItem(__file__)
#define GDT_GETFULLPATH(__file__ ) GlobalDataTransfer.MakePath(__file__)
#define GDT_CLEAR_ALL_CACHE() GlobalDataTransfer.RemoveAllCacheFiles()
#define GDT_COPY_TO(__file__, __dest_path__) GlobalDataTransfer.CopyItemTo(__file__, __dest_path__)
#define GDT_COPY_AS(__file__, __dest_path__) GlobalDataTransfer.CopyItemAS(__file__, __dest_path__)
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include <time.h>
#include <string>
#include "Export.h"
class UNSWSC_DLL_EXPORT DateTime
{
public:
struct UNSWSC_DLL_EXPORT Full
{
int year, month, day, hour, minute, second;
};
struct UNSWSC_DLL_EXPORT Span
{
int days, hours, minutes, seconds;
};
private:
time_t storage;
Full sep_time;
private:
// 移除了硬编码的秒数常量,内部改用 std::chrono 处理
void UpdateDT();
time_t FormatConvert(int year, int month, int day, int hour, int minute, int second);
time_t SpanedSeconds(Span sp);
Span ToSpan(time_t tim);
public:
DateTime();
DateTime(time_t t);
DateTime(const tm& stm);
DateTime(const Full& ftm);
DateTime(const DateTime& obj);
DateTime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0);
public:
static DateTime Now();
public:
int GetYear() const;
int GetMonth() const;
int GetDay() const;
int GetHour() const;
int GetMinute() const;
int GetSecond() const;
time_t GetTimeStamp() const;
Full GetFullDateTime() const;
std::string Format(std::string fmt_str);
public:
bool IsAM();
bool IsPM();
bool TimePassed();
public:
operator tm();
operator Full();
operator time_t();
operator std::string();
Span operator-(const tm& stm);
Span operator-(const Span& ts);
Span operator-(const Full& sdt);
Span operator-(const time_t& t);
Span operator-(const DateTime& dt);
Span operator+(const tm& stm);
Span operator+(const Span& ts);
Span operator+(const Full& sdt);
Span operator+(const time_t& t);
Span operator+(const DateTime& dt);
DateTime operator=(const tm& stm);
DateTime operator=(const Full& sdt);
DateTime operator=(const time_t& t);
DateTime operator=(const DateTime& dt);
DateTime operator+=(const tm& stm);
DateTime operator+=(const Span& ts);
DateTime operator+=(const Full& sdt);
DateTime operator+=(const time_t& t);
DateTime operator+=(const DateTime& dt);
DateTime operator-=(const tm& stm);
DateTime operator-=(const Span& ts);
DateTime operator-=(const Full& sdt);
DateTime operator-=(const time_t& t);
DateTime operator-=(const DateTime& dt);
bool operator==(const tm& stm);
bool operator==(const Full& sdt);
bool operator==(const time_t& t);
bool operator==(const DateTime& dt);
bool operator!=(const tm& stm);
bool operator!=(const Full& sdt);
bool operator!=(const time_t& t);
bool operator!=(const DateTime& dt);
bool operator>(const tm& stm);
bool operator>(const Full& sdt);
bool operator>(const time_t& t);
bool operator>(const DateTime& dt);
bool operator>=(const tm& stm);
bool operator>=(const Full& sdt);
bool operator>=(const time_t& t);
bool operator>=(const DateTime& dt);
bool operator<(const tm& stm);
bool operator<(const Full& sdt);
bool operator<(const time_t& t);
bool operator<(const DateTime& dt);
bool operator<(const DateTime& dt) const;
bool operator<=(const tm& stm);
bool operator<=(const Full& sdt);
bool operator<=(const time_t& t);
bool operator<=(const DateTime& dt);
};
+15
View File
@@ -0,0 +1,15 @@
#ifdef _WINDOWS
#ifdef UNSWEBSERVERCORE_EXPORTS
#define UNSWSC_DLL_EXPORT __declspec(dllexport)
#else
#define UNSWSC_DLL_EXPORT __declspec(dllimport)
#ifdef _DEBUG
#pragma comment(lib, "UNSWebServerCored.lib")
#else
#pragma comment(lib, "UNSWebServerCore.lib")
#endif
#endif
#pragma warning(disable: 4251)
#else
#define UNSWSC_DLL_EXPORT __attribute__((visibility("default")))
#endif
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "Global.h"
#include "IPTable.h"
#include <functional>
#include "WebFileInfo.h"
#include "HTTPObjects.h"
using FileProcessorCallback = std::function<void(WebFileInfoVec, const std::string&)>;
class UNSWSC_DLL_EXPORT FileReceiver
{
protected:
class Impl;
std::unique_ptr<Impl> pimpl;
public:
FileReceiver();
virtual ~FileReceiver(); // 基类必须有虚析构函数!
public:
bool CallFileProcesser();
void SetResponseMode(bool html);
void SetCORSEnable(bool enable);
std::string EncodeUploadResult();
std::string EncodeUploadResultHTML();
void UpdateBlockedIPs(IPTablePtr ip);
void SetTempRoot(std::string temp_root);
void SetFileCallback(FileProcessorCallback fpcb);
uns::HTTPMethod GetMethod(uns::RequestPtr request);
void AppenedBlockedIP(DateTime::Span block_time, std::string ip);
bool WriteFile(const std::string& path, const std::string& bytes);
public:
virtual uns::Status PreCheckRequest(uns::RequestPtr request) = 0;
virtual bool PreCheckForm(uns::FormPartPtr form) = 0;
virtual uns::PathTraversalDefenceLevel PTDefence();
virtual bool IsPathSafe(const std::string& raw_path);
public:
// 核心驱动入口:供内部适配器调用的实际执行流
uns::ResponsePtr Execute(uns::RequestPtr request);
};
using FileReceiverPtr = std::shared_ptr<FileReceiver>;
+155
View File
@@ -0,0 +1,155 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include "Export.h"
#include "DateTime.h"
#pragma warning(disable : 4455)
#define G_SERVER_VERSION "2.0.0"
constexpr double G_SERVICE_UPDATE_TIMESPAN = 1000;
#define G_SERVER_NAME "U.N.S. Server Core/" G_SERVER_VERSION
constexpr auto G_SERVICE_NAME = "UNS_HTTP_ServerCore";
constexpr auto G_ERROR_PAGE = R"(
<html>
<head>
<meta charset="utf-8"/>
<title>HTTP {:03d}</title>
</head>
<body>
<center>
<h1>HTTP ERROR {:03d}</h1>
<hr/>
<p>{}</p>
</center>
</body>
</html>
)";
// inline constexpr std::string_view G_HTTP_STD_MONTH[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
// inline constexpr std::string_view G_HTTP_STD_WEEK[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
namespace uns
{
enum HTTPMethod
{
H_GET = 0b0000000001,
H_PUT = 0b0000000010,
H_POST = 0b0000000100,
H_HEAD = 0b0000001000,
H_TRACE = 0b0000010000,
H_PATCH = 0b0000100000,
H_DELETE = 0b0001000000,
H_OPTIONS = 0b0010000000,
H_CONNECT = 0b0100000000,
H_UNKNOWN = 0b1000000000,
H_ALL_ENABLED = 0b1111111111
};
namespace cors
{
inline constexpr std::string_view reqh_o = "Origin";
inline constexpr std::string_view reqh_acrm = "Access-Control-Request-Method";
inline constexpr std::string_view reqh_acrh = "Access-Control-Request-Headers";
inline constexpr std::string_view resh_acao = "Access-Control-Allow-Origin";
inline constexpr std::string_view resh_acam = "Access-Control-Allow-Methods";
inline constexpr std::string_view resh_acah = "Access-Control-Allow-Headers";
inline constexpr std::string_view resh_acac = "Access-Control-Allow-Credentials";
inline constexpr std::string_view resh_acma = "Access-Control-Max-Age";
};
using POSTArgs = std::map<std::string, std::string>;
std::string UNSWSC_DLL_EXPORT EncodeErrorPage(int code);
std::string UNSWSC_DLL_EXPORT EncodeHTTPTime(time_t* time);
std::string UNSWSC_DLL_EXPORT btos(bool val);
bool UNSWSC_DLL_EXPORT stob(const std::string& obj);
DateTime UNSWSC_DLL_EXPORT stot(const std::string& obj);
std::string UNSWSC_DLL_EXPORT RestoreURL(const std::string& url);
void UNSWSC_DLL_EXPORT Stringsplit(const std::string& str, const std::string& splits, std::vector<std::string>& res);
void UNSWSC_DLL_EXPORT ProcessPOSTArgs(const std::string& args, POSTArgs& args_output);
namespace tools
{
//基于SHA3-256,无暴力破解防护能力,不建议使用
std::string UNSWSC_DLL_EXPORT EncryptPassword(const std::string reg_date, const std::string& password);
//安全的密码加密函数,基于libsodium
std::string UNSWSC_DLL_EXPORT EncryptPasswordSodium(const std::string& pwd);
//密码检查函数
bool UNSWSC_DLL_EXPORT CheckPasswordSodium(const std::string& pwd, const std::string& encrypted);
//安全随机数
std::string UNSWSC_DLL_EXPORT SecureRandomHex(size_t bytes);
std::string UNSWSC_DLL_EXPORT ToUpper(const std::string& s);
std::string UNSWSC_DLL_EXPORT ToLower(const std::string& s);
std::string UNSWSC_DLL_EXPORT CalculateFileHashSHA256(const std::string& file);
}
namespace secure
{
/**
* @brief 基于物理与逻辑边界的路径穿透(目录穿越)安全校验
* @param safe_path 允许访问的沙盒根目录(绝对或相对路径均可)
* @param requested_path 客户端传入的、解码后的目标子路径
* @return true 安全(在沙盒内);false 不安全(企图穿越或路径非法)
*/
bool IsSafePath(const std::string& safe_path, const std::string& requested_path);
}
};
#undef MIN
inline constexpr unsigned long long operator""B(unsigned long long n)
{
return n;
}
inline constexpr unsigned long long operator""KB(unsigned long long n)
{
return (n * 1024);
}
inline constexpr unsigned long long operator""MB(unsigned long long n)
{
return (n * 1024 * 1024);
}
inline constexpr unsigned long long operator""GB(unsigned long long n)
{
return (n * 1024 * 1024 * 1024);
}
inline constexpr unsigned long long operator""TB(unsigned long long n)
{
return (n * 1024 * 1024 * 1024 * 1024);
}
inline constexpr time_t operator""S(unsigned long long t)
{
return t;
}
inline constexpr time_t operator""MIN(unsigned long long t)
{
return (t * 60);
}
inline constexpr time_t operator""HOUR(unsigned long long t)
{
return (t * 60 * 60);
}
inline constexpr time_t operator""DAY(unsigned long long t)
{
return (t * 60 * 60 * 24);
}
+336
View File
@@ -0,0 +1,336 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <utility>
#include "Export.h"
#include <string_view>
class FileReceiver;
namespace uns
{
// HTTP status codes.
// Don't use "enum class" for converting to/from int easily.
// The full list is available here:
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
enum Status
{
// 此临时响应表明客户端应继续请求,或者如果请求已完成,则忽略此响应。
kContinue = 100,
// 此代码是在响应客户端的 Upgrade 请求标头时发送的,用于指示服务器即将切换到的协议。
kSwitchingProtocols = 101,
// 此代码曾在 WebDAV 上下文中使用,表示服务器已收到请求,但在响应时无法提供状态。
kProcessing = 102,
// 此状态码主要与 Link 标头一起使用,允许用户代理在服务器准备响应时开始预加载资源,或预连接到页面需要资源的源站。
kEarlyHints = 103,
// 请求成功。
kOK = 200,
// 请求成功,并因此创建了一个新资源。
kCreated = 201,
// 请求已被接收但尚未处理。
kAccepted = 202,
// 此响应代码表示返回的元数据与原始服务器上可用的不完全相同,而是从本地或第三方副本收集的。这主要用于另一个资源的镜像或备份。
kNonAuthoritativeInformation = 203,
// 对于此请求,没有内容可发送,但响应头可能有用。
kNoContent = 204,
// 告知用户代理重置发送此请求的文档。
kResetContent = 205,
// 当客户端请求了资源的一部分时,使用此响应代码进行响应。
kPartialContent = 206,
// 在可能需要多个状态码的情况下,传递关于多个资源的信息。
kMultiStatus = 207,
// 在 <dav:propstat> 响应元素内部使用,以避免重复枚举同一集合的多个绑定的内部成员。
kAlreadyReported = 208,
// 服务器已完成了对资源的 GET 请求,并且响应是对当前实例应用了一个或多个实例操作后的结果表示。
kIMUsed = 226,
// 在代理驱动(agent-driven)的内容协商中,请求有多个可能的响应,用户代理或用户应选择其中之一。
kMultipleChoices = 300,
// 请求资源的 URL 已永久更改。新 URL 在响应中给出。
kMovedPermanently = 301,
// 此响应代码意味着请求资源的 URI 已暂时更改。未来可能还会对 URI 进行进一步更改,因此客户端在未来的请求中应使用相同的 URI。
kFound = 302,
// 服务器发送此响应以指示客户端使用 GET 请求在另一个 URI 获取请求的资源。
kSeeOther = 303,
// 用于缓存目的。它告知客户端响应未被修改,因此客户端可以继续使用相同的缓存响应版本。
kNotModified = 304,
// 在 HTTP 规范的前一版本中定义,表示请求的响应必须通过代理访问。由于涉及代理带内配置的安全问题,此状态码已被弃用。
kUseProxy = 305,
// 此响应代码不再使用,但被保留。它曾在 HTTP/1.1 规范的先前版本中使用。
k__Unused = 306,
// 服务器发送此响应以指示客户端使用与先前请求相同的方法在另一个 URI 获取请求的资源。其语义与 302 Found 响应代码相同,但用户代理不得更改使用的 HTTP 方法:如果在第一个请求中使用了 POST,则在重定向请求中也必须使用 POST。
kTemporaryRedirect = 307,
// 表示资源现在永久位于另一个 URI,由 Location 响应头指定。其语义与 301 Moved Permanently HTTP 响应代码相同,但用户代理不得更改使用的 HTTP 方法:如果在第一个请求中使用了 POST,则在第二个请求中也必须使用 POST。
kPermanentRedirect = 308,
// 由于被认为是客户端错误的原因(例如,格式错误的请求语法、无效的请求消息结构或欺骗性的请求路由),服务器无法或不会处理该请求。
kBadRequest = 400,
// 尽管 HTTP 标准指定为 "unauthorized",但从语义上讲,此响应的意思是 "unauthenticated"。即,客户端必须进行身份验证才能获得请求的响应。
kUnauthorized = 401,
// 此代码最初用于数字支付系统,但此状态码很少使用,且不存在标准约定。
kPaymentRequired = 402,
// 客户端没有访问内容的权利;也就是说,它是未授权的,因此服务器拒绝提供请求的资源。与 401 Unauthorized 不同,服务器知道客户端的身份。
kForbidden = 403,
// 服务器找不到请求的资源。
kNotFound = 404,
// 服务器知道请求方法,但目标资源不支持该方法。
kMethodNotAllowed = 405,
// 当 Web 服务器执行服务器驱动的内容协商后,找不到任何符合用户代理给定条件的内容时,会发送此响应。
kNotAcceptable = 406,
// 类似于 401 Unauthorized,但需要通过代理进行身份验证。
kProxyAuthenticationRequired = 407,
// 某些服务器会在空闲连接上发送此响应,即使客户端之前没有任何请求。这意味着服务器希望关闭此未使用的连接。
kRequestTimeout = 408,
// 当请求与服务器的当前状态冲突时,发送此响应。
kConflict = 409,
// 当请求的内容已从服务器永久删除,且没有转发地址时,发送此响应。
kGone = 410,
// 服务器拒绝了请求,因为未定义 Content-Length 标头字段,而服务器需要它。
kLengthRequired = 411,
// 在条件请求中,客户端在其标头中指明了服务器不满足的前提条件。
kPreconditionFailed = 412,
// 请求体大于服务器定义的限制。
kContentTooLarge = 413,
// 客户端请求的 URI 长度超过了服务器愿意解释的长度。
kURITooLong = 414,
// 服务器不支持请求数据的媒体格式,因此服务器拒绝该请求。
kUnsupportedMediaType = 415,
// 无法满足请求中 Range 标头字段指定的范围。可能范围超出了目标资源数据的大小。
kRangeNotSatisfiable = 416,
// 此响应代码表示服务器无法满足 Expect 请求标头字段指示的期望。
kExpectationFailed = 417,
// 服务器拒绝尝试用茶壶煮咖啡。
kIamATeapot = 418,
// 请求被发送到了一个无法产生响应的服务器。
kMisdirectedRequest = 421,
// 请求格式正确,但由于语义错误而无法被遵循。
kUnprocessableContent = 422,
// 正在访问的资源已被锁定。
kLocked = 423,
// 由于先前的请求失败,导致当前请求失败。
kFailedDependency = 424,
// 表示服务器不愿意冒险处理一个可能被重放的请求。
kTooEarly = 425,
// 服务器拒绝使用当前协议执行请求,但可能在客户端升级到其他协议后愿意执行。服务器在 426 响应中发送 Upgrade 标头以指示所需的协议。
kUpgradeRequired = 426,
// 原始服务器要求请求是有条件的。此响应旨在防止"丢失更新"问题,即客户端 GET 资源状态,修改后 PUT 回服务器,而同时第三方已修改了服务器上的状态,导致冲突。
kPreconditionRequired = 428,
// 用户在给定的时间内发送了太多请求(速率限制)。
kTooManyRequests = 429,
// 服务器因请求头字段太大而不愿意处理该请求。
kRequestHeaderFieldsTooLarge = 431,
// 用户代理请求了一个无法合法提供的资源,例如被政府审查的网页。
kUnavailableForLegalReasons = 451,
// 服务器遇到了不知道如何处理的情况。此错误是通用性的,表示服务器找不到更合适的 5XX 状态码来响应。
kInternalServerError = 500,
// 服务器不支持请求方法,无法处理。
kNotImplemented = 501,
// 此错误响应意味着服务器作为网关或代理时,收到了一个无效的响应。
kBadGateway = 502,
// 服务器尚未准备好处理请求。
kServiceUnavailable = 503,
// 当服务器作为网关或代理,无法及时获得响应时,会给出此错误响应。
kGatewayTimeout = 504,
// 服务器不支持请求中使用的 HTTP 版本。
kHTTPVersionNotSupported = 505,
// 服务器存在内部配置错误:在内容协商过程中,被选中的变体被配置为自身参与内容协商,这导致在创建响应时出现循环引用。
kVariantAlsoNegotiates = 506,
// 由于服务器无法存储成功完成请求所需的表示,因此无法对资源执行该方法。
kInsufficientStorage = 507,
// 服务器在处理请求时检测到无限循环。
kLoopDetected = 508,
// 客户端请求声明了一个应使用 HTTP 扩展(RFC 2774)来处理请求,但该扩展不受支持。
kNotExtended = 510,
// 表示客户端需要进行身份验证才能获得网络访问权限。
kNetworkAuthenticationRequired = 511,
//Not Standard Code By UnknownObject
// 请求载体的格式错误,如:无法解析的JSON等。
k_uRequestFormatError = 489,
// 请求无效。可能是由于未正确携带数据等必要信息。
k_uRequestInvalid = 490,
// 请求URL超范围。此响应表示请求的URL是错误的。
k_uURLOutOfRange = 492,
// 无效的请求主机。指示请求时使用了错误的域名/IP。
k_uInvalidRequestHost = 493,
// IP地址被封禁。
k_uIPBlocked = 494,
// 非法上传请求。指示本次上传请求不符合服务器规定。
k_uIllegalUpload = 495,
// 文件格式错误。指示上传的文件格式不符合服务器规定。
k_uFileFormatError = 496,
// 无效文件。处理请求所需的文件已过期/无法访问。
k_uInvalidFile = 497,
// 上传的文件过大。非文件上传时应使用 413 Content Too Large。
k_uFileTooLarge = 498,
// 每秒请求数过多。仅在一些特殊API中使用,常规情况需使用 429 Too Many Requests。
k_uRPSLimited = 499,
// 子过程失败。服务器在处理请求的某个步骤中遇到无法恢复的错误。
k_uSubProcessFalied = 533,
// 服务器检测到漏洞利用/可执行文件上传等网络攻击行为。
k_uServerHateYou = 540,
// 检测到拒绝服务漏洞攻击。
k_uDoSFound = 550,
// 检测到分布式拒绝服务漏洞攻击。
k_uDDoSFound = 551,
// 未知的服务器错误。当服务器无法定位错误来源时返回。否则应使用 500 Internal Server Error。
k_uUnknownServerError = 560
};
// 路径穿透防御等级
enum class PathTraversalDefenceLevel
{
DenyAll, //拒绝所有带有对应特征的路径
AutoNormalize, //允许普通路径穿透,自动归一化,并要求进行安全路径检查
AllowNormal, //允许普通路径穿透,原样返回,要求进行安全路径检查
AllowAll [[deprecated("警告: AllowAll 已启用 - 路径穿透防御关闭 - 仅供测试环境使用.")]] //允许所有行为(仅供测试)
};
class UNSWSC_DLL_EXPORT UrlQuery
{
public:
UrlQuery();
~UrlQuery();
// 显式支持深拷贝与移动,内部完美同步 webcc 状态
UrlQuery(const UrlQuery& other);
UrlQuery& operator=(const UrlQuery& other);
UrlQuery(UrlQuery&& other) noexcept;
UrlQuery& operator=(UrlQuery&& other) noexcept;
public:
bool Empty() const;
std::size_t Size() const;
bool Has(const std::string& key) const;
const std::string& Get(const std::string& key) const;
std::pair<std::string, std::string> Get(std::size_t index) const;
std::string ToString(bool encode = true) const;
private:
friend class Request;
class Impl;
std::unique_ptr<Impl> pimpl;
explicit UrlQuery(std::unique_ptr<Impl> impl);
};
class UNSWSC_DLL_EXPORT FormPart
{
private:
class Impl;
private:
friend class Request;
friend class ::FileReceiver;
std::unique_ptr<Impl> pimpl;
private:
explicit FormPart(std::unique_ptr<Impl> impl);
public:
~FormPart();
public:
std::string GetNameS() const;
std::string_view GetName() const;
std::string GetFileNameS() const;
std::string_view GetFileName() const;
std::string GetMediaTypeS() const;
std::string_view GetMediaType() const;
const std::string& GetData() const;
std::size_t GetSize() const;
std::size_t GetDataSize() const;
};
using FormPartPtr = std::shared_ptr<FormPart>;
class UNSWSC_DLL_EXPORT Request
{
private:
class Impl;
friend class ServerCore;
friend class FileReceiverAdapter;
friend class ServerProcessorAdapter;
friend class SyncFileReceiverAdapter;
std::unique_ptr<Impl> pimpl;
private:
explicit Request(std::unique_ptr<Impl> impl);
public:
~Request();
Impl* GetImpl() const
{
return pimpl.get();
}
public:
// 基础请求信息
std::string GetMethodS() const;
std::string_view GetMethod() const;
std::string GetAddressS() const;
std::string_view GetAddress() const;
size_t GetContentLength() const;
std::string GetDataS() const;
std::string_view GetData() const;
// 扁平化后的 Url 核心数据项
std::string GetSchemeS() const;
std::string_view GetScheme() const;
std::string GetHostS() const;
std::string_view GetHost() const;
int GetPortI() const;
std::string GetPortS() const;
std::string_view GetPort() const;
std::string GetPathS() const;
std::string_view GetPath() const;
std::string GetQueryStringS() const;
std::string_view GetQueryString() const;
// 复杂复合对象获取
UrlQuery GetQuery() const;
const std::vector<std::string>& GetArgs() const;
// 多部分表单(文件上传)支撑
bool IsForm() const;
const std::vector<FormPartPtr>& GetFormParts() const;
// 请求头获取
bool HasHeader(std::string_view header) const;
std::string GetHeader(std::string_view header, bool* existed = nullptr) const;
std::vector<std::pair<std::string, std::string>> GetAllHeaders() const;
};
class UNSWSC_DLL_EXPORT Response
{
private:
class Impl;
private:
friend class ResponseBuilder;
std::unique_ptr<Impl> pimpl;
private:
explicit Response(std::unique_ptr<Impl> impl);
public:
~Response();
Impl* GetImpl() const
{
return pimpl.get();
}
};
using RequestPtr = std::shared_ptr<Request>;
using ResponsePtr = std::shared_ptr<Response>;
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <set>
#include <string>
#include "Export.h"
#include <initializer_list>
class UNSWSC_DLL_EXPORT IPList
{
private:
std::set<std::string> list;
private:
bool CheckIPv4(std::string ip);
public:
IPList();
IPList(std::initializer_list<std::string> list);
IPList(const IPList& obj);
public:
bool Push(std::string ip);
bool Pop(std::string ip);
bool Exist(std::string ip);
bool Exist(std::string ip) const;
bool Empty();
size_t Size();
public:
auto begin();
auto end();
auto rbegin();
auto rend();
auto begin() const;
auto end() const;
auto rbegin() const;
auto rend() const;
public:
bool operator<(const IPList& obj) const;
};
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <set>
#include "IPList.h"
#include "DateTime.h"
class UNSWSC_DLL_EXPORT IPTable
{
private:
std::set<std::pair<DateTime, IPList>> storage;
public:
IPTable();
IPTable(const IPTable& obj);
public:
void Appened(DateTime expr_time, IPList list);
void Update();
bool IPExist(std::string ip);
};
using IPTablePtr = IPTable*;
//using IPTablePtr = std::shared_ptr<IPTable>;
+254
View File
@@ -0,0 +1,254 @@
#pragma once
#include <ctime>
#include <string>
#include <vector>
#include <chrono>
#include <variant>
#include "Export.h"
#include <functional>
#include <string_view>
#include <type_traits>
namespace uns
{
// 前置声明捕获器
struct RangeCapturer;
struct PairCapturer;
struct FuncCapturer;
struct TimeCapturer;
// 终极 Variant 池:容纳所有可能解包出的原子形态
using LogVariant = std::variant<
bool, char, int, unsigned int, long long, unsigned long long, double, std::string_view, const void*,
std::string, // 专门用于承载宽字符转换后的 UTF-8 临时字符串
RangeCapturer, // 专门用于承载 std::vector / std::list 等容器
PairCapturer, // 专门用于承载 std::pair (支持 std::map)
FuncCapturer, //承载 std::function
TimeCapturer //承载时间
>;
// --- 编译期类型推导工具链 ---
template <typename T, typename = void>
struct is_container : std::false_type
{
};
// 识别标准容器(排除字符串本身)
template <typename T>
struct is_container<T, std::void_t<typename T::value_type, decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> : std::integral_constant<bool, !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>>
{
};
template <typename T> struct is_pair : std::false_type
{
};
template <typename F, typename S> struct is_pair<std::pair<F, S>> : std::true_type
{
};
// --- 延迟桥接结构体 ---
struct LogArg;
struct RangeCapturer
{
const void* ptr;
void (*to_log_args)(const void*, std::vector<LogArg>&);
};
struct PairCapturer
{
const void* ptr;
void (*to_log_args)(const void*, std::vector<LogArg>&);
};
struct FuncCapturer
{
const void* code_ptr; // 真正改变:这里存真正的函数代码地址
bool is_closure; // 标记:这究竟是个纯函数,还是个 Lambda 闭包
};
// 升级版 Trait:不仅识别,还提取原生函数指针类型(R(*)(Args...))
template <typename T> struct function_traits
{
static constexpr bool is_func = false;
using pointer_type = void*;
};
template <typename R, typename... Args>
struct function_traits<std::function<R(Args...)>>
{
static constexpr bool is_func = true;
using pointer_type = R(*)(Args...); // 提取出原生函数指针类型
};
enum class TimeMode
{
Point,
TMPoint,
Duration
};
struct TimeCapturer
{
TimeMode mode{ TimeMode::Point };
std::tm _tm{};
std::chrono::system_clock::time_point tp{};
std::chrono::nanoseconds duration{ 0 };
std::string format_spec; // "%Y-%m-%d %H:%M:%S" or empty
// 可选:单位策略控制
bool smart_unit = true;
};
template<typename T>
struct is_time_point : std::false_type
{
};
template<typename Clock, typename Dur>
struct is_time_point<std::chrono::time_point<Clock, Dur>> : std::true_type
{
};
template<>
struct is_time_point<std::tm> : std::true_type
{
};
template<typename T>
inline constexpr bool is_time_point_v = is_time_point<T>::value;
template<typename T>
struct is_duration : std::false_type
{
};
template<typename Rep, typename Period>
struct is_duration<std::chrono::duration<Rep, Period>> : std::true_type
{
};
template<typename T>
inline constexpr bool is_duration_v = is_duration<T>::value;
inline TimeCapturer MakeTimeCapturer(std::chrono::system_clock::time_point tp)
{
TimeCapturer c;
c.mode = TimeMode::Point;
c.tp = tp;
return c;
}
template<typename Rep, typename Period>
inline TimeCapturer MakeTimeCapturer(std::chrono::duration<Rep, Period> d)
{
TimeCapturer c;
c.mode = TimeMode::Duration;
c.duration = std::chrono::duration_cast<std::chrono::nanoseconds>(d);
c.smart_unit = true;
return c;
}
inline TimeCapturer MakeTimeCapturer(const std::tm& tm)
{
TimeCapturer c;
c.mode = TimeMode::TMPoint;
c._tm = tm;
return c;
}
// --- 宽字符转换声明(实现卸载到 .cpp) ---
std::string UNSWSC_DLL_EXPORT ConvertWStringToUtf8(std::wstring_view wstr);
// --- 核心包装类 ---
struct LogArg
{
LogVariant value;
template<typename T>
LogArg(T&& val)
{
using D = std::decay_t<T>;
// 1. 窄字符串系列(0拷贝)
if constexpr (std::is_same_v<D, std::string> || std::is_same_v<D, std::string_view>)
value = std::string_view(val);
else if constexpr (std::is_same_v<D, const char*> || std::is_same_v<D, char*>)
value = val ? std::string_view(val) : std::string_view("<null>");
// 2. 宽字符串系列(特殊处理:触发内部 UTF-8 转换)
else if constexpr (std::is_same_v<D, std::wstring> || std::is_same_v<D, std::wstring_view>)
value = ConvertWStringToUtf8(val);
else if constexpr (std::is_same_v<D, const wchar_t*> || std::is_same_v<D, wchar_t*>)
value = val ? ConvertWStringToUtf8(val) : std::string("<null>");
// 3. 基础原子类型(防范 wchar_t 被误判为整数)
else if constexpr (std::is_same_v<D, wchar_t> || std::is_same_v<D, char16_t> || std::is_same_v<D, char32_t>)
value = ConvertWStringToUtf8(std::wstring_view((const wchar_t*)&val, 1));
else if constexpr (std::is_same_v<D, bool>)
value = static_cast<bool>(val);
else if constexpr (std::is_same_v<D, char> || std::is_same_v<D, signed char>)
value = static_cast<char>(val);
// 3.5 时间点类型系列(防止时间对象退化为未知类型)
else if constexpr (is_time_point_v<D>)
value = MakeTimeCapturer(val);
else if constexpr (is_duration_v<D>)
value = MakeTimeCapturer(val);
// 4. 全整型变种矩阵 (short, int, long, long long, unsigned...)
else if constexpr (std::is_integral_v<D> && std::is_signed_v<D>)
{
if constexpr (sizeof(D) <= sizeof(int))
value = static_cast<int>(val);
else
value = static_cast<long long>(val);
}
else if constexpr (std::is_integral_v<D> && std::is_unsigned_v<D>)
{
if constexpr (sizeof(D) <= sizeof(unsigned int))
value = static_cast<unsigned int>(val);
else
value = static_cast<unsigned long long>(val);
}
// 5. 浮点矩阵
else if constexpr (std::is_floating_point_v<D>)
value = static_cast<double>(val);
// 6. 指针
else if constexpr (std::is_pointer_v<D>)
value = static_cast<const void*>(val);
// 7. 标准库容器(关键点:利用 Lambda 闭包在不引入 fmt 的情况下擦除容器类型!)
else if constexpr (is_container<D>::value)
{
value = RangeCapturer{ &val, [] (const void* p, std::vector<LogArg>& out)
{
for (const auto& item : *static_cast<const D*>(p))
out.emplace_back(item); // 递归包装子元素
}
};
}
// 8. 键值对(支持 Map 展开)
else if constexpr (is_pair<D>::value)
{
value = PairCapturer{ &val, [] (const void* p, std::vector<LogArg>& out)
{
const auto& pair = *static_cast<const D*>(p);
out.emplace_back(pair.first);
out.emplace_back(pair.second);
}
};
}
else if constexpr (function_traits<D>::is_func)
{
using TargetPtr = typename function_traits<D>::pointer_type;
// 关键点:尝试用 target() 拿底层指针
// std::function::target<T>() 返回的是 T*,所以如果 T 是函数指针,返回的就是“函数指针的指针”
if (auto* const* func_ptr = val.template target<TargetPtr>()) // 情况 A:内部装的是普通纯函数或静态成员函数
value = FuncCapturer{ reinterpret_cast<const void*>(*func_ptr), false };
else // 情况 B:内部装的是 Lambda 表达式或带状态的仿函数, 此时它在堆/栈上有一个闭包实体,我们退而求其次,打印这个闭包对象的地址
value = FuncCapturer{ static_cast<const void*>(&val), true };
}
else
value = std::string_view("<unsupported type>");
}
};
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Unknown Network Service Web Server Core
* Version 1.2.2
*
* TCP Core - boost::aiso
* HTTP Core - webcc
* Application Level Repack & Windows Service Interface - UnknownObject
*/
#pragma once
#include <thread>
#include <memory>
#include "Global.h"
#include "Export.h"
#include "FileReceiver.h"
#include "ServerProcessor.h"
#include "SyncFileReceiver.h"
class UNSWSC_DLL_EXPORT ServerCore
{
private:
class Impl;
std::unique_ptr<Impl> pimpl;
public:
ServerCore(int port);
~ServerCore();
ServerCore() = delete;
ServerCore(const ServerCore& obj) = delete;
ServerCore& operator=(const ServerCore& obj) = delete;
public:
void Run(int worker_thread = 1, int loop_thread = 1);
void ThreadRun(int worker_thread = 1, int loop_thread = 1);
void Stop();
bool Running();
void UpdateProcessor();
bool EnableCORSSupport();
bool AppenedProcessor(std::string url, ServerProcessorPtr ptr, std::uint32_t methods, bool enable_ip_check = false);
bool AppenedFileReceiver(std::string url, FileReceiverPtr ptr, std::uint32_t methods, FileProcessorCallback fpcb, bool html_response = false);
bool AppenedFileReceiver(std::string url, SyncFileReceiverPtr ptr, std::uint32_t methods, bool html_response = false);
public:
//启用WebCC日志
//path: 日志存放文件夹,留空为仅控制台日志
//level: 0-4, 0=VERB, 1=INFO, 2=USER(default), 3=WARN, 4=ERROR
static void EnableWebCCLog(const std::string& path = "", int level = 2);
};
+210
View File
@@ -0,0 +1,210 @@
#pragma once
#pragma warning(disable : 4996)
#include <deque>
#include <mutex>
#include <thread>
#include <string>
#include <atomic>
#include <cstring>
#include <fstream>
#include "LogArg.h"
#include "Export.h"
#include <condition_variable>
namespace uns
{
enum ServerLogLevel
{
llAll = 0x000000,
llTrace = 0x000001,
llDebug = 0x000010,
llInfo = 0x000020,
llWarning = 0x000030,
llError = 0x000040,
llFatal = 0x000050,
llOff = 0xFFFFFF
};
enum LogRotationPeriod
{
RP_None = 0,
RP_Hourly = 1,
RP_Daily = 2
};
std::string UNSWSC_DLL_EXPORT toBinary(long number, int bits);
std::string UNSWSC_DLL_EXPORT toBinary(std::uint32_t number, int bits);
std::string UNSWSC_DLL_EXPORT toBinary(unsigned long number, int bits);
};
class UNSWSC_DLL_EXPORT ServerLogger
{
private:
std::fstream LogStream;
std::string LogFileName;
std::atomic<uns::ServerLogLevel> CurrentLevel; //避免数据竟态UB
// 异步队列与线程控制
std::deque<std::string> LogQueue;
std::mutex QueueMutex;
std::condition_variable QueueCV;
std::thread WorkerThread;
std::atomic_bool WorkerRunning;
std::atomic_bool ThreadRunningFlag;
// 日志分时段(轮转)支持
uns::LogRotationPeriod RotatePeriod;
std::time_t CurrentFilePeriodStart;
// 新增:大小轮转支持
std::size_t MaxFileSizeBytes; // 最大文件大小(字节)
int CurrentOSLIndex; // 当前 period 下的 OSL 索引(从 0 开始)
private:
std::string GenerateLogHeader(uns::ServerLogLevel LogLevel);
std::string GenerateFileInfo(std::string filename, int line_num);
void WriteBatchToOutputs(const std::deque<std::string>& batch)
;
void WorkerLoop();
std::string MakeRotatedFileName(const std::string& base, std::time_t t, int osl_index = 0);
void RotateIfNeeded(std::time_t now, bool check_size_after_write = false);
void LogImpl(uns::ServerLogLevel level, const std::string& format, const uns::LogArg* args, size_t count);
void LogFImpl(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string& format, const uns::LogArg* args, size_t count);
void LogFMTImpl(uns::ServerLogLevel level, const std::string& format, const uns::LogArg* args, size_t count);
void LogFMT_FImpl(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string& format, const uns::LogArg* args, size_t count);
public:
ServerLogger();
ServerLogger(uns::ServerLogLevel log_level, std::string file = "", uns::LogRotationPeriod rotation = uns::RP_None, std::size_t max_bytes = 50 * 1024 * 1024);
ServerLogger(const ServerLogger& obj) = delete;
~ServerLogger();
public:
bool InitLogger(uns::ServerLogLevel log_level, const std::string& file = "", uns::LogRotationPeriod rotation = uns::RP_None, std::size_t max_bytes = 50 * 1024 * 1024);
void CloseLog();
void DisableLog();
void FlushLogBuffer();
void EnableLog(uns::ServerLogLevel level);
template<typename... Args>
void Log(uns::ServerLogLevel level, const std::string& format, Args&&... args);
template<typename... Args>
void LogF(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string format, Args&&... args);
template<typename... Args>
void LogFMT(uns::ServerLogLevel level, const std::string& format, Args&&... args);
template<typename... Args>
void LogFMT_F(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string format, Args&&... args);
};
template<typename... Args>
inline void ServerLogger::Log(uns::ServerLogLevel level, const std::string& format, Args&&... args)
{
uns::ServerLogLevel curr_level = CurrentLevel.load(std::memory_order_relaxed);
if ((curr_level == uns::llOff) || (level < curr_level))
return;
if constexpr (sizeof...(Args) == 0)
LogImpl(level, format, nullptr, 0);
else
{
// 在客户端的栈上瞬间分配数组并完成类型擦除
uns::LogArg packed_args[] = { uns::LogArg(std::forward<Args>(args))... };
LogImpl(level, format, packed_args, sizeof...(Args));
}
}
template<typename... Args>
inline void ServerLogger::LogF(uns::ServerLogLevel level, const std::string& filename, int line_num, std::string format, Args&&... args)
{
uns::ServerLogLevel curr_level = CurrentLevel.load(std::memory_order_relaxed);
if ((curr_level == uns::llOff) || (level < curr_level))
return;
if constexpr (sizeof...(Args) == 0)
LogFImpl(level, filename, line_num, format, nullptr, 0);
else
{
uns::LogArg packed_args[] = { uns::LogArg(std::forward<Args>(args))... };
LogFImpl(level, filename, line_num, format, packed_args, sizeof...(Args));
}
}
template<typename ...Args>
inline void ServerLogger::LogFMT(uns::ServerLogLevel level, const std::string& format, Args && ...args)
{
uns::ServerLogLevel curr_level = CurrentLevel.load(std::memory_order_relaxed);
if ((curr_level == uns::llOff) || (level < curr_level))
return;
if constexpr (sizeof...(Args) == 0)
LogFMTImpl(level, format, nullptr, 0);
else
{
uns::LogArg packed_args[] = { uns::LogArg(std::forward<Args>(args))... };
LogFMTImpl(level, format, packed_args, sizeof...(Args));
}
}
template<typename ...Args>
inline void ServerLogger::LogFMT_F(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string format, Args && ...args)
{
uns::ServerLogLevel curr_level = CurrentLevel.load(std::memory_order_relaxed);
if ((curr_level == uns::llOff) || (level < curr_level))
return;
if constexpr (sizeof...(Args) == 0)
LogFMT_FImpl(level, filename, line_num, format, nullptr, 0);
else
{
uns::LogArg packed_args[] = { uns::LogArg(std::forward<Args>(args))... };
LogFMT_FImpl(level, filename, line_num, format, packed_args, sizeof...(Args));
}
}
extern UNSWSC_DLL_EXPORT ServerLogger GlobalServerLogger;
#if (defined(_WIN32) || defined(_WIN64))
#define __FILENAME__ std::strrchr("\\" __FILE__, '\\') + 1
#else
#define __FILENAME__ std::strrchr("/" __FILE__, '/') + 1
#endif
#define SCLOG_CONSOLE_INIT(__log_level__) GlobalServerLogger.InitLogger(__log_level__)
#define SCLOG_FILE_INIT(__file_name__, __log_level__, ...) GlobalServerLogger.InitLogger(__log_level__, __file_name__, ##__VA_ARGS__)
#define SCLOG_WRITE(__level__, __text__, ...) GlobalServerLogger.LogF(__level__, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_TRACE(__text__, ...) GlobalServerLogger.LogF(uns::llTrace, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_DEBUG(__text__, ...) GlobalServerLogger.LogF(uns::llDebug, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_INFO(__text__, ...) GlobalServerLogger.LogF(uns::llInfo, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_WARNING(__text__, ...) GlobalServerLogger.LogF(uns::llWarning, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_ERROR(__text__, ...) GlobalServerLogger.LogF(uns::llError, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_FATAL(__text__, ...) GlobalServerLogger.LogF(uns::llFatal, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_WRITE(__level__, __text__, ...) GlobalServerLogger.Log(__level__, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_TRACE(__text__, ...) GlobalServerLogger.Log(uns::llTrace, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_DEBUG(__text__, ...) GlobalServerLogger.Log(uns::llDebug, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_INFO(__text__, ...) GlobalServerLogger.Log(uns::llInfo, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_WARNING(__text__, ...) GlobalServerLogger.Log(uns::llWarning, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_ERROR(__text__, ...) GlobalServerLogger.Log(uns::llError, __text__, ##__VA_ARGS__)
#define SCLOG_SHORT_FATAL(__text__, ...) GlobalServerLogger.Log(uns::llFatal, __text__, ##__VA_ARGS__)
#define SCLOGF_WRITE(__level__, __text__, ...) GlobalServerLogger.LogFMT_F(__level__, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_TRACE(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llTrace, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_DEBUG(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llDebug, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_INFO(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llInfo, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_WARNING(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llWarning, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_ERROR(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llError, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_FATAL(__text__, ...) GlobalServerLogger.LogFMT_F(uns::llFatal, __FILENAME__, __LINE__, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_WRITE(__level__, __text__, ...) GlobalServerLogger.LogFMT(__level__, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_TRACE(__text__, ...) GlobalServerLogger.LogFMT(uns::llTrace, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_DEBUG(__text__, ...) GlobalServerLogger.LogFMT(uns::llDebug, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_INFO(__text__, ...) GlobalServerLogger.LogFMT(uns::llInfo, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_WARNING(__text__, ...) GlobalServerLogger.LogFMT(uns::llWarning, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_ERROR(__text__, ...) GlobalServerLogger.LogFMT(uns::llError, __text__, ##__VA_ARGS__)
#define SCLOGF_SHORT_FATAL(__text__, ...) GlobalServerLogger.LogFMT(uns::llFatal, __text__, ##__VA_ARGS__)
#define SCLOG_ENABLE(__level__) GlobalServerLogger.EnableLog(__level__)
#define SCLOG_DISABLE() GlobalServerLogger.DisableLog()
#define SCLOG_CLOSE() GlobalServerLogger.CloseLog()
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "Global.h"
#include "Export.h"
#include "IPTable.h"
#include "HTTPObjects.h"
class UNSWSC_DLL_EXPORT ServerProcessor
{
protected:
class Impl;
std::unique_ptr<Impl> pimpl;
public:
ServerProcessor();
virtual ~ServerProcessor();
public:
void EnableIPCheck();
void DisableIPCheck();
void UpdateBlockedIPList(IPTablePtr ips);
void AppenedBlockedIP(DateTime::Span block_time, std::string ip);
uns::HTTPMethod GetMethod(uns::RequestPtr request);
void AddStreamSettings(std::string method, bool stream);
public:
virtual uns::ResponsePtr Processor(uns::RequestPtr request) = 0;
virtual uns::PathTraversalDefenceLevel PTDefence();
virtual bool IsPathSafe(const std::string& raw_path);
public:
uns::ResponsePtr Handle(uns::RequestPtr request);
bool Stream(const std::string& method);
};
using ServerProcessorPtr = std::shared_ptr<ServerProcessor>;
+86
View File
@@ -0,0 +1,86 @@
#pragma once
#include <map>
#include <set>
#include <mutex>
#include <chrono>
#include "Export.h"
#include "HTTPObjects.h"
namespace Json
{
class Value;
}
class UNSWSC_DLL_EXPORT SessionManager
{
public:
using systime = std::chrono::system_clock::time_point;
class UNSWSC_DLL_EXPORT Session
{
private:
int uid;
std::string cookie;
systime expiry_date;
static const std::chrono::hours max_age;
public:
Session();
Session(const Session& obj);
Session(const std::string& cookie); //构造仅用于比较的临时Session对象
Session(const Json::Value& json_obj); //从JSON反序列化
Session(int uid, const std::string& cookie);
public:
int GetUID() const;
bool Expired() const;
std::string GetCookie() const;
systime GetExpiryDate() const;
bool NeedExpiryDateRefresh() const; //使用超过一天且在活跃的cookie将被刷新。此处仅作时间判断
public:
void SetUID(int uid);
void RefreshExpiryDate();
void SetCookie(const std::string& cookie);
void SetExpiryDate(const systime& expiry_time);
public:
bool operator<(const Session& obj) const; //For std::map/std::set/...
operator Json::Value() const; //JSON序列化
static int GetMaxAgeSeconds();
};
private:
std::mutex pool_mutex;
//Key: Cookie Pair(oreo=xxxxx), Value: UID
std::map<std::string, int> cookie_storage;
//Key: UID, Value: Cookie Lists
std::map<int, std::set<Session>> reverse_cookie_storage;
const std::string prefix = "oreo=";
//const std::string cookie_template = prefix + "{}; HttpOnly; Path=/; SameSite=Lax; Max-Age={}";
const std::string cookie_template = prefix + "{}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age={}";
public:
SessionManager();
SessionManager(const SessionManager& obj) = delete;
public:
static std::string Random();
static std::string ISO8601_TimeString();
public:
void AutoCleanCookiePool();
std::string GenerateCookieForUser(int uid, bool cookie_only = false);
std::string DeleteCookie(const std::string& cookie);
bool CheckRequestCookie(uns::RequestPtr request, int& uid);
std::string DeleteAllCookieForUser(int uid, const std::string& current_cookie);
bool HasDumpedCookie(const std::string& path);
bool LoadDumpedCookie(const std::string& path);
bool DumpAllValidCookies(const std::string& path);
};
extern UNSWSC_DLL_EXPORT SessionManager GlobalSessionManager;
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "Export.h"
#include "Global.h"
#include "IPTable.h"
#include "WebFileInfo.h"
#include "HTTPObjects.h"
class UNSWSC_DLL_EXPORT SyncFileReceiver
{
protected:
class Impl;
std::unique_ptr<Impl> pimpl;
public:
SyncFileReceiver();
virtual ~SyncFileReceiver();
public:
void SetResponseMode(bool html);
void SetCORSEnable(bool enable);
std::string EncodeUploadResult();
std::string EncodeUploadResultHTML();
void UpdateBlockedIPs(IPTablePtr ip);
void SetTempRoot(std::string temp_root);
uns::HTTPMethod GetMethod(uns::RequestPtr request);
void AppenedBlockedIP(DateTime::Span block_time, std::string ip);
bool WriteFile(const std::string& path, const std::string& bytes);
public:
virtual uns::Status PreCheckRequest(uns::RequestPtr request) = 0;
virtual bool PreCheckForm(uns::FormPartPtr form) = 0;
virtual uns::PathTraversalDefenceLevel PTDefence();
virtual bool IsPathSafe(const std::string& raw_path);
// 【修改】由原先的 Callback 改为可供子类重写的虚函数
// 返回值改为 webcc::ResponsePtr,并且引入 request 参数以便子类调用 AutoCORS 或解析请求头
virtual uns::ResponsePtr ProcessFiles(const WebFileInfoVec& file_info, const std::string& tmp_root, uns::RequestPtr request);
public:
uns::ResponsePtr Execute(uns::RequestPtr request);
};
using SyncFileReceiverPtr = std::shared_ptr<SyncFileReceiver>;
+238
View File
@@ -0,0 +1,238 @@
#pragma once
#include <set>
#include <memory>
#include "Export.h"
#include <filesystem>
#include "HTTPObjects.h"
namespace Json
{
class Value;
}
namespace uns
{
class UNSWSC_DLL_EXPORT ResponseBuilder
{
private:
class Impl;
std::unique_ptr<Impl> impl;
private:
bool FileExist(std::string file);
public:
static void SetGlobalAllowFraming(bool allow);
public:
ResponseBuilder();
virtual ~ResponseBuilder();
ResponseBuilder(uns::RequestPtr req);
ResponseBuilder(const ResponseBuilder&) = delete;
ResponseBuilder& operator=(const ResponseBuilder&) = delete;
public:
uns::ResponsePtr operator()();
public: //Standard Return Code 1xx
//100 - 此临时响应表明客户端应继续请求,或者如果请求已完成,则忽略此响应。
ResponseBuilder& Continue();
//101 - 此代码是在响应客户端的 Upgrade 请求标头时发送的,用于指示服务器即将切换到的协议。
ResponseBuilder& SwitchingProtocols();
//102 - 此代码曾在 WebDAV 上下文中使用,表示服务器已收到请求,但在响应时无法提供状态。
ResponseBuilder& Processing();
//103 - 此状态码主要与 Link 标头一起使用,允许用户代理在服务器准备响应时开始预加载资源,或预连接到页面需要资源的源站。
ResponseBuilder& EarlyHints();
public: //Standard Return Code 2xx
//200 - 请求成功。
ResponseBuilder& OK();
//201 - 请求成功,并因此创建了一个新资源。
ResponseBuilder& Created();
//202 - 请求已被接收但尚未处理。
ResponseBuilder& Accepted();
//203 - 此响应代码表示返回的元数据与原始服务器上可用的不完全相同,而是从本地或第三方副本收集的。这主要用于另一个资源的镜像或备份。
ResponseBuilder& NonAuthoritativeInformation();
//204 - 对于此请求,没有内容可发送,但响应头可能有用。
ResponseBuilder& NoContent();
//205 - 告知用户代理重置发送此请求的文档。
ResponseBuilder& ResetContent();
//206 - 当客户端请求了资源的一部分时,使用此响应代码进行响应。
ResponseBuilder& PartialContent();
//207 - 在可能需要多个状态码的情况下,传递关于多个资源的信息。
ResponseBuilder& MultiStatus();
//208 - 在 <dav:propstat> 响应元素内部使用,以避免重复枚举同一集合的多个绑定的内部成员。
ResponseBuilder& AlreadyReported();
//226 - 服务器已完成了对资源的 GET 请求,并且响应是对当前实例应用了一个或多个实例操作后的结果表示。
ResponseBuilder& IMUsed();
public: //Standard Return Code 3xx
//300 - 在代理驱动(agent-driven)的内容协商中,请求有多个可能的响应,用户代理或用户应选择其中之一。
ResponseBuilder& MultipleChoices();
//301 - 请求资源的 URL 已永久更改。新 URL 在响应中给出。
ResponseBuilder& MovedPermanently(const std::string& new_url);
//302 - 此响应代码意味着请求资源的 URI 已暂时更改。未来可能还会对 URI 进行进一步更改,因此客户端在未来的请求中应使用相同的 URI。
ResponseBuilder& Found(const std::string& new_url);
//303 - 服务器发送此响应以指示客户端使用 GET 请求在另一个 URI 获取请求的资源。
ResponseBuilder& SeeOther(const std::string& new_url);
//304 - 用于缓存目的。它告知客户端响应未被修改,因此客户端可以继续使用相同的缓存响应版本。
ResponseBuilder& NotModified();
//305 - 在 HTTP 规范的前一版本中定义,表示请求的响应必须通过代理访问。由于涉及代理带内配置的安全问题,此状态码已被弃用。
ResponseBuilder& UseProxy();
//306 - 此响应代码不再使用,但被保留。它曾在 HTTP/1.1 规范的先前版本中使用。
ResponseBuilder& __Unused();
//307 - 服务器发送此响应以指示客户端使用与先前请求相同的方法在另一个 URI 获取请求的资源。其语义与 302 Found 响应代码相同,但用户代理不得更改使用的 HTTP 方法。
ResponseBuilder& TemporaryRedirect(const std::string& new_url);
//308 - 表示资源现在永久位于另一个 URI,由 Location 响应头指定。其语义与 301 Moved Permanently HTTP 响应代码相同,但用户代理不得更改使用的 HTTP 方法。
ResponseBuilder& PermanentRedirect(const std::string& new_url);
public: //Recode Code 4xx
//400 - 由于被认为是客户端错误的原因(例如,格式错误的请求语法、无效的请求消息结构或欺骗性的请求路由),服务器无法或不会处理该请求。
ResponseBuilder& BadRequest();
//401 - 尽管 HTTP 标准指定为 "unauthorized",但从语义上讲,此响应的意思是 "unauthenticated"。即,客户端必须进行身份验证才能获得请求的响应。
ResponseBuilder& Unauthorized();
//402 - 此代码最初用于数字支付系统,但此状态码很少使用,且不存在标准约定。
ResponseBuilder& PaymentRequired();
//403 - 客户端没有访问内容的权利;也就是说,它是未授权的,因此服务器拒绝提供请求的资源。与 401 Unauthorized 不同,服务器知道客户端的身份。
ResponseBuilder& Forbidden();
//404 - 服务器找不到请求的资源。
ResponseBuilder& NotFound();
//405 - 服务器知道请求方法,但目标资源不支持该方法。
ResponseBuilder& MethodNotAllowed();
//406 - 当 Web 服务器执行服务器驱动的内容协商后,找不到任何符合用户代理给定条件的内容时,会发送此响应。
ResponseBuilder& NotAcceptable();
//407 - 类似于 401 Unauthorized,但需要通过代理进行身份验证。
ResponseBuilder& ProxyAuthenticationRequired();
//408 - 某些服务器会在空闲连接上发送此响应,即使客户端之前没有任何请求。这意味着服务器希望关闭此未使用的连接。
ResponseBuilder& RequestTimeout();
//409 - 当请求与服务器的当前状态冲突时,发送此响应。
ResponseBuilder& Conflict();
//410 - 当请求的内容已从服务器永久删除,且没有转发地址时,发送此响应。
ResponseBuilder& Gone();
//411 - 服务器拒绝了请求,因为未定义 Content-Length 标头字段,而服务器需要它。
ResponseBuilder& LengthRequired();
//412 - 在条件请求中,客户端在其标头中指明了服务器不满足的前提条件。
ResponseBuilder& PreconditionFailed();
//413 - 请求体大于服务器定义的限制。
ResponseBuilder& ContentTooLarge();
//414 - 客户端请求的 URI 长度超过了服务器愿意解释的长度。
ResponseBuilder& URITooLong();
//415 - 服务器不支持请求数据的媒体格式,因此服务器拒绝该请求。
ResponseBuilder& UnsupportedMediaType();
//416 - 无法满足请求中 Range 标头字段指定的范围。可能范围超出了目标资源数据的大小。
ResponseBuilder& RangeNotSatisfiable();
//417 - 此响应代码表示服务器无法满足 Expect 请求标头字段指示的期望。
ResponseBuilder& ExpectationFailed();
//418 - 服务器拒绝尝试用茶壶煮咖啡。
ResponseBuilder& IamATeapot();
//421 - 请求被发送到了一个无法产生响应的服务器。
ResponseBuilder& MisdirectedRequest();
//422 - 请求格式正确,但由于语义错误而无法被遵循。
ResponseBuilder& UnprocessableContent();
//423 - 正在访问的资源已被锁定。
ResponseBuilder& Locked();
//424 - 由于先前的请求失败,导致当前请求失败。
ResponseBuilder& FailedDependency();
//425 - 表示服务器不愿意冒险处理一个可能被重放的请求。
ResponseBuilder& TooEarly();
//426 - 服务器拒绝使用当前协议执行请求,但可能在客户端升级到其他协议后愿意执行。服务器在 426 响应中发送 Upgrade 标头以指示所需的协议。
ResponseBuilder& UpgradeRequired(const std::string& protocol);
//428 - 原始服务器要求请求是有条件的。此响应旨在防止"丢失更新"问题,即客户端 GET 资源状态,修改后 PUT 回服务器,而同时第三方已修改了服务器上的状态,导致冲突。
ResponseBuilder& PreconditionRequired();
//429 - 用户在给定的时间内发送了太多请求(速率限制)。
ResponseBuilder& TooManyRequests();
//431 - 服务器因请求头字段太大而不愿意处理该请求。
ResponseBuilder& RequestHeaderFieldsTooLarge();
//451 - 用户代理请求了一个无法合法提供的资源,例如被政府审查的网页。
ResponseBuilder& UnavailableForLegalReasons();
public: //Standard Return Code 5xx
//500 - 服务器遇到了不知道如何处理的情况。此错误是通用性的,表示服务器找不到更合适的 5XX 状态码来响应。
ResponseBuilder& InternalServerError();
//501 - 服务器不支持请求方法,无法处理。
ResponseBuilder& NotImplemented();
//502 - 此错误响应意味着服务器作为网关或代理时,收到了一个无效的响应。
ResponseBuilder& BadGateway();
//503 - 服务器尚未准备好处理请求。
ResponseBuilder& ServiceUnavailable();
//504 - 当服务器作为网关或代理,无法及时获得响应时,会给出此错误响应。
ResponseBuilder& GatewayTimeout();
//505 - 服务器不支持请求中使用的 HTTP 版本。
ResponseBuilder& HTTPVersionNotSupported();
//506 - 服务器存在内部配置错误:在内容协商过程中,被选中的变体被配置为自身参与内容协商,这导致在创建响应时出现循环引用。
ResponseBuilder& VariantAlsoNegotiates();
//507 - 由于服务器无法存储成功完成请求所需的表示,因此无法对资源执行该方法。
ResponseBuilder& InsufficientStorage();
//508 - 服务器在处理请求时检测到无限循环。
ResponseBuilder& LoopDetected();
//510 - 客户端请求声明了一个应使用 HTTP 扩展(RFC 2774)来处理请求,但该扩展不受支持。
ResponseBuilder& NotExtended();
//511 - 表示客户端需要进行身份验证才能获得网络访问权限。
ResponseBuilder& NetworkAuthenticationRequired();
public: //Non-Standard Return Code 4xx
//489 - 请求载体的格式错误,如:无法解析的JSON等。
ResponseBuilder& RequestFormatError();
//490 - 请求无效。可能是由于未正确携带数据等必要信息。
ResponseBuilder& RequestInvalid();
//492 - 请求URL超范围。此响应表示请求的URL是错误的。
ResponseBuilder& URLOutOfRange();
//493 - 无效的请求主机。指示请求时使用了错误的域名/IP。
ResponseBuilder& InvalidRequestHost();
//494 - IP地址被封禁。
ResponseBuilder& IPBlocked();
//495 -非法上传请求。指示本次上传请求不符合服务器规定。
ResponseBuilder& IllegalUpload();
//496 - 文件格式错误。指示上传的文件格式不符合服务器规定。
ResponseBuilder& FileFormatError();
//497 - 无效文件。处理请求所需的文件已过期/无法访问。
ResponseBuilder& InvalidFile();
//498 - 上传的文件过大。非文件上传时应使用 413 Content Too Large。
ResponseBuilder& FileTooLarge();
//499 - 每秒请求数过多。仅在一些特殊API中使用,常规情况需使用 429 Too Many Requests。
ResponseBuilder& RPSLimited();
public: //Non-Standard Code 5xx
//533 - 子过程失败。服务器在处理请求的某个步骤中遇到无法恢复的错误。
ResponseBuilder& SubProcessFalied();
//540 - 服务器检测到漏洞利用/可执行文件上传等网络攻击行为。
ResponseBuilder& ServerHateYou();
//550 - 检测到拒绝服务漏洞攻击。
ResponseBuilder& DoSFound();
//551 - 检测到分布式拒绝服务漏洞攻击。
ResponseBuilder& DDoSFound();
//560 - 未知的服务器错误。当服务器无法定位错误来源时返回。否则应使用 500 Internal Server Error。
ResponseBuilder& UnknownServerError();
public: //Header Functions
ResponseBuilder& Utf8();
ResponseBuilder& Json();
ResponseBuilder& Date();
ResponseBuilder& GZip();
ResponseBuilder& DenyFraming();
ResponseBuilder& AllowFraming();
ResponseBuilder& Code(int code);
ResponseBuilder& Charset(std::string_view charset);
ResponseBuilder& SetCookie(std::string_view cookie);
ResponseBuilder& MediaType(std::string_view media_type);
ResponseBuilder& Header(std::string_view key, std::string_view value);
public: //Body Functions
ResponseBuilder& EmptyBody();
ResponseBuilder& Body(int data);
ResponseBuilder& Body(std::string&& data);
ResponseBuilder& Body(const Json::Value& data);
ResponseBuilder& Body(const std::string& data);
ResponseBuilder& ErrorPage();
ResponseBuilder& ErrorPage(std::string_view error_page);
ResponseBuilder& File(const std::filesystem::path& path, bool infer_media_type = true, std::size_t chunk_size = 1024);
ResponseBuilder& FileForDownload(const std::filesystem::path& path, bool infer_media_type = true, std::size_t chunk_size = 1024);
public: //CORS Functions
//Perform CORS Check and Return CORS Headers, Must be the last function called
ResponseBuilder& AutoCORS(uns::RequestPtr req);
ResponseBuilder& CORS(const std::string& origin);
ResponseBuilder& CORS_Full(const std::string& origin, const std::set<std::string>& headers);
};
};
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <vector>
#include "Export.h"
#include "DateTime.h"
class UNSWSC_DLL_EXPORT WebFileInfo
{
private:
std::string OriginalFileName;
std::string StorageFileName;
std::string ExtensionName;
DateTime UploadTime;
size_t FileSize;
private:
std::string GetRandomNumber();
std::string GetExtension(std::string ofn);
std::string GenerateStorageName(std::string ofn);
public:
WebFileInfo() = default;
WebFileInfo(std::string ofn, size_t size);
WebFileInfo(std::string ofn, std::string sfn, std::string en, DateTime upt, size_t siz);
WebFileInfo(const WebFileInfo& obj);
public:
std::string MakePath(std::string storage_dir) const;
std::string GetOriginalFileName() const;
std::string GetStorageFileName() const;
std::string GetExtensionName() const;
DateTime GetUploadTime() const;
size_t GetFileSize() const;
};
using WebFileInfoVec = std::vector<WebFileInfo>;