From 0f5f1f9cdcdb03901f6589cf98a78d387b2f7b54 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 13:11:33 +0100 Subject: Moved experimental/frontend.* to frontend/frontend.* --- zenserver/experimental/frontend.cpp | 119 ------------------------------------ zenserver/experimental/frontend.h | 24 -------- zenserver/frontend/frontend.cpp | 119 ++++++++++++++++++++++++++++++++++++ zenserver/frontend/frontend.h | 24 ++++++++ zenserver/zenserver.cpp | 2 +- 5 files changed, 144 insertions(+), 144 deletions(-) delete mode 100644 zenserver/experimental/frontend.cpp delete mode 100644 zenserver/experimental/frontend.h create mode 100644 zenserver/frontend/frontend.cpp create mode 100644 zenserver/frontend/frontend.h diff --git a/zenserver/experimental/frontend.cpp b/zenserver/experimental/frontend.cpp deleted file mode 100644 index 4bd3ec90a..000000000 --- a/zenserver/experimental/frontend.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "frontend.h" - -#include -#include - -namespace zen { - -namespace html { - - constexpr std::string_view Index = R"( - - - - - - - - - - -
-
-
-
-__________                  _________  __                           
-\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____  
-  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
- /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/ 
-/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
-        \/     \/      \/         \/                             \/ 
-				
-
-			
-
-
-
Z$:
-

-		
-
- - -)"; - -} // namespace html - -HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) -{ -} - -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; - - if (m_Directory.empty()) - { - Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, html::Index); - } - else - { - std::string_view Uri = Request.RelativeUri(); - std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri}; - std::filesystem::path AbsPath = m_Directory / RelPath; - - FileContents File = ReadFile(AbsPath); - - if (!File.ErrorCode) - { - // TODO: Map file extension to MIME type - Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]); - } - else - { - return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv); - } - } -} - -} // namespace zen diff --git a/zenserver/experimental/frontend.h b/zenserver/experimental/frontend.h deleted file mode 100644 index 2ae20e940..000000000 --- a/zenserver/experimental/frontend.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include - -#include - -namespace zen { - -class HttpFrontendService final : public zen::HttpService -{ -public: - HttpFrontendService(std::filesystem::path Directory); - virtual ~HttpFrontendService(); - - virtual const char* BaseUri() const override; - virtual void HandleRequest(zen::HttpServerRequest& Request) override; - -private: - std::filesystem::path m_Directory; -}; - -} // namespace zen diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp new file mode 100644 index 000000000..4bd3ec90a --- /dev/null +++ b/zenserver/frontend/frontend.cpp @@ -0,0 +1,119 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "frontend.h" + +#include +#include + +namespace zen { + +namespace html { + + constexpr std::string_view Index = R"( + + + + + + + + + + +
+
+
+
+__________                  _________  __                           
+\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____  
+  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
+ /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/ 
+/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
+        \/     \/      \/         \/                             \/ 
+				
+
+			
+
+
+
Z$:
+

+		
+
+ + +)"; + +} // namespace html + +HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) +{ +} + +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; + + if (m_Directory.empty()) + { + Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, html::Index); + } + else + { + std::string_view Uri = Request.RelativeUri(); + std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri}; + std::filesystem::path AbsPath = m_Directory / RelPath; + + FileContents File = ReadFile(AbsPath); + + if (!File.ErrorCode) + { + // TODO: Map file extension to MIME type + Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]); + } + else + { + return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv); + } + } +} + +} // namespace zen diff --git a/zenserver/frontend/frontend.h b/zenserver/frontend/frontend.h new file mode 100644 index 000000000..2ae20e940 --- /dev/null +++ b/zenserver/frontend/frontend.h @@ -0,0 +1,24 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include + +#include + +namespace zen { + +class HttpFrontendService final : public zen::HttpService +{ +public: + HttpFrontendService(std::filesystem::path Directory); + virtual ~HttpFrontendService(); + + virtual const char* BaseUri() const override; + virtual void HandleRequest(zen::HttpServerRequest& Request) override; + +private: + std::filesystem::path m_Directory; +}; + +} // namespace zen diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp index 576c88cb8..a684272c4 100644 --- a/zenserver/zenserver.cpp +++ b/zenserver/zenserver.cpp @@ -106,7 +106,7 @@ ZEN_THIRD_PARTY_INCLUDES_END #include "cache/structuredcachestore.h" #include "compute/apply.h" #include "diag/diagsvcs.h" -#include "experimental/frontend.h" +#include "frontend/frontend.h" #include "experimental/usnjournal.h" #include "monitoring/httpstats.h" #include "monitoring/httpstatus.h" -- cgit v1.2.3 From 7a13c8b127f3527f7e2a2c58840327029efb0e93 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 13:14:01 +0100 Subject: Set a default value for "Size" parameter of IoBuffer(OuterBuffer, ...) --- zencore/include/zencore/iobuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zencore/include/zencore/iobuffer.h b/zencore/include/zencore/iobuffer.h index 5dbbb95bf..2802194a6 100644 --- a/zencore/include/zencore/iobuffer.h +++ b/zencore/include/zencore/iobuffer.h @@ -336,7 +336,7 @@ public: /** Create a buffer which references a sequence of bytes inside another buffer */ - ZENCORE_API IoBuffer(const IoBuffer& OuterBuffer, size_t Offset, size_t SizeBytes); + ZENCORE_API IoBuffer(const IoBuffer& OuterBuffer, size_t Offset, size_t SizeBytes=~0ull); /** Create a buffer which references a range of bytes which we assume will live * for the entire life time. -- cgit v1.2.3 From 8e419a9cd6d96f4ff25f202a4f5e4e0f9725a53c Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 13:14:34 +0100 Subject: Marked IoBuffer::operator bool () method as explicit --- zencore/include/zencore/iobuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zencore/include/zencore/iobuffer.h b/zencore/include/zencore/iobuffer.h index 2802194a6..da68be516 100644 --- a/zencore/include/zencore/iobuffer.h +++ b/zencore/include/zencore/iobuffer.h @@ -351,7 +351,7 @@ public: ZENCORE_API IoBuffer(EFileTag, void* FileHandle, uint64_t ChunkFileOffset, uint64_t ChunkSize); ZENCORE_API IoBuffer(EBorrowedFileTag, void* FileHandle, uint64_t ChunkFileOffset, uint64_t ChunkSize); - inline operator bool() const { return !m_Core->IsNull(); } + inline explicit operator bool() const { return !m_Core->IsNull(); } inline operator MemoryView() const& { return MemoryView(m_Core->DataPointer(), m_Core->DataBytes()); } inline void MakeOwned() { return m_Core->MakeOwned(); } [[nodiscard]] inline bool IsOwned() const { return m_Core->IsOwned(); } -- cgit v1.2.3 From 00956b29d36a57d398a93ef828af660b401afa21 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 13:15:56 +0100 Subject: ZipFs class providing a rudimentary in-memory/memory-mapped file system --- zenserver/frontend/zipfs.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++ zenserver/frontend/zipfs.h | 24 ++++++ 2 files changed, 194 insertions(+) create mode 100644 zenserver/frontend/zipfs.cpp create mode 100644 zenserver/frontend/zipfs.h diff --git a/zenserver/frontend/zipfs.cpp b/zenserver/frontend/zipfs.cpp new file mode 100644 index 000000000..5fb9d0177 --- /dev/null +++ b/zenserver/frontend/zipfs.cpp @@ -0,0 +1,170 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zipfs.h" + +namespace zen { + +////////////////////////////////////////////////////////////////////////// +namespace { + +#if ZEN_COMPILER_MSC +# pragma warning(push) +# pragma warning(disable : 4200) +#endif + +using ZipInt16 = uint16_t; + +struct ZipInt32 +{ + operator uint32_t () const { return *(uint32_t*)Parts; } + uint16_t Parts[2]; +}; + +struct EocdRecord +{ + enum : uint32_t { + Magic = 0x0605'4b50, + }; + ZipInt32 Signature; + ZipInt16 ThisDiskIndex; + ZipInt16 CdStartDiskIndex; + ZipInt16 CdRecordThisDiskCount; + ZipInt16 CdRecordCount; + ZipInt32 CdSize; + ZipInt32 CdOffset; + ZipInt16 CommentSize; + char Comment[]; +}; + +struct CentralDirectoryRecord +{ + enum : uint32_t { + Magic = 0x0201'4b50, + }; + + ZipInt32 Signature; + ZipInt16 VersionMadeBy; + ZipInt16 VersionRequired; + ZipInt16 Flags; + ZipInt16 CompressionMethod; + ZipInt16 LastModTime; + ZipInt16 LastModDate; + ZipInt32 Crc32; + ZipInt32 CompressedSize; + ZipInt32 OriginalSize; + ZipInt16 FileNameLength; + ZipInt16 ExtraFieldLength; + ZipInt16 CommentLength; + ZipInt16 DiskIndex; + ZipInt16 InternalFileAttr; + ZipInt32 ExternalFileAttr; + ZipInt32 Offset; + char FileName[]; +}; + +struct LocalFileHeader +{ + enum : uint32_t { + Magic = 0x0304'4b50, + }; + + ZipInt32 Signature; + ZipInt16 VersionRequired; + ZipInt16 Flags; + ZipInt16 CompressionMethod; + ZipInt16 LastModTime; + ZipInt16 LastModDate; + ZipInt32 Crc32; + ZipInt32 CompressedSize; + ZipInt32 OriginalSize; + ZipInt16 FileNameLength; + ZipInt16 ExtraFieldLength; + char FileName[]; +}; + +#if ZEN_COMPILER_MSC +# pragma warning(pop) +#endif + +} // namespace + + + +////////////////////////////////////////////////////////////////////////// +ZipFs::ZipFs(IoBuffer&& Buffer) +{ + MemoryView View = Buffer.GetView(); + + uint8_t* Cursor = (uint8_t*)(View.GetData()) + View.GetSize(); + if (View.GetSize() < sizeof(EocdRecord)) + { + return; + } + + const auto* EocdCursor = (EocdRecord*)(Cursor - sizeof(EocdRecord)); + + // It is more correct to search backwards for EocdRecord::Magic as the + // comment can be of a variable length. But here we're not going to support + // zip files with comments. + if (EocdCursor->Signature != EocdRecord::Magic) + { + return; + } + + // Zip64 isn't supported either + if (EocdCursor->ThisDiskIndex == 0xffff) + { + return; + } + + Cursor -= View.GetSize(); + + const auto* CdCursor = (CentralDirectoryRecord*)(Cursor + EocdCursor->CdOffset); + for (int i = 0, n = EocdCursor->CdRecordCount; i < n; ++i) + { + const CentralDirectoryRecord& Cd = *CdCursor; + + bool Acceptable = true; + Acceptable &= (Cd.OriginalSize > 0); // has some content + Acceptable &= (Cd.CompressionMethod == 0); // is stored uncomrpessed + if (Acceptable) + { + const uint8_t* Lfh = Cursor + Cd.Offset; + if (uintptr_t(Lfh - Cursor) < View.GetSize()) + { + std::string_view FileName(Cd.FileName, Cd.FileNameLength); + m_Files.insert(std::make_pair(FileName, FileItem{Lfh, size_t(0)})); + } + } + + uint32_t ExtraBytes = Cd.FileNameLength + Cd.ExtraFieldLength + Cd.CommentLength; + CdCursor = (CentralDirectoryRecord*)(Cd.FileName + ExtraBytes); + } + + m_Buffer = std::move(Buffer); +} + +////////////////////////////////////////////////////////////////////////// +IoBuffer ZipFs::GetFile(const std::string_view& FileName) const +{ + FileMap::iterator Iter = m_Files.find(FileName); + if (Iter == m_Files.end()) + { + return{}; + } + + FileItem& Item = Iter->second; + if (Item.GetSize() > 0) + { + return IoBuffer(IoBuffer::Wrap, Item.GetData(), Item.GetSize()); + } + + const auto* Lfh = (LocalFileHeader*)(Item.GetData()); + Item = MemoryView( + Lfh->FileName + Lfh->FileNameLength + Lfh->ExtraFieldLength, + Lfh->OriginalSize + ); + return IoBuffer(IoBuffer::Wrap, Item.GetData(), Item.GetSize()); +} + +} // namespace zen diff --git a/zenserver/frontend/zipfs.h b/zenserver/frontend/zipfs.h new file mode 100644 index 000000000..a304e9ff5 --- /dev/null +++ b/zenserver/frontend/zipfs.h @@ -0,0 +1,24 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#include + +namespace zen { + +////////////////////////////////////////////////////////////////////////// +class ZipFs +{ +public: + ZipFs() = default; + ZipFs(IoBuffer&& Buffer); + IoBuffer GetFile(const std::string_view& FileName) const; + +private: + using FileItem = MemoryView; + using FileMap = std::unordered_map; + FileMap mutable m_Files; + IoBuffer m_Buffer; +}; + +} // namespace zen -- cgit v1.2.3 From 57c32ad6d2534e975fb62d8c36768c72f06877f6 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Tue, 1 Mar 2022 14:34:23 +0100 Subject: Added some new mime types; javascript, css, png and ico --- zencore/include/zencore/iobuffer.h | 13 +++++++++++++ zenhttp/httpserver.cpp | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/zencore/include/zencore/iobuffer.h b/zencore/include/zencore/iobuffer.h index da68be516..449236127 100644 --- a/zencore/include/zencore/iobuffer.h +++ b/zencore/include/zencore/iobuffer.h @@ -27,6 +27,10 @@ enum class ZenContentType : uint8_t kCompressedBinary = 7, kUnknownContentType = 8, kHTML = 9, + kJavaScript = 10, + kCSS = 11, + kPNG = 12, + kIcon = 13, kCOUNT }; @@ -58,6 +62,15 @@ ToString(ZenContentType ContentType) return "yaml"sv; case ZenContentType::kHTML: return "html"sv; + case ZenContentType::kJavaScript: + return "javascript"sv; + case ZenContentType::kCSS: + return "css"sv; + case ZenContentType::kPNG: + return "png"sv; + case ZenContentType::kIcon: + return "icon"sv; + } } diff --git a/zenhttp/httpserver.cpp b/zenhttp/httpserver.cpp index c8e11468e..710b6f356 100644 --- a/zenhttp/httpserver.cpp +++ b/zenhttp/httpserver.cpp @@ -60,6 +60,18 @@ MapContentTypeToString(HttpContentType ContentType) case HttpContentType::kHTML: return "text/html"sv; + + case HttpContentType::kJavaScript: + return "application/javascript"sv; + + case HttpContentType::kCSS: + return "text/css"sv; + + case HttpContentType::kPNG: + return "image/png"sv; + + case HttpContentType::kIcon: + return "image/x-icon"sv; } } @@ -76,6 +88,10 @@ static constinit uint32_t HashCompressedBinary = HashStringDjb2("application/x static constinit uint32_t HashJson = HashStringDjb2("json"sv); static constinit uint32_t HashYaml = HashStringDjb2("yaml"sv); static constinit uint32_t HashHtml = HashStringDjb2("text/html"sv); +static constinit uint32_t HashJavaScript = HashStringDjb2("application/javascript"sv); +static constinit uint32_t HashCss = HashStringDjb2("text/css"sv); +static constinit uint32_t HashPng = HashStringDjb2("image/png"sv); +static constinit uint32_t HashIcon = HashStringDjb2("image/x-icon"sv); std::once_flag InitContentTypeLookup; @@ -96,6 +112,10 @@ struct HashedTypeEntry {HashText, HttpContentType::kText}, {HashCompressedBinary, HttpContentType::kCompressedBinary}, {HashHtml, HttpContentType::kHTML}, + {HashJavaScript, HttpContentType::kJavaScript}, + {HashCss, HttpContentType::kCSS}, + {HashPng, HttpContentType::kPNG}, + {HashIcon, HttpContentType::kIcon}, // clang-format on }; -- cgit v1.2.3 From 6a955b38cfcc3a566b332f1d1fcc630f7a2f641f Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 09:19:25 +0100 Subject: Function to find .zip appended to a IoBuffer-wrapped executable file --- zenserver/frontend/frontend.cpp | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 4bd3ec90a..0611f068c 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -5,8 +5,105 @@ #include #include +ZEN_THIRD_PARTY_INCLUDES_START +#if ZEN_PLATFORM_WINDOWS +# include +#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 +# error "needs to be implemented!" +#endif // win/linux/mac + + if (!BinSize || BinSize > BinBuffer.GetSize()) + { + return {}; + } + + return IoBuffer(BinBuffer, BinSize); +} + namespace html { constexpr std::string_view Index = R"( -- cgit v1.2.3 From 27b351c2bac99d06f020f60fd859ecc93d4fd140 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 10:18:20 +0100 Subject: Implemented zip file locating for fat Mach-o binaries --- zenserver/frontend/frontend.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 0611f068c..17614f163 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -2,6 +2,7 @@ #include "frontend.h" +#include #include #include @@ -93,7 +94,38 @@ static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer) } } #elif ZEN_PLATFORM_MAC -# error "needs to be implemented!" + 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()) -- cgit v1.2.3 From 9dd7b73739486d4de3206c79f497231b286f53e6 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Wed, 2 Mar 2022 10:30:00 +0100 Subject: Serve the /dashboard endpoint from an embedded .zip file --- zenserver/frontend/frontend.cpp | 162 +++++++++++++++++++------------------ zenserver/frontend/frontend.h | 13 +-- zenserver/frontend/html/index.html | 59 ++++++++++++++ 3 files changed, 148 insertions(+), 86 deletions(-) create mode 100644 zenserver/frontend/html/index.html diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp index 17614f163..b87d7e313 100644 --- a/zenserver/frontend/frontend.cpp +++ b/zenserver/frontend/frontend.cpp @@ -136,113 +136,115 @@ static IoBuffer FindZipFsInBinary(const IoBuffer& BinBuffer) return IoBuffer(BinBuffer, BinSize); } -namespace html { - - constexpr std::string_view Index = R"( - - - - - - - - - - -
-
-
-
-__________                  _________  __                           
-\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____  
-  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
- /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/ 
-/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
-        \/     \/      \/         \/                             \/ 
-				
-
-			
-
-
-
Z$:
-

-		
-
- - -)"; - -} // namespace html - -HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory) +//////////////////////////////////////////////////////////////////////////////// +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; - if (m_Directory.empty()) + 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::OK, HttpContentType::kHTML, html::Index); + Request.WriteResponse(HttpResponseCode::Forbidden); + return; } - else + + // 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) { - std::string_view Uri = Request.RelativeUri(); - std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri}; - std::filesystem::path AbsPath = m_Directory / RelPath; + 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; + } - FileContents File = ReadFile(AbsPath); + 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) { - // TODO: Map file extension to MIME type - Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]); - } - else - { - return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv); + 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 diff --git a/zenserver/frontend/frontend.h b/zenserver/frontend/frontend.h index 2ae20e940..bf5298169 100644 --- a/zenserver/frontend/frontend.h +++ b/zenserver/frontend/frontend.h @@ -3,6 +3,7 @@ #pragma once #include +#include "zipfs.h" #include @@ -11,14 +12,14 @@ namespace zen { class HttpFrontendService final : public zen::HttpService { public: - HttpFrontendService(std::filesystem::path Directory); - virtual ~HttpFrontendService(); - - virtual const char* BaseUri() const override; - virtual void HandleRequest(zen::HttpServerRequest& Request) override; + HttpFrontendService(std::filesystem::path Directory); + virtual ~HttpFrontendService(); + virtual const char* BaseUri() const override; + virtual void HandleRequest(zen::HttpServerRequest& Request) override; private: - std::filesystem::path m_Directory; + ZipFs m_ZipFs; + std::filesystem::path m_Directory; }; } // namespace zen diff --git a/zenserver/frontend/html/index.html b/zenserver/frontend/html/index.html new file mode 100644 index 000000000..252ee621e --- /dev/null +++ b/zenserver/frontend/html/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + +
+
+
+
+__________                  _________  __
+\____    /  ____    ____   /   _____/_/  |_   ____  _______   ____
+  /     / _/ __ \  /    \  \_____  \ \   __\ /  _ \ \_  __ \_/ __ \
+ /     /_ \  ___/ |   |  \ /        \ |  |  (  <_> ) |  | \/\  ___/
+/_______ \ \___  >|___|  //_______  / |__|   \____/  |__|    \___  >
+        \/     \/      \/         \/                             \/
+				
+
+			
+
+
+
Z$:
+

+		
+
+ + -- cgit v1.2.3 From f8170856b601089ba89a69047ccbcd57f16b872b Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Thu, 3 Mar 2022 13:23:42 +0100 Subject: Allow nil arguments when bundle.lua launches processes --- scripts/bundle.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/bundle.lua b/scripts/bundle.lua index 90fb13657..e2e43cd87 100644 --- a/scripts/bundle.lua +++ b/scripts/bundle.lua @@ -2,8 +2,15 @@ -------------------------------------------------------------------------------- local function _exec(cmd, ...) - print("--", cmd, ...) - local ret = os.execv(cmd, {...}) + local args = {} + for _, arg in pairs({...}) do + if arg then + table.insert(args, arg) + end + end + + print("--", cmd, table.unpack(args)) + local ret = os.execv(cmd, args) print() return ret end -- cgit v1.2.3 From 7c4d93104f51cc4ad4f9c643a308aa0551f40cd9 Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Thu, 3 Mar 2022 13:24:45 +0100 Subject: Zip up the frontend content and append to executable when bundling --- scripts/bundle.lua | 93 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/scripts/bundle.lua b/scripts/bundle.lua index e2e43cd87..238a04bc1 100644 --- a/scripts/bundle.lua +++ b/scripts/bundle.lua @@ -42,23 +42,52 @@ local function _build(arch, debug, config_args) end -------------------------------------------------------------------------------- -local function _zip(zip_path, ...) +local function _zip(store_only, zip_path, ...) + -- Here's the rules; if len(...) is 1 and it is a dir then create a zip with + -- archive paths like this; + -- + -- glob(foo/bar/**) -> foo/bar/abc, foo/bar/dir/123 -> zip(abc, dir/123) + -- + -- Otherwise assume ... is file paths and add without leading directories; + -- + -- foo/abc, bar/123 -> zip(abc, 123) + + zip_path = path.absolute(zip_path) os.tryrm(zip_path) + local inputs = {...} + + local source_dir = nil + if #inputs == 1 and os.isdir(inputs[1]) then + source_dir = inputs[1] + end + import("detect.tools.find_7z") local cmd = find_7z() if cmd then - -- A ./ prefix makes 7z ignore the directory structure input_paths = {} - for _, input_path in ipairs({...}) do - input_path = path.relative(input_path, ".") - if input_path:sub(2,2) ~= ":" then - input_path = "./"..input_path + if source_dir then + -- Suffixing a directory path with a "/." will have 7z set the path + -- for archived files relative to that directory. + input_paths = { path.join(source_dir, ".") } + else + for _, input_path in pairs(inputs) do + -- If there is a "/./" anywhere in file paths then 7z drops all + -- directory information and just archives the file by name + input_path = path.relative(input_path, ".") + if input_path:sub(2,2) ~= ":" then + input_path = "./"..input_path + end + table.insert(input_paths, input_path) end - table.insert(input_paths, input_path) end - local ret = _exec("7z", "a", zip_path, table.unpack(input_paths)) + compression_level = "-mx9" + if store_only then + compression_level = "-mx0" + end + + local ret = _exec("7z", "a", "-r", compression_level, zip_path, table.unpack(input_paths)) if ret > 0 then raise("Received error from 7z") end @@ -69,10 +98,29 @@ local function _zip(zip_path, ...) import("detect.tools.find_zip") cmd = find_zip() if cmd then - local ret = _exec("zip", "--junk-paths", zip_path, ...) + local input_paths = inputs + local cwd = os.curdir() + if source_dir then + os.cd(source_dir) + input_paths = { "." } + end + + compression_level = "-9" + if store_only then + compression_level = "-0" + end + + local strip_leading_path = nil + if not source_dir then + strip_leading_path = "--junk-paths" + end + + local ret = _exec("zip", "-r", compression_level, strip_leading_path, zip_path, table.unpack(input_paths)) if ret > 0 then raise("Received error from zip") end + + os.cd(cwd) return end print("zip not found") @@ -96,6 +144,21 @@ local function _find_vcpkg_binary(triple, port, binary) return bin_path end +-------------------------------------------------------------------------------- +local function _append_content_zip(bin_path) + local zip_path = "build/frontend.zip" + local content_dir = "zenserver/frontend/html/" + _zip(true, zip_path, content_dir) + + zip_file = io.open(zip_path, "rb") + local zip_data = zip_file:read("*all") + zip_file:close() + + bin_file = io.open(bin_path, "ab") + bin_file:write(zip_data) + bin_file:close() +end + -------------------------------------------------------------------------------- local function main_windows() import("core.base.option") @@ -109,12 +172,14 @@ local function main_windows() _build("x64", false, config_args) + _append_content_zip("build/windows/x64/release/zenserver.exe") + local crashpad_handler_path = _find_vcpkg_binary( "x64-windows-static", "sentry-native", "crashpad_handler.exe") - _zip( + _zip(false, zip_path, "build/windows/x64/release/zenserver.exe", "build/windows/x64/release/zenserver.pdb", @@ -139,6 +204,8 @@ local function main_mac() raise("Failed creating univeral binary") end + _append_content_zip("build/macosx/universal/release/zenserver") + -- At the time of writing vcpkg does not support sentry-native on arm64. Once -- it does we can create a univeral binary for this. For now just bundle x64 local crashpad_handler_path = _find_vcpkg_binary( @@ -147,7 +214,7 @@ local function main_mac() "crashpad_handler") -- Zip - _zip( + _zip(false, "build/zenserver-macos.zip", "build/macosx/universal/release/zenserver", crashpad_handler_path) @@ -166,9 +233,11 @@ local function main_linux() "crashpad_handler") --]] + _append_content_zip("build/linux/x86_64/release/zenserver") + _exec("scripts/bundle_linux.sh") - _zip( + _zip(false, "build/zenserver-linux.zip", "build/appimage/zenserver", crashpad_handler_path) -- cgit v1.2.3 From c057edd241483139107365426873a6dcf825f1af Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Thu, 3 Mar 2022 13:25:09 +0100 Subject: Fixed typo --- scripts/bundle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bundle.lua b/scripts/bundle.lua index 238a04bc1..01268ab14 100644 --- a/scripts/bundle.lua +++ b/scripts/bundle.lua @@ -201,7 +201,7 @@ local function main_mac() "build/macosx/arm64/release/zenserver" ) if ret > 0 then - raise("Failed creating univeral binary") + raise("Failed creating universal binary") end _append_content_zip("build/macosx/universal/release/zenserver") -- cgit v1.2.3