upgrade core add header check and incrace upload speed
This commit is contained in:
+102
-58
@@ -13,7 +13,8 @@ public:
|
||||
bool HTMLResponse = false;
|
||||
std::string TempRoot;
|
||||
IPTablePtr BlockedIPs = nullptr;
|
||||
WebFileInfoVec FileInfo;
|
||||
TempFileManager FileManager;
|
||||
std::chrono::seconds FileTimeout = 300s, FileMaxProcTimeout = 600s;
|
||||
};
|
||||
|
||||
SyncFileReceiver::SyncFileReceiver() : pimpl(std::make_unique<Impl>())
|
||||
@@ -23,31 +24,34 @@ SyncFileReceiver::SyncFileReceiver() : pimpl(std::make_unique<Impl>())
|
||||
SyncFileReceiver::~SyncFileReceiver() = default;
|
||||
|
||||
// 【修改】虚函数的默认实现:如果子类不重写,则默认返回原先的成功状态(201 Created)
|
||||
uns::ResponsePtr SyncFileReceiver::ProcessFiles(const WebFileInfoVec& file_info, const std::string& tmp_root, uns::RequestPtr request)
|
||||
uns::ResponsePtr SyncFileReceiver::ProcessFiles(TempFileManager& file_info, SFR_FileMap file_map, const std::string& tmp_root, uns::RequestPtr request)
|
||||
{
|
||||
SCLOGF_ERROR("SyncFileReceiver::ProcessFiles default handler triggered. Files count: {}", file_info.size());
|
||||
|
||||
SCLOGF_ERROR("SyncFileReceiver::ProcessFiles default handler triggered. Files count: {}", file_map.size());
|
||||
|
||||
std::string resp_body = (pimpl->HTMLResponse ? EncodeUploadResultHTML() : EncodeUploadResult());
|
||||
|
||||
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Created().Body(resp_body).AutoCORS(request)() : uns::ResponseBuilder().Created().Body(resp_body)());
|
||||
}
|
||||
|
||||
void SyncFileReceiver::SetResponseMode(bool html)
|
||||
{
|
||||
pimpl->HTMLResponse = html;
|
||||
SCLOG_DEBUG("SyncFileReceiver init mode: %s", (html ? "html" : "json"));
|
||||
SCLOGF_DEBUG("SyncFileReceiver init mode: {}", (html ? "html" : "json"));
|
||||
}
|
||||
|
||||
void SyncFileReceiver::SetCORSEnable(bool enable)
|
||||
{
|
||||
pimpl->EnableCORS = enable;
|
||||
SCLOG_DEBUG("SyncFileReceiver CORS mode: %s", (enable ? "enabled" : "disabled"));
|
||||
SCLOGF_DEBUG("SyncFileReceiver CORS mode: {}", (enable ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
void SyncFileReceiver::SetTempRoot(std::string temp_root)
|
||||
{
|
||||
pimpl->TempRoot = temp_root;
|
||||
SCLOG_TRACE("SFR-TempRoot: %s", pimpl->TempRoot.c_str());
|
||||
if (pimpl->FileManager.SetBaseDirectory(temp_root))
|
||||
SCLOGF_TRACE("SFR-TempRoot: {}", pimpl->TempRoot);
|
||||
else
|
||||
SCLOGF_WARNING("SFR: Failed to Set Temp Root ({})", temp_root);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,7 +67,7 @@ void SyncFileReceiver::AppenedBlockedIP(DateTime::Span block_time, std::string i
|
||||
IPList li{ ip };
|
||||
pimpl->BlockedIPs->Appened(expr_time, li);
|
||||
pimpl->BlockedIPs->Update();
|
||||
SCLOG_INFO("IP: [%s] has been blocked untill {%s}", ip.c_str(), std::string(expr_time).c_str());
|
||||
SCLOGF_INFO("IP: [{}] has been blocked untill {{{}}}", ip, std::string(expr_time));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,13 +101,24 @@ bool SyncFileReceiver::WriteFile(const std::string& path, const std::string& byt
|
||||
std::ofstream stream{ path, std::ios::binary };
|
||||
if (stream.fail())
|
||||
{
|
||||
SCLOG_WARNING("Failed to write file [%s]: can't open stream", path.c_str());
|
||||
SCLOGF_WARNING("Failed to write file [{}]: can't open stream", path);
|
||||
return false;
|
||||
}
|
||||
stream.write(bytes.data(), bytes.size());
|
||||
if (stream.fail())
|
||||
SCLOG_WARNING("Failed to write file [%s]: can't write to stream", path.c_str());
|
||||
return !stream.fail();
|
||||
bool fail = stream.fail();
|
||||
if (fail)
|
||||
SCLOGF_WARNING("Failed to write file [{}]: can't write to stream", path);
|
||||
else
|
||||
SCLOGF_TRACE("Wrote {siz-b} to file [{}]", bytes.size(), path);
|
||||
return !fail;
|
||||
}
|
||||
|
||||
void SyncFileReceiver::SetFileTimeout(std::chrono::seconds timeout, std::chrono::seconds max_proc_timeout) noexcept
|
||||
{
|
||||
if (timeout.count() > 0)
|
||||
pimpl->FileTimeout = timeout;
|
||||
if (max_proc_timeout.count() > 0)
|
||||
pimpl->FileMaxProcTimeout = max_proc_timeout;
|
||||
}
|
||||
|
||||
uns::PathTraversalDefenceLevel SyncFileReceiver::PTDefence()
|
||||
@@ -111,25 +126,41 @@ uns::PathTraversalDefenceLevel SyncFileReceiver::PTDefence()
|
||||
return uns::PathTraversalDefenceLevel::DenyAll;
|
||||
}
|
||||
|
||||
bool SyncFileReceiver::IsPathSafe(const std::string & raw_path)
|
||||
bool SyncFileReceiver::IsPathSafe(const std::string& raw_path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SyncFileReceiver::IsHeaderValid(uns::RequestPtr request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SyncFileReceiver::EncodeUploadResult()
|
||||
{
|
||||
Json::Value root;
|
||||
Json::FastWriter writer;
|
||||
root["AcceptedCount"] = pimpl->FileInfo.size();
|
||||
root["AcceptedFiles"] = Json::Value(Json::arrayValue);
|
||||
for (auto& ele : pimpl->FileInfo)
|
||||
try
|
||||
{
|
||||
Json::Value sub;
|
||||
sub["FileName"] = ele.GetStorageFileName();
|
||||
sub["UploadTime"] = ele.GetUploadTime().GetTimeStamp();
|
||||
root["AcceptedFiles"].append(sub);
|
||||
Json::Value root;
|
||||
Json::FastWriter writer;
|
||||
// 1. 从管理器安全获取当前所有文件的快照
|
||||
auto file_infos = pimpl->FileManager.GetAllFileInfos();
|
||||
// 2. 组装 JSON 数据
|
||||
root["AcceptedCount"] = static_cast<Json::Value::UInt64>(file_infos.size());
|
||||
root["AcceptedFiles"] = Json::Value(Json::arrayValue);
|
||||
for (const auto& ele : file_infos)
|
||||
{
|
||||
Json::Value sub;
|
||||
sub["FileName"] = ele.GetStorageFileName();
|
||||
sub["UploadTime"] = ele.GetUploadTime().GetTimeStamp();
|
||||
root["AcceptedFiles"].append(sub);
|
||||
}
|
||||
return writer.write(root);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// 极致异常安全兜底:如果 Json 报错或内存写满,返回一个合法的空 JSON 字符串
|
||||
return "{\"AcceptedCount\":0,\"AcceptedFiles\":[]}";
|
||||
}
|
||||
return writer.write(root);
|
||||
}
|
||||
|
||||
std::string SyncFileReceiver::EncodeUploadResultHTML()
|
||||
@@ -150,44 +181,60 @@ std::string SyncFileReceiver::EncodeUploadResultHTML()
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
std::string tmp;
|
||||
for (auto& ele : pimpl->FileInfo)
|
||||
tmp += "[" + ele.GetStorageFileName() + "] - {" + ele.GetUploadTime().Format("%Y-%m-%d %H:%M:%S") + "}<br>";
|
||||
size_t html_size = strlen(html) + tmp.size() + 10;
|
||||
char* result = new char[html_size];
|
||||
memset(result, 0, sizeof(result));
|
||||
sprintf(result, html, pimpl->FileInfo.size(), tmp.c_str());
|
||||
tmp = std::string(result);
|
||||
delete[] result;
|
||||
return tmp;
|
||||
try
|
||||
{
|
||||
// 1. 获取文件快照
|
||||
auto file_infos = pimpl->FileManager.GetAllFileInfos();
|
||||
// 2. 拼接文件列表 HTML
|
||||
std::string tmp;
|
||||
for (const auto& ele : file_infos)
|
||||
tmp += "[" + ele.GetStorageFileName() + "] - {" + ele.GetUploadTime().Format("%Y-%m-%d %H:%M:%S") + "}<br>";
|
||||
// 3. 动态安全计算所需缓冲区大小(32字节用于容纳 %lld 的数字展开)
|
||||
size_t html_size = strlen(html) + tmp.size() + 32;
|
||||
// 利用 std::string 管理缓冲区内存(RAII 机制,无论发生什么都会自动释放,绝不泄漏)
|
||||
std::string result_str(html_size, '\0');
|
||||
// 使用安全的 snprintf 写入 string 内部缓冲区
|
||||
int written = snprintf(result_str.data(), result_str.size(), html, static_cast<long long>(file_infos.size()), tmp.c_str());
|
||||
if (written > 0)
|
||||
{
|
||||
result_str.resize(written); // 裁剪掉尾部多余的 \0
|
||||
return result_str;
|
||||
}
|
||||
return "HTML generation failed";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// 异常安全兜底
|
||||
return "<html><body><center><h1>Upload Result Error</h1></center></body></html>";
|
||||
}
|
||||
}
|
||||
|
||||
uns::ResponsePtr SyncFileReceiver::Execute(uns::RequestPtr request)
|
||||
{
|
||||
uns::HTTPMethod method = GetMethod(request);
|
||||
std::string x_real_ip;
|
||||
if(request->GetImpl()->webcc_req->HasHeader("X-Real-IP"))
|
||||
if (request->GetImpl()->webcc_req->HasHeader("X-Real-IP"))
|
||||
x_real_ip = request->GetImpl()->webcc_req->GetHeader("X-Real-IP");
|
||||
std::string req_ip = (x_real_ip.empty() ? request->GetImpl()->webcc_req->address() : x_real_ip);
|
||||
SCLOG_DEBUG("Request recived, ip: [%s], method: %s", req_ip.c_str(), request->GetImpl()->webcc_req->method().c_str());
|
||||
SCLOGF_DEBUG("Request recived, ip: [{}], method: {}", req_ip, request->GetImpl()->webcc_req->method());
|
||||
// path test
|
||||
std::string path = request->GetImpl()->webcc_req->url().path();
|
||||
auto status = PathTraversal::AnalyzeUrlTraversal(path);
|
||||
if(status != PathTraversal::UrlSafetyStatus::Safe)
|
||||
if (status != PathTraversal::UrlSafetyStatus::Safe)
|
||||
SCLOGF_WARNING("PathTraversal Detected: {}, Level: {}", path, PathTraversal::ToString(status));
|
||||
switch(PTDefence())
|
||||
switch (PTDefence())
|
||||
{
|
||||
case uns::PathTraversalDefenceLevel::DenyAll:
|
||||
if(status != PathTraversal::UrlSafetyStatus::Safe)
|
||||
if (status != PathTraversal::UrlSafetyStatus::Safe)
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
break;
|
||||
case uns::PathTraversalDefenceLevel::AutoNormalize:
|
||||
{
|
||||
if(status == PathTraversal::UrlSafetyStatus::EvasiveTraversal)
|
||||
if (status == PathTraversal::UrlSafetyStatus::EvasiveTraversal)
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
auto decoded_path = PathTraversal::UrlDecode(path);
|
||||
if(!IsPathSafe(decoded_path))
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
if (!IsPathSafe(decoded_path))
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
auto url = request->GetImpl()->webcc_req->url();
|
||||
url.ForceSet_Path(PathTraversal::NormalizeUrlPath(decoded_path));
|
||||
request->GetImpl()->webcc_req->set_url(std::move(url));
|
||||
@@ -195,11 +242,11 @@ uns::ResponsePtr SyncFileReceiver::Execute(uns::RequestPtr request)
|
||||
}
|
||||
case uns::PathTraversalDefenceLevel::AllowNormal:
|
||||
{
|
||||
if(status == PathTraversal::UrlSafetyStatus::EvasiveTraversal)
|
||||
if (status == PathTraversal::UrlSafetyStatus::EvasiveTraversal)
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
auto decoded_path = PathTraversal::UrlDecode(path);
|
||||
if(!IsPathSafe(decoded_path))
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
if (!IsPathSafe(decoded_path))
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Forbidden().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Forbidden().EmptyBody()());
|
||||
auto url = request->GetImpl()->webcc_req->url();
|
||||
url.ForceSet_Path(decoded_path);
|
||||
request->GetImpl()->webcc_req->set_url(std::move(url));
|
||||
@@ -217,13 +264,14 @@ uns::ResponsePtr SyncFileReceiver::Execute(uns::RequestPtr request)
|
||||
if (pimpl->BlockedIPs->IPExist(req_ip))
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().IPBlocked().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().IPBlocked().EmptyBody()());
|
||||
}
|
||||
webcc::Status tmpStatus = uns::ConvertStatus(PreCheckRequest(request));
|
||||
if (tmpStatus != webcc::kOK)
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Code(tmpStatus).EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Code(tmpStatus).EmptyBody()());
|
||||
webcc::Status tmp_status = uns::ConvertStatus(PreCheckRequest(request));
|
||||
if (tmp_status != webcc::kOK)
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().Code(tmp_status).EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().Code(tmp_status).EmptyBody()());
|
||||
else if (!request->IsForm())
|
||||
return (pimpl->EnableCORS ? uns::ResponseBuilder().RequestFormatError().EmptyBody().AutoCORS(request)() : uns::ResponseBuilder().RequestFormatError().EmptyBody()());
|
||||
else
|
||||
{
|
||||
SFR_FileMap fmap;
|
||||
for (auto& form : request->GetFormParts())
|
||||
{
|
||||
if (form->GetFileName().empty())
|
||||
@@ -236,17 +284,13 @@ uns::ResponsePtr SyncFileReceiver::Execute(uns::RequestPtr request)
|
||||
SCLOGF_DEBUG("File recived: [{}], {} bytes", form->GetFileName(), form->GetDataSize());
|
||||
WebFileInfo info(form->GetFileNameS(), form->GetDataSize());
|
||||
WriteFile(info.MakePath(pimpl->TempRoot), form->GetData());
|
||||
pimpl->FileInfo.push_back(info);
|
||||
fmap.insert({ form->GetFileNameS(), info.GetStorageFileName() });
|
||||
//pimpl->FileInfo.push_back(info);
|
||||
if (!pimpl->FileManager.RegisterFile(info, pimpl->FileTimeout, pimpl->FileMaxProcTimeout))
|
||||
SCLOGF_WARNING("FileManager.RegisterFile Error, File: {}, TempRoot: {}", form->GetFileName(), pimpl->TempRoot);
|
||||
}
|
||||
|
||||
// 【修改的关键步骤】
|
||||
// 1. 同步调用虚函数,获取具体的业务处理结果(及构筑好的自定义 HTTP Response)
|
||||
uns::ResponsePtr response = ProcessFiles(pimpl->FileInfo, pimpl->TempRoot, request);
|
||||
|
||||
// 2. 清理当前类中的文件缓存(防止污染下一次 HTTP 请求)
|
||||
pimpl->FileInfo.clear();
|
||||
|
||||
// 3. 作为最后一步直接返回
|
||||
// 同步调用虚函数,获取具体的业务处理结果(及构筑好的自定义 HTTP Response)
|
||||
uns::ResponsePtr response = ProcessFiles(pimpl->FileManager, fmap, pimpl->TempRoot, request);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user