aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/frontend.cpp
blob: 9bc408711951bcb9764a760a2fb9e8dc0195581e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright Epic Games, Inc. All Rights Reserved.

#include "frontend.h"

#include <zencore/endian.h>
#include <zencore/filesystem.h>
#include <zencore/fmtutils.h>
#include <zencore/logging.h>
#include <zencore/string.h>

ZEN_THIRD_PARTY_INCLUDES_START
#if ZEN_PLATFORM_WINDOWS
#	include <Windows.h>
#endif
ZEN_THIRD_PARTY_INCLUDES_END

static unsigned char gHtmlZipData[] = {
#include <html.zip.h>
};
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