Ui serving
This commit is contained in:
@@ -85,6 +85,7 @@ HttpControlServer::~HttpControlServer()
|
||||
}
|
||||
|
||||
bool HttpControlServer::Start(
|
||||
const std::filesystem::path& uiRoot,
|
||||
const std::filesystem::path& docsRoot,
|
||||
HttpControlServerConfig config,
|
||||
HttpControlServerCallbacks callbacks,
|
||||
@@ -96,6 +97,7 @@ bool HttpControlServer::Start(
|
||||
return false;
|
||||
mWinsockStarted = true;
|
||||
|
||||
mUiRoot = uiRoot;
|
||||
mDocsRoot = docsRoot;
|
||||
mConfig = config;
|
||||
mCallbacks = std::move(callbacks);
|
||||
@@ -173,6 +175,12 @@ void HttpControlServer::SetCallbacksForTest(HttpControlServerCallbacks callbacks
|
||||
mCallbacks = std::move(callbacks);
|
||||
}
|
||||
|
||||
void HttpControlServer::SetRootsForTest(const std::filesystem::path& uiRoot, const std::filesystem::path& docsRoot)
|
||||
{
|
||||
mUiRoot = uiRoot;
|
||||
mDocsRoot = docsRoot;
|
||||
}
|
||||
|
||||
void HttpControlServer::ThreadMain()
|
||||
{
|
||||
while (mRunning.load(std::memory_order_acquire))
|
||||
@@ -241,8 +249,16 @@ HttpControlServer::HttpResponse HttpControlServer::ServeGet(const HttpRequest& r
|
||||
if (request.path == "/docs" || request.path == "/docs/")
|
||||
return ServeSwaggerDocs();
|
||||
if (request.path == "/" || request.path == "/index.html")
|
||||
return TextResponse("200 OK", "RenderCadenceCompositor control server");
|
||||
return TextResponse("404 Not Found", "Not Found");
|
||||
return ServeUiAsset("index.html");
|
||||
if (request.path.rfind("/assets/", 0) == 0)
|
||||
return ServeUiAsset(request.path.substr(1));
|
||||
if (request.path.size() > 1)
|
||||
{
|
||||
const HttpResponse asset = ServeUiAsset(request.path.substr(1));
|
||||
if (asset.status != "404 Not Found")
|
||||
return asset;
|
||||
}
|
||||
return ServeUiAsset("index.html");
|
||||
}
|
||||
|
||||
HttpControlServer::HttpResponse HttpControlServer::ServePost(const HttpRequest& request) const
|
||||
@@ -279,6 +295,22 @@ HttpControlServer::HttpResponse HttpControlServer::ServeSwaggerDocs() const
|
||||
return { "200 OK", "text/html", html.str() };
|
||||
}
|
||||
|
||||
HttpControlServer::HttpResponse HttpControlServer::ServeUiAsset(const std::string& relativePath) const
|
||||
{
|
||||
if (mUiRoot.empty())
|
||||
return TextResponse("404 Not Found", "UI root is not configured");
|
||||
|
||||
const std::filesystem::path sanitizedPath = std::filesystem::path(relativePath).lexically_normal();
|
||||
if (!IsSafeRelativePath(sanitizedPath))
|
||||
return TextResponse("404 Not Found", "Not Found");
|
||||
|
||||
const std::filesystem::path path = mUiRoot / sanitizedPath;
|
||||
const std::string body = LoadTextFile(path);
|
||||
if (body.empty())
|
||||
return TextResponse("404 Not Found", "Not Found");
|
||||
return { "200 OK", GuessContentType(path), body };
|
||||
}
|
||||
|
||||
std::string HttpControlServer::LoadTextFile(const std::filesystem::path& path) const
|
||||
{
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
@@ -300,6 +332,11 @@ HttpControlServer::HttpResponse HttpControlServer::TextResponse(const std::strin
|
||||
return { status, "text/plain", body };
|
||||
}
|
||||
|
||||
HttpControlServer::HttpResponse HttpControlServer::HtmlResponse(const std::string& status, const std::string& body)
|
||||
{
|
||||
return { status, "text/html", body };
|
||||
}
|
||||
|
||||
std::string HttpControlServer::ActionResponse(bool ok, const std::string& error)
|
||||
{
|
||||
JsonWriter writer;
|
||||
@@ -318,9 +355,38 @@ std::string HttpControlServer::GuessContentType(const std::filesystem::path& pat
|
||||
return "application/yaml";
|
||||
if (extension == ".json")
|
||||
return "application/json";
|
||||
if (extension == ".js" || extension == ".mjs")
|
||||
return "text/javascript";
|
||||
if (extension == ".css")
|
||||
return "text/css";
|
||||
if (extension == ".html" || extension == ".htm")
|
||||
return "text/html";
|
||||
if (extension == ".svg")
|
||||
return "image/svg+xml";
|
||||
if (extension == ".png")
|
||||
return "image/png";
|
||||
if (extension == ".jpg" || extension == ".jpeg")
|
||||
return "image/jpeg";
|
||||
if (extension == ".ico")
|
||||
return "image/x-icon";
|
||||
if (extension == ".map")
|
||||
return "application/json";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
bool HttpControlServer::IsSafeRelativePath(const std::filesystem::path& path)
|
||||
{
|
||||
if (path.empty() || path.is_absolute())
|
||||
return false;
|
||||
|
||||
for (const std::filesystem::path& part : path)
|
||||
{
|
||||
if (part == "..")
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string HttpControlServer::ToLower(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char character) {
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
HttpControlServer& operator=(const HttpControlServer&) = delete;
|
||||
|
||||
bool Start(
|
||||
const std::filesystem::path& uiRoot,
|
||||
const std::filesystem::path& docsRoot,
|
||||
HttpControlServerConfig config,
|
||||
HttpControlServerCallbacks callbacks,
|
||||
@@ -80,6 +81,7 @@ public:
|
||||
unsigned short Port() const { return mPort; }
|
||||
|
||||
void SetCallbacksForTest(HttpControlServerCallbacks callbacks);
|
||||
void SetRootsForTest(const std::filesystem::path& uiRoot, const std::filesystem::path& docsRoot);
|
||||
HttpResponse RouteRequestForTest(const HttpRequest& request) const;
|
||||
|
||||
static bool ParseHttpRequest(const std::string& rawRequest, HttpRequest& request);
|
||||
@@ -94,14 +96,18 @@ private:
|
||||
HttpResponse ServePost(const HttpRequest& request) const;
|
||||
HttpResponse ServeOpenApiSpec() const;
|
||||
HttpResponse ServeSwaggerDocs() const;
|
||||
HttpResponse ServeUiAsset(const std::string& relativePath) const;
|
||||
std::string LoadTextFile(const std::filesystem::path& path) const;
|
||||
|
||||
static HttpResponse JsonResponse(const std::string& status, const std::string& body);
|
||||
static HttpResponse TextResponse(const std::string& status, const std::string& body);
|
||||
static HttpResponse HtmlResponse(const std::string& status, const std::string& body);
|
||||
static std::string ActionResponse(bool ok, const std::string& error = std::string());
|
||||
static std::string GuessContentType(const std::filesystem::path& path);
|
||||
static bool IsSafeRelativePath(const std::filesystem::path& path);
|
||||
static std::string ToLower(std::string text);
|
||||
|
||||
std::filesystem::path mUiRoot;
|
||||
std::filesystem::path mDocsRoot;
|
||||
HttpControlServerConfig mConfig;
|
||||
HttpControlServerCallbacks mCallbacks;
|
||||
|
||||
Reference in New Issue
Block a user