Files

146 lines
4.5 KiB
C++
Raw Permalink 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.
#include "CORSProcessor.h"
#include "CORSConfig.h"
#include "ServerLogger.h"
#include "UNSResponseBuilder.h"
// ----- 辅助函数(文件作用域) -----
static inline std::string Trim(const std::string& s)
{
size_t start = 0;
while ((start < s.size()) && std::isspace(static_cast<unsigned char>(s[start])))
++start;
if (start == s.size())
return "";
size_t end = s.size() - 1;
while ((end > start) && std::isspace(static_cast<unsigned char>(s[end])))
--end;
return s.substr(start, end - start + 1);
}
static inline std::vector<std::string> SplitByComma(const std::string& raw)
{
std::vector<std::string> parts;
std::istringstream ss(raw);
std::string token;
while (std::getline(ss, token, ','))
parts.push_back(token);
return parts;
}
// 1) 解析请求头(Access-Control-Request-Headers)
// 返回小写形式的集合(用于比较/交集)
std::set<std::string> CORSProcessor::ParseHeaderList(const std::string& raw_headers)
{
std::set<std::string> out;
if (raw_headers.empty())
return out;
auto parts = SplitByComma(raw_headers);
for (const auto& p : parts)
{
std::string t = Trim(p);
if (t.empty())
continue;
out.insert(uns::tools::ToLower(t)); // 规范化为小写用于比较
}
return out;
}
// 2) 解析请求方法字符串(Access-Control-Request-Method 或逗号分隔的 Methods)
// 返回大写形式的集合(用于比较)
std::set<std::string> CORSProcessor::ParseMethodList(const std::string& raw_headers)
{
std::set<std::string> out;
if (raw_headers.empty())
return out;
auto parts = SplitByComma(raw_headers);
for (const auto& p : parts)
{
std::string t = Trim(p);
if (t.empty())
continue;
out.insert(uns::tools::ToUpper(t));
}
return out;
}
uns::ResponsePtr CORSProcessor::Processor(uns::RequestPtr request)
{
if(request->GetMethod() != "OPTIONS")
return uns::ResponseBuilder().MethodNotAllowed().EmptyBody()();
SCLOG_INFO("OPTIONS Request Catched!");
if((!request->HasHeader(uns::cors::reqh_acrm)) || (!request->HasHeader(uns::cors::reqh_o)))
return uns::ResponseBuilder().BadRequest().EmptyBody()();
std::string origin = Trim(request->GetHeader(uns::cors::reqh_o));
std::string acrm = Trim(request->GetHeader(uns::cors::reqh_acrm));
std::string acrh = request->HasHeader(uns::cors::reqh_acrh) ? Trim(request->GetHeader(uns::cors::reqh_acrh)) : std::string();
std::string host = request->HasHeader("Host") ? request->GetHeader("Host") : "";
if ((!host.empty()) && (!GlobalCORSConfig.HostValidate(host)))
{
SCLOGF_WARNING("Rejected by Host check: {}", host);
return uns::ResponseBuilder().Forbidden().EmptyBody()();
}
if(!GlobalCORSConfig.UrlValidate(origin))
{
SCLOGF_WARNING("Invalid CORS Origin: {}", origin);
return uns::ResponseBuilder().Forbidden().EmptyBody()();
}
auto acrm_values = ParseMethodList(acrm);
if (acrm_values.empty())
{
SCLOG_WARNING("Empty Access-Control-Request-Method");
return uns::ResponseBuilder().BadRequest().EmptyBody()();
}
for(const auto& method : acrm_values)
if(!GlobalCORSConfig.IsMethodAllowed(method))
{
SCLOGF_WARNING("Invalid CORS Method: {} (All Methods: {})", method, acrm);
return uns::ResponseBuilder().NotAcceptable().EmptyBody()();
}
auto acrh_values = acrh.empty() ? std::set<std::string>() : ParseHeaderList(acrh);
if (!acrh_values.empty())
{
const size_t MAX_HDR_COUNT = 50;
const size_t MAX_HDR_TOTAL_LEN = 4096; // 可调整
if ((acrh_values.size() > MAX_HDR_COUNT) || (acrh.size() > MAX_HDR_TOTAL_LEN))
{
SCLOG_WARNING("ACRH too large or too many entries");
return uns::ResponseBuilder().BadRequest().EmptyBody()();
}
}
auto valid_headers = GlobalCORSConfig.GetValidateHeaders(acrh_values);
if((!acrh_values.empty()) && valid_headers.empty())
{
SCLOGF_WARNING("Invalid CORS Header(s): {}", acrh);
return uns::ResponseBuilder().NotAcceptable().EmptyBody()();
}
std::string allow_methods_value = GlobalCORSConfig.GetValidateMethods();
if (allow_methods_value.empty())
{
SCLOG_WARNING("No allowed methods configured");
return uns::ResponseBuilder().Forbidden().EmptyBody()();
}
SCLOGF_INFO("CORS preflight allow origin={} methods={} headers={} cred={}", origin, allow_methods_value, valid_headers, GlobalCORSConfig.AllowCookie() ? 1 : 0);
return uns::ResponseBuilder().CORS_Full(origin, acrh_values).NoContent().EmptyBody()();
}
std::string CORSProcessor::UrlRegex()
{
return R"(/[\s\S]*)";
}
std::shared_ptr<CORSProcessor> CORSProcessor::SharedPtr()
{
return std::make_shared<CORSProcessor>();
}