Files
UNSWebServerCore_LinuxSO/UNSResponseBuilder.cpp
T
2026-06-30 17:49:51 +08:00

779 lines
18 KiB
C++

#include "UNSResponseBuilder.h"
#include "Global.h"
#include <json/json.h>
#include "CORSConfig.h"
#include "ServerLogger.h"
#include "HTTPObjectsBridge.h"
#include <webcc/response_builder.h>
inline bool global_allow_framing = false;
class uns::ResponseBuilder::Impl : public webcc::ResponseBuilder
{
public:
bool empty_body = false;
bool framing_override = false;
bool allow_framing = global_allow_framing;
public:
Impl() : webcc::ResponseBuilder()
{
}
Impl(webcc::RequestPtr req) : webcc::ResponseBuilder(req)
{
}
};
bool uns::ResponseBuilder::FileExist(std::string file)
{
std::fstream fs(file, std::ios::in);
bool succ = fs.is_open();
if (succ)
fs.close();
return succ;
}
void uns::ResponseBuilder::SetGlobalAllowFraming(bool allow)
{
global_allow_framing = allow;
}
uns::ResponseBuilder::ResponseBuilder() : impl(std::make_unique<Impl>())
{
}
uns::ResponseBuilder::~ResponseBuilder() = default;
uns::ResponseBuilder::ResponseBuilder(uns::RequestPtr req)
{
auto raw_webcc_req = req->GetImpl()->webcc_req;
impl = std::make_unique<Impl>(raw_webcc_req);
}
uns::ResponsePtr uns::ResponseBuilder::operator()()
{
//Auto appened error page when body is empty and not set as empty body
if((impl->GetCode() >= 400) && impl->IsBodyEmpty() && (!impl->empty_body))
{
std::string body = uns::EncodeErrorPage(impl->GetCode());
impl->Body(body);
}
if(impl->empty_body)
impl->Body("");
SCLOGF_INFO("Response ready, Code: {}, Content-Length: {}", impl->GetCode(), impl->GetBodySize());
auto& webcc_res_b = impl->Header("Date", uns::EncodeHTTPTime(nullptr)).Header("Server", G_SERVER_NAME);
bool framing = (impl->framing_override ? impl->allow_framing : global_allow_framing);
if(!framing)
webcc_res_b.Header("X-Frame-Options", "SAMEORIGIN").Header("Content-Security-Policy", "frame-ancestors 'self'");
auto res_impl = std::make_unique<uns::Response::Impl>(webcc_res_b());
return uns::ResponsePtr(new uns::Response(std::move(res_impl)));
}
uns::ResponseBuilder& uns::ResponseBuilder::Continue()
{
impl->Code(webcc::kContinue);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::SwitchingProtocols()
{
impl->Code(webcc::kSwitchingProtocols);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Processing()
{
impl->Code(webcc::kProcessing);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::EarlyHints()
{
impl->Code(webcc::kEarlyHints);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::OK()
{
impl->OK();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Created()
{
impl->Created();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Accepted()
{
impl->Code(webcc::kAccepted);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NonAuthoritativeInformation()
{
impl->Code(webcc::kNonAuthoritativeInformation);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NoContent()
{
impl->Code(webcc::kNoContent);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ResetContent()
{
impl->Code(webcc::kResetContent);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::PartialContent()
{
impl->Code(webcc::kPartialContent);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MultiStatus()
{
impl->Code(webcc::kMultiStatus);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::AlreadyReported()
{
impl->Code(webcc::kAlreadyReported);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::IMUsed()
{
impl->Code(webcc::kIMUsed);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MultipleChoices()
{
impl->Code(webcc::kMultipleChoices);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MovedPermanently(const std::string& new_url)
{
impl->Code(webcc::kMovedPermanently);
impl->Header("Location", new_url);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Found(const std::string& new_url)
{
impl->Code(webcc::kFound);
impl->Header("Location", new_url);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::SeeOther(const std::string& new_url)
{
impl->Code(webcc::kSeeOther);
impl->Header("Location", new_url);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NotModified()
{
impl->Code(webcc::kNotModified);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UseProxy()
{
impl->Code(webcc::kUseProxy);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::__Unused()
{
impl->Code(webcc::k__Unused);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::TemporaryRedirect(const std::string& new_url)
{
impl->Code(webcc::kTemporaryRedirect);
impl->Header("Location", new_url);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::PermanentRedirect(const std::string& new_url)
{
impl->Code(webcc::kPermanentRedirect);
impl->Header("Location", new_url);
EmptyBody();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::BadRequest()
{
impl->BadRequest();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Unauthorized()
{
impl->Code(webcc::kUnauthorized);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::PaymentRequired()
{
impl->Code(webcc::kPaymentRequired);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Forbidden()
{
impl->Code(webcc::kForbidden);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NotFound()
{
impl->NotFound();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MethodNotAllowed()
{
impl->Code(webcc::kMethodNotAllowed);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NotAcceptable()
{
impl->Code(webcc::kNotAcceptable);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ProxyAuthenticationRequired()
{
impl->Code(webcc::kProxyAuthenticationRequired);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RequestTimeout()
{
impl->Code(webcc::kRequestTimeout);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Conflict()
{
impl->Code(webcc::kConflict);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Gone()
{
impl->Code(webcc::kGone);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::LengthRequired()
{
impl->Code(webcc::kLengthRequired);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::PreconditionFailed()
{
impl->Code(webcc::kPreconditionFailed);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ContentTooLarge()
{
impl->Code(webcc::kContentTooLarge);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::URITooLong()
{
impl->Code(webcc::kURITooLong);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UnsupportedMediaType()
{
impl->Code(webcc::kUnsupportedMediaType);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RangeNotSatisfiable()
{
impl->Code(webcc::kRangeNotSatisfiable);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ExpectationFailed()
{
impl->Code(webcc::kExpectationFailed);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::IamATeapot()
{
impl->Code(webcc::kIamATeapot);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MisdirectedRequest()
{
impl->Code(webcc::kMisdirectedRequest);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UnprocessableContent()
{
impl->Code(webcc::kUnprocessableContent);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Locked()
{
impl->Code(webcc::kLocked);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::FailedDependency()
{
impl->Code(webcc::kFailedDependency);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::TooEarly()
{
impl->Code(webcc::kTooEarly);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UpgradeRequired(const std::string& protocol)
{
impl->Code(webcc::kMovedPermanently);
impl->Header("Upgrade", protocol);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::PreconditionRequired()
{
impl->Code(webcc::kPreconditionRequired);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::TooManyRequests()
{
impl->Code(webcc::kTooManyRequests);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RequestHeaderFieldsTooLarge()
{
impl->Code(webcc::kRequestHeaderFieldsTooLarge);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UnavailableForLegalReasons()
{
impl->Code(webcc::kUnavailableForLegalReasons);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::InternalServerError()
{
impl->InternalServerError();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NotImplemented()
{
impl->NotImplemented();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::BadGateway()
{
impl->Code(webcc::kBadGateway);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ServiceUnavailable()
{
impl->ServiceUnavailable();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::GatewayTimeout()
{
impl->Code(webcc::kGatewayTimeout);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::HTTPVersionNotSupported()
{
impl->Code(webcc::kHTTPVersionNotSupported);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::VariantAlsoNegotiates()
{
impl->Code(webcc::kVariantAlsoNegotiates);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::InsufficientStorage()
{
impl->Code(webcc::kInsufficientStorage);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::LoopDetected()
{
impl->Code(webcc::kLoopDetected);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NotExtended()
{
impl->Code(webcc::kNotExtended);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::NetworkAuthenticationRequired()
{
impl->Code(webcc::kNetworkAuthenticationRequired);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RequestFormatError()
{
impl->Code(webcc::k_uRequestFormatError);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RequestInvalid()
{
impl->Code(webcc::k_uRequestInvalid);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::URLOutOfRange()
{
impl->Code(webcc::k_uURLOutOfRange);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::InvalidRequestHost()
{
impl->Code(webcc::k_uInvalidRequestHost);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::IPBlocked()
{
impl->Code(webcc::k_uIPBlocked);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::IllegalUpload()
{
impl->Code(webcc::k_uIllegalUpload);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::FileFormatError()
{
impl->Code(webcc::k_uFileFormatError);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::InvalidFile()
{
impl->Code(webcc::k_uInvalidFile);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::FileTooLarge()
{
impl->Code(webcc::k_uFileTooLarge);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::RPSLimited()
{
impl->Code(webcc::k_uRPSLimited);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::SubProcessFalied()
{
impl->Code(webcc::k_uSubProcessFalied);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ServerHateYou()
{
impl->Code(webcc::k_uServerHateYou);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::DoSFound()
{
impl->Code(webcc::k_uDoSFound);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::DDoSFound()
{
impl->Code(webcc::k_uDDoSFound);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::UnknownServerError()
{
impl->Code(webcc::k_uUnknownServerError);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Utf8()
{
impl->Utf8();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Json()
{
impl->Json();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Date()
{
impl->Header("Date", EncodeHTTPTime(nullptr));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::GZip()
{
impl->Gzip();
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::DenyFraming()
{
impl->framing_override = true;
impl->allow_framing = false;
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::AllowFraming()
{
impl->framing_override = true;
impl->allow_framing = true;
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Code(int code)
{
impl->Code(static_cast<webcc::Status>(code));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Charset(std::string_view charset)
{
impl->Charset(charset);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::SetCookie(std::string_view cookie)
{
impl->Header("Set-Cookie", cookie);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::MediaType(std::string_view media_type)
{
impl->MediaType(media_type);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Header(std::string_view key, std::string_view value)
{
impl->Header(key, value);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::EmptyBody()
{
impl->empty_body = true;
impl->Body("");
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Body(int data)
{
impl->Body(std::to_string(data));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Body(std::string&& data)
{
impl->Body(data);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Body(const Json::Value& data)
{
Json::FastWriter writer;
impl->Json();
impl->Body(writer.write(data));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::Body(const std::string& data)
{
impl->Body(data);
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ErrorPage()
{
impl->Body(EncodeErrorPage(impl->GetCode()));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::ErrorPage(std::string_view error_page)
{
impl->Body(error_page.data());
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::File(const std::filesystem::path& path, bool infer_media_type, std::size_t chunk_size)
{
try
{
SCLOGF_INFO("Responsing With File: {}", path.generic_string());
if (FileExist(path.generic_string()))
impl->File(path, infer_media_type, chunk_size);
else
{
impl->Code(webcc::kNotFound);
SCLOGF_WARNING("File {} Does not Exist", path.generic_string());
}
}
catch (const webcc::Error& e)
{
SCLOGF_WARNING("webcc::Error During File Resopnse: {} ({})", e.code(), e.message());
impl->Code(webcc::kNotFound);
}
catch (...)
{
impl->Code(webcc::kInternalServerError);
}
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::FileForDownload(const std::filesystem::path& path, bool infer_media_type, std::size_t chunk_size)
{
try
{
SCLOGF_INFO("Responsing With File For Download: {}", path.generic_string());
if (FileExist(path.generic_string()))
{
impl->File(path, infer_media_type, chunk_size);
impl->Header("Content-Disposition", "attachment;filename=" + path.filename().string());
impl->Header("Content-Type", "application/octet-stream");
}
else
{
impl->Code(webcc::kNotFound);
SCLOGF_WARNING("File {} Does not Exist", path.generic_string());
}
}
catch (const webcc::Error& e)
{
SCLOGF_WARNING("webcc::Error During File-Downloading Resopnse: {} ({})", e.code(), e.message());
impl->Code(webcc::kNotFound);
}
catch (...)
{
impl->Code(webcc::kInternalServerError);
}
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::AutoCORS(uns::RequestPtr req)
{
auto Trim = [](const std::string& s) -> std::string
{
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);
};
if(req->GetImpl()->webcc_req->HasHeader(uns::cors::reqh_o))
{
std::string origin = Trim(req->GetImpl()->webcc_req->GetHeader(uns::cors::reqh_o));
if(GlobalCORSConfig.UrlValidate(origin))
{
std::string host = req->GetImpl()->webcc_req->HasHeader("Host") ? req->GetImpl()->webcc_req->GetHeader("Host") : "";
if(host.empty() || GlobalCORSConfig.HostValidate(host))
{
SCLOG_INFO("Applied CORS headers for origin=%s cred=%d", origin.c_str(), GlobalCORSConfig.AllowCookie() ? 1 : 0);
return CORS(origin);
}
else
{
SCLOG_WARNING("Actual request: Host not allowed: %s", host.c_str());
return Forbidden().Body(std::string("CORS ERROR"));
}
}
else
{
SCLOG_WARNING("Actual request: Origin not allowed: %s", origin.c_str());
return Forbidden().Body(std::string("CORS ERROR"));
}
}
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::CORS(const std::string& origin)
{
impl->Header("Vary", "Origin");
impl->Header(uns::cors::resh_acao, origin);
impl->Header(uns::cors::resh_acac, uns::btos(GlobalCORSConfig.AllowCookie()));
return *this;
}
uns::ResponseBuilder& uns::ResponseBuilder::CORS_Full(const std::string& origin, const std::set<std::string>& headers)
{
impl->Header("Vary", "Origin");
impl->Header(uns::cors::resh_acao, origin);
impl->Header(uns::cors::resh_acam, GlobalCORSConfig.GetValidateMethods());
impl->Header(uns::cors::resh_acah, GlobalCORSConfig.GetValidateHeaders(headers));
impl->Header(uns::cors::resh_acac, uns::btos(GlobalCORSConfig.AllowCookie()));
impl->Header(uns::cors::resh_acma, std::to_string(GlobalCORSConfig.GetMaxAge()));
return *this;
}