#include "CORSProcessor.h" #include "ServerLogger.h" #include "CORSConfig.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(s[start]))) ++start; if (start == s.size()) return ""; size_t end = s.size() - 1; while ((end > start) && std::isspace(static_cast(s[end]))) --end; return s.substr(start, end - start + 1); } static inline std::vector SplitByComma(const std::string& raw) { std::vector 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 CORSProcessor::ParseHeaderList(const std::string& raw_headers) { std::set 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 CORSProcessor::ParseMethodList(const std::string& raw_headers) { std::set 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))) { SCLOG_WARNING("Rejected by Host check: %s", host.c_str()); return uns::ResponseBuilder().Forbidden().EmptyBody()(); } if(!GlobalCORSConfig.UrlValidate(origin)) { SCLOG_WARNING("Invalid CORS Origin: %s", origin.c_str()); 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)) { SCLOG_WARNING("Invalid CORS Method: %s (All Methods: %d)", method.c_str(), acrm.c_str()); return uns::ResponseBuilder().NotAcceptable().EmptyBody()(); } auto acrh_values = acrh.empty() ? std::set() : 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()) { SCLOG_WARNING("Invalid CORS Header(s): %s", acrh.c_str()); 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()(); } SCLOG_INFO("CORS preflight allow origin=%s methods=%s headers=%s cred=%d", origin.c_str(), allow_methods_value.c_str(), valid_headers.c_str(), 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::SharedPtr() { return std::make_shared(); }