aboutsummaryrefslogtreecommitdiff
path: root/zenserver/frontend/frontend.cpp
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2022-03-02 10:30:00 +0100
committerMartin Ridgers <[email protected]>2022-03-15 13:45:20 +0100
commit9dd7b73739486d4de3206c79f497231b286f53e6 (patch)
tree3a14f83f2f2b8728e1a4f2263616b4654e75d326 /zenserver/frontend/frontend.cpp
parentImplemented zip file locating for fat Mach-o binaries (diff)
downloadzen-9dd7b73739486d4de3206c79f497231b286f53e6.tar.xz
zen-9dd7b73739486d4de3206c79f497231b286f53e6.zip
Serve the /dashboard endpoint from an embedded .zip file
Diffstat (limited to 'zenserver/frontend/frontend.cpp')
-rw-r--r--zenserver/frontend/frontend.cpp162
1 files changed, 82 insertions, 80 deletions
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"(
-<!DOCTYPE html>
-<html>
-<head>
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
-<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-skAcpIdS7UcVUC05LJ9Dxay8AXcDYfBJqt1CJ85S/CFujBsIzCIv+l9liuYLaMQ/" crossorigin="anonymous"></script>
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
-<style type="text/css">
-body {
- background-color: #fafafa;
-}
-</style>
-<script type="text/javascript">
- const getCacheStats = () => {
- const opts = { headers: { "Accept": "application/json" } };
- fetch("/stats/z$", opts)
- .then(response => {
- if (!response.ok) {
- throw Error(response.statusText);
- }
- return response.json();
- })
- .then(json => {
- document.getElementById("status").innerHTML = "connected"
- document.getElementById("stats").innerHTML = JSON.stringify(json, null, 4);
- })
- .catch(error => {
- document.getElementById("status").innerHTML = "disconnected"
- document.getElementById("stats").innerHTML = ""
- console.log(error);
- })
- .finally(() => {
- window.setTimeout(getCacheStats, 1000);
- });
- };
- getCacheStats();
-</script>
-</head>
-<body>
- <div class="container">
- <div class="row">
- <div class="text-center mt-5">
- <pre>
-__________ _________ __
-\____ / ____ ____ / _____/_/ |_ ____ _______ ____
- / / _/ __ \ / \ \_____ \ \ __\ / _ \ \_ __ \_/ __ \
- / /_ \ ___/ | | \ / \ | | ( <_> ) | | \/\ ___/
-/_______ \ \___ >|___| //_______ / |__| \____/ |__| \___ >
- \/ \/ \/ \/ \/
- </pre>
- <pre id="status"/>
- </div>
- </div>
- <div class="row">
- <pre class="mb-0">Z$:</pre>
- <pre id="stats"></pre>
- <div>
- </div>
-</body>
-</html>
-)";
-
-} // 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