#include "UNSResponseBuilder.h" #include "Global.h" #include #include "CORSConfig.h" #include "ServerLogger.h" #include "HTTPObjectsBridge.h" #include 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()) { } uns::ResponseBuilder::~ResponseBuilder() = default; uns::ResponseBuilder::ResponseBuilder(uns::RequestPtr req) { auto raw_webcc_req = req->GetImpl()->webcc_req; impl = std::make_unique(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(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(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(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); }; 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)) { SCLOGF_INFO("Applied CORS headers for origin={} cred={}", origin, GlobalCORSConfig.AllowCookie() ? 1 : 0); return CORS(origin); } else { SCLOGF_WARNING("Actual request: Host not allowed: {}", host); return Forbidden().Body(std::string("CORS ERROR")); } } else { SCLOGF_WARNING("Actual request: Origin not allowed: {}", origin); 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& 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; }