aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/frontend.cpp
blob: 149d97924b6f3aac151565755243e5d7511d2a80 (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
// 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 {

////////////////////////////////////////////////////////////////////////////////
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);
	m_ZipFs				= ZipFs(std::move(SelfBuffer));

#if ZEN_BUILD_DEBUG
	if (!Directory.empty())
	{
		return;
	}

	std::error_code ErrorCode;
	auto			Path = SelfPath;
	while (Path.has_parent_path())
	{
		auto ParentPath = Path.parent_path();
		if (ParentPath == Path)
		{
			break;
		}
		if (std::filesystem::is_regular_file(ParentPath / "xmake.lua", ErrorCode))
		{
			if (ErrorCode)
			{
				break;
			}

			auto HtmlDir = ParentPath / "zenserver" / "frontend" / "html";
			if (std::filesystem::is_directory(HtmlDir, ErrorCode))
			{
				m_Directory = HtmlDir;
			}
			break;
		}
		Path = ParentPath;
	};
#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.RelativeUriWithExtension();
	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)
	{
		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