aboutsummaryrefslogtreecommitdiff
path: root/zenserver/frontend/frontend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zenserver/frontend/frontend.cpp')
-rw-r--r--zenserver/frontend/frontend.cpp250
1 files changed, 250 insertions, 0 deletions
diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp
new file mode 100644
index 000000000..b87d7e313
--- /dev/null
+++ b/zenserver/frontend/frontend.cpp
@@ -0,0 +1,250 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "frontend.h"
+
+#include <zencore/endian.h>
+#include <zencore/filesystem.h>
+#include <zencore/string.h>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#if ZEN_PLATFORM_WINDOWS
+# include <Windows.h>
+#endif
+ZEN_THIRD_PARTY_INCLUDES_END
+
+namespace zen {
+
+//////////////////////////////////////////////////////////////////////////
+static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer)
+{
+ if (BinBuffer.GetSize() < 4)
+ {
+ return {};
+ }
+
+ uintptr_t Cursor = uintptr_t(BinBuffer.GetData());
+ size_t BinSize = 0;
+
+ uint32_t Magic = *(uint32_t*)(BinBuffer.GetData());
+#if ZEN_PLATFORM_LINUX
+ if (Magic == 0x464c457f)
+ {
+ struct Elf64Header
+ {
+ char Ident[16];
+ uint16_t Type;
+ uint16_t Machine;
+ uint32_t Version;
+ uint64_t Entry;
+ uint64_t ProgHeaderOffset;
+ uint64_t SectionHeaderOffset;
+ uint32_t Flags;
+ uint16_t EhSize;
+ uint16_t ProgHeaderEntrySize;
+ uint16_t ProgHeaderCount;
+ uint16_t SectionHeaderEntrySize;
+ uint16_t SectionHeaderCount;
+ uint16_t SectionStringIndex;
+ };
+
+ struct SectionHeader
+ {
+ uint32_t NameIndex;
+ uint32_t Type;
+ uint64_t Flags;
+ uint64_t Address;
+ uint64_t Offset;
+ uint64_t Size;
+ uint64_t _Other[3];
+ };
+
+ const auto* Elf = (Elf64Header*)Cursor;
+ if (Elf->Ident[4] != 0x02) // Elf64
+ {
+ return {};
+ }
+
+ const auto* Section = (SectionHeader*)(Cursor + Elf->SectionHeaderOffset);
+
+ /*
+ size_t BinSize = 0;
+ for (int i = 0, n = Elf->SectionHeaderCount; i < n; ++i, ++Section)
+ {
+ uint32_t SectionEnd = Section->Offset + Section->Size;
+ BinSize = (SectionEnd > BinSize) ? SectionEnd : BinSize;
+ }
+ */
+
+ // What if the section headers aren't the last thing in the fiile though?
+ BinSize = Elf->SectionHeaderEntrySize;
+ BinSize *= Elf->SectionHeaderCount;
+ BinSize += Elf->SectionHeaderOffset;
+ }
+#elif ZEN_PLATFORM_WINDOWS
+ if ((Magic & 0xffff) == 0x5a4d)
+ {
+ const auto* Dos = (IMAGE_DOS_HEADER*)Cursor;
+ const auto* Nt = (IMAGE_NT_HEADERS64*)(Cursor + Dos->e_lfanew);
+ const auto* Section = (IMAGE_SECTION_HEADER*)(uintptr_t(&Nt->OptionalHeader) + Nt->FileHeader.SizeOfOptionalHeader);
+
+ for (int i = 0, n = Nt->FileHeader.NumberOfSections; i < n; ++i, ++Section)
+ {
+ uint32_t SectionEnd = Section->PointerToRawData + Section->SizeOfRawData;
+ BinSize = (SectionEnd > BinSize) ? SectionEnd : BinSize;
+ }
+ }
+#elif ZEN_PLATFORM_MAC
+ if (Magic == 0xbebafeca)
+ {
+ struct MachInt32
+ {
+ operator uint32_t () const { return ByteSwap(Value); }
+ uint32_t Value;
+ };
+
+ struct MachFatArch
+ {
+ MachInt32 CpuType;
+ MachInt32 SubType;
+ MachInt32 Offset;
+ MachInt32 Size;
+ MachInt32 Alignment;
+ };
+
+ struct MachFatHeader
+ {
+ uint32_t Magic;
+ MachInt32 NumArchs;
+ MachFatArch Archs[];
+ };
+
+ const auto* Header = (MachFatHeader*)Cursor;
+ for (int i = 0, n = Header->NumArchs; i < n; ++i)
+ {
+ const MachFatArch* Arch = Header->Archs + i;
+ uint32_t ArchEnd = Arch->Offset + Arch->Size;
+ BinSize = (ArchEnd > BinSize) ? ArchEnd : BinSize;
+ }
+ }
+#endif // win/linux/mac
+
+ if (!BinSize || BinSize > BinBuffer.GetSize())
+ {
+ return {};
+ }
+
+ return IoBuffer(BinBuffer, BinSize);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+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 SelfBuffer = IoBufferBuilder::MakeFromFile(SelfPath);
+ IoBuffer SelfTailBuffer = FindZipFsInBinary(SelfBuffer);
+ if (SelfTailBuffer)
+ {
+ m_ZipFs = ZipFs(std::move(SelfTailBuffer));
+ }
+
+#if ZEN_BUILD_DEBUG
+ if (!Directory.empty())
+ {
+ return;
+ }
+
+ std::error_code ErrorCode;
+ for (auto Path = SelfPath.parent_path(); !Path.empty(); Path = Path.parent_path())
+ {
+ if (!std::filesystem::is_regular_file(Path / "xmake.lua", ErrorCode))
+ {
+ continue;
+ }
+
+ auto HtmlDir = (Path / __FILE__).parent_path() / "html";
+ if (std::filesystem::is_directory(HtmlDir, ErrorCode))
+ {
+ m_Directory = HtmlDir;
+ }
+ break;
+ }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+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.RelativeUri();
+ for (; 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)
+ {
+ Request.WriteResponse(HttpResponseCode::Forbidden);
+ return;
+ }
+
+ // Map the file extension to a MIME type. To keep things constrained, only a
+ // small subset of file extensions is allowed.
+ HttpContentType ContentType = HttpContentType::kCOUNT;
+ size_t DotIndex = Uri.rfind(".");
+ if (DotIndex != Uri.npos)
+ {
+ const std::string_view DotExt = Uri.substr(DotIndex);
+ if (DotExt == ".html") ContentType = HttpContentType::kHTML;
+ else if (DotExt == ".js") ContentType = HttpContentType::kJSON;
+ else if (DotExt == ".css") ContentType = HttpContentType::kCSS;
+ else if (DotExt == ".png") ContentType = HttpContentType::kPNG;
+ else if (DotExt == ".ico") ContentType = HttpContentType::kIcon;
+ }
+
+ if (ContentType == HttpContentType::kCOUNT)
+ {
+ Request.WriteResponse(HttpResponseCode::Forbidden);
+ return;
+ }
+
+ // 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)
+ {
+ Request.WriteResponse(HttpResponseCode::OK, ContentType, File.Data[0]);
+ return;
+ }
+ }
+
+ IoBuffer FileBuffer = m_ZipFs.GetFile(Uri);
+ if (FileBuffer)
+ {
+ Request.WriteResponse(HttpResponseCode::OK, ContentType, FileBuffer);
+ return;
+ }
+
+ Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Not found"sv);
+}
+
+} // namespace zen