// Copyright Epic Games, Inc. All Rights Reserved. #include "frontend.h" #include #include #include #include #include ZEN_THIRD_PARTY_INCLUDES_START #if ZEN_PLATFORM_WINDOWS # include #endif ZEN_THIRD_PARTY_INCLUDES_END static unsigned char gHtmlZipData[] = { #include }; namespace zen { //////////////////////////////////////////////////////////////////////////////// HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) { std::filesystem::path SelfPath = GetRunningExecutablePath(); // Locate a .zip file appended onto the end of this binary IoBuffer HtmlZipDataBuffer(IoBuffer::Wrap, gHtmlZipData, sizeof(gHtmlZipData) - 1); m_ZipFs = ZipFs(std::move(HtmlZipDataBuffer)); if (m_Directory.empty() && !m_ZipFs) { // Probe for development layout std::error_code ErrorCode; std::filesystem::path Path = SelfPath; while (Path.has_parent_path()) { std::filesystem::path ParentPath = Path.parent_path(); if (ParentPath == Path) { break; } if (std::filesystem::is_regular_file(ParentPath / "xmake.lua", ErrorCode)) { if (ErrorCode) { break; } std::filesystem::path HtmlDir = ParentPath / "src" / "zenserver" / "frontend" / "html"; if (std::filesystem::is_directory(HtmlDir, ErrorCode)) { m_Directory = HtmlDir; } break; } Path = ParentPath; } } if (m_ZipFs) { ZEN_INFO("front-end is served from embedded zip"); } else if (!m_Directory.empty()) { ZEN_INFO("front-end is served from '{}'", m_Directory); } else { ZEN_INFO("front-end is NOT AVAILABLE"); } } HttpFrontendService::~HttpFrontendService() { } const char* HttpFrontendService::BaseUri() const { return "/dashboard/"; // in order to use the root path we need to remove HttpAddUrlToUrlGroup in HttpSys.cpp } //////////////////////////////////////////////////////////////////////////////// void HttpFrontendService::HandleRequest(zen::HttpServerRequest& Request) { using namespace std::literals; std::string_view Uri = Request.RelativeUriWithExtension(); for (; Uri.length() > 0 && Uri[0] == '/'; Uri = Uri.substr(1)) ; if (Uri.empty()) { Uri = "index.html"sv; } // Dismiss if the URI contains .. anywhere to prevent arbitrary file reads if (Uri.find("..") != Uri.npos) { return Request.WriteResponse(HttpResponseCode::Forbidden); } // Map the file extension to a MIME type. To keep things constrained, only a // small subset of file extensions is allowed HttpContentType ContentType = HttpContentType::kUnknownContentType; if (const size_t DotIndex = Uri.rfind("."); DotIndex != Uri.npos) { const std::string_view DotExt = Uri.substr(DotIndex + 1); ContentType = ParseContentType(DotExt); } if (ContentType == HttpContentType::kUnknownContentType) { return Request.WriteResponse(HttpResponseCode::Forbidden); } // The given content directory overrides any zip-fs discovered in the binary if (!m_Directory.empty()) { FileContents File = ReadFile(m_Directory / Uri); if (!File.ErrorCode) { return Request.WriteResponse(HttpResponseCode::OK, ContentType, File.Data[0]); } } if (IoBuffer FileBuffer = m_ZipFs.GetFile(Uri)) { return Request.WriteResponse(HttpResponseCode::OK, ContentType, FileBuffer); } Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Not found"sv); } } // namespace zen