added suport for embedding resources and added serving /favicon.ico
Dieser Commit ist enthalten in:
Ursprung
8c2d89cdc6
Commit
de65905387
@ -203,6 +203,21 @@ if(DEFINED ENV{VCPKG_ROOT})
|
|||||||
include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
|
include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
### RESOURCES ###
|
||||||
|
|
||||||
|
include(cmake/add-resource.cmake)
|
||||||
|
|
||||||
|
add_resource(resource-shared
|
||||||
|
BASE_DIR ../
|
||||||
|
FILES
|
||||||
|
shared/gfx/appicon.ico
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(traintastic-server resource-shared)
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
add_dependencies(traintastic-server-test resource-shared)
|
||||||
|
endif()
|
||||||
|
|
||||||
### OPTIONS ###
|
### OPTIONS ###
|
||||||
|
|
||||||
if(NO_LOCALHOST_ONLY_SETTING)
|
if(NO_LOCALHOST_ONLY_SETTING)
|
||||||
|
|||||||
40
server/cmake/add-resource.cmake
Normale Datei
40
server/cmake/add-resource.cmake
Normale Datei
@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# This file is part of the traintastic source code.
|
||||||
|
# See <https://github.com/traintastic/traintastic>.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 Reinder Feenstra
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
function(add_resource TARGET_NAME)
|
||||||
|
cmake_parse_arguments(PARSE_ARG "" "BASE_DIR" "FILES" ${ARGN})
|
||||||
|
if(PARSE_ARG_BASE_DIR)
|
||||||
|
set(PARSE_ARG_BASE_DIR "${CMAKE_SOURCE_DIR}/${PARSE_ARG_BASE_DIR}")
|
||||||
|
else()
|
||||||
|
set(PARSE_ARG_BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
foreach(INPUT_FILE ${PARSE_ARG_FILES})
|
||||||
|
set(OUTPUT_FILE ${CMAKE_BINARY_DIR}/resource/${INPUT_FILE}.hpp)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${OUTPUT_FILE}
|
||||||
|
COMMAND Python3::Interpreter ${CMAKE_SOURCE_DIR}/cmake/generateresourceheader.py ${PARSE_ARG_BASE_DIR} ${INPUT_FILE} ${OUTPUT_FILE}
|
||||||
|
DEPENDS ${CMAKE_SOURCE_DIR}/cmake/generateresourceheader.py ${PARSE_ARG_BASE_DIR}/${INPUT_FILE}
|
||||||
|
COMMENT "Generating resource header resource/${INPUT_FILE}.hpp"
|
||||||
|
)
|
||||||
|
list(APPEND OUTPUT_HEADERS ${OUTPUT_FILE})
|
||||||
|
endforeach()
|
||||||
|
add_custom_target(${TARGET_NAME} ALL DEPENDS ${OUTPUT_HEADERS})
|
||||||
|
endfunction()
|
||||||
84
server/cmake/generateresourceheader.py
Normale Datei
84
server/cmake/generateresourceheader.py
Normale Datei
@ -0,0 +1,84 @@
|
|||||||
|
#
|
||||||
|
# This file is part of the traintastic source code.
|
||||||
|
# See <https://github.com/traintastic/traintastic>.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 Reinder Feenstra
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
if len(sys.argv) != 4:
|
||||||
|
print(f"Usage: {sys.argv[0]} <input dir> <input file> <output header>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
input_file = os.path.join(sys.argv[1], sys.argv[2])
|
||||||
|
input_file_ext = os.path.splitext(input_file)[1]
|
||||||
|
|
||||||
|
namespaces = ['Resource'] + os.path.dirname(sys.argv[2]).replace('../', '').split('/')
|
||||||
|
variable = re.sub(r'[\.]+','_', os.path.basename(sys.argv[2]).lower())
|
||||||
|
guard = '_'.join(namespaces).upper() + '_' + re.sub(r'[\.]+','_', os.path.basename(sys.argv[3]).upper())
|
||||||
|
|
||||||
|
is_binary = input_file_ext not in ['html', 'css', 'js']
|
||||||
|
|
||||||
|
with open(input_file, 'rb') as f:
|
||||||
|
contents = f.read()
|
||||||
|
|
||||||
|
if is_binary:
|
||||||
|
size = len(contents)
|
||||||
|
contents = ', '.join(['std::byte{' + str(by) + '}' for by in contents])
|
||||||
|
|
||||||
|
contents = '\n '.join(textwrap.wrap(contents, width=120))
|
||||||
|
|
||||||
|
with open(sys.argv[3], 'w') as f:
|
||||||
|
f.write(f'''// Auto-generated, do not edit, it will be overwritten
|
||||||
|
|
||||||
|
#ifndef {guard}
|
||||||
|
#define {guard}
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace {'::'.join(namespaces)}
|
||||||
|
{{
|
||||||
|
|
||||||
|
constexpr std::array<std::byte, {size}> {variable}{{{{
|
||||||
|
{contents}
|
||||||
|
}}}};
|
||||||
|
|
||||||
|
}}
|
||||||
|
#endif
|
||||||
|
''')
|
||||||
|
|
||||||
|
else: # text
|
||||||
|
with open(sys.argv[3], 'w') as f:
|
||||||
|
f.write(f'''// Auto-generated, do not edit, it will be overwritten
|
||||||
|
|
||||||
|
#ifndef {guard}
|
||||||
|
#define {guard}
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace {'::'.join(namespaces)}
|
||||||
|
{{
|
||||||
|
|
||||||
|
constexpr std::string_view {variable} = R"({contents})";
|
||||||
|
|
||||||
|
}}
|
||||||
|
#endif
|
||||||
|
''')
|
||||||
@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "server.hpp"
|
#include "server.hpp"
|
||||||
|
#include <boost/beast/http/buffer_body.hpp>
|
||||||
|
#include <tcb/span.hpp>
|
||||||
#include <traintastic/network/message.hpp>
|
#include <traintastic/network/message.hpp>
|
||||||
#include <version.hpp>
|
#include <version.hpp>
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
@ -29,6 +31,7 @@
|
|||||||
#include "../log/log.hpp"
|
#include "../log/log.hpp"
|
||||||
#include "../log/logmessageexception.hpp"
|
#include "../log/logmessageexception.hpp"
|
||||||
#include "../utils/setthreadname.hpp"
|
#include "../utils/setthreadname.hpp"
|
||||||
|
#include <resource/shared/gfx/appicon.ico.hpp>
|
||||||
|
|
||||||
#define IS_SERVER_THREAD (std::this_thread::get_id() == m_thread.get_id())
|
#define IS_SERVER_THREAD (std::this_thread::get_id() == m_thread.get_id())
|
||||||
|
|
||||||
@ -40,7 +43,8 @@ namespace
|
|||||||
|
|
||||||
static constexpr std::string_view serverHeader{"Traintastic-server/" TRAINTASTIC_VERSION_FULL};
|
static constexpr std::string_view serverHeader{"Traintastic-server/" TRAINTASTIC_VERSION_FULL};
|
||||||
static constexpr std::string_view contentTypeTextPlain{"text/plain"};
|
static constexpr std::string_view contentTypeTextPlain{"text/plain"};
|
||||||
static constexpr std::string_view contentTypeTextHtml{"text/Html"};
|
static constexpr std::string_view contentTypeTextHtml{"text/html"};
|
||||||
|
static constexpr std::string_view contentTypeImageXIcon{"image/x-icon"};
|
||||||
|
|
||||||
http::message_generator notFound(const http::request<http::string_body>& request)
|
http::message_generator notFound(const http::request<http::string_body>& request)
|
||||||
{
|
{
|
||||||
@ -70,6 +74,30 @@ http::message_generator methodNotAllowed(const http::request<http::string_body>&
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http::message_generator binary(const http::request<http::string_body>& request, std::string_view contentType, tcb::span<const std::byte> body)
|
||||||
|
{
|
||||||
|
if(request.method() != http::verb::get && request.method() != http::verb::head)
|
||||||
|
{
|
||||||
|
return methodNotAllowed(request, {http::verb::get, http::verb::head});
|
||||||
|
}
|
||||||
|
http::response<http::buffer_body> response{http::status::ok, request.version()};
|
||||||
|
response.set(http::field::server, serverHeader);
|
||||||
|
response.set(http::field::content_type, contentType);
|
||||||
|
response.keep_alive(request.keep_alive());
|
||||||
|
if(request.method() == http::verb::head)
|
||||||
|
{
|
||||||
|
response.content_length(body.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.body().data = const_cast<std::byte*>(body.data());
|
||||||
|
response.body().size = body.size();
|
||||||
|
}
|
||||||
|
response.body().more = false;
|
||||||
|
response.prepare_payload();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
http::message_generator text(const http::request<http::string_body>& request, std::string_view contentType, std::string_view body)
|
http::message_generator text(const http::request<http::string_body>& request, std::string_view contentType, std::string_view body)
|
||||||
{
|
{
|
||||||
if(request.method() != http::verb::get && request.method() != http::verb::head)
|
if(request.method() != http::verb::get && request.method() != http::verb::head)
|
||||||
@ -288,10 +316,11 @@ void Server::doAccept()
|
|||||||
|
|
||||||
http::message_generator Server::handleHTTPRequest(http::request<http::string_body>&& request)
|
http::message_generator Server::handleHTTPRequest(http::request<http::string_body>&& request)
|
||||||
{
|
{
|
||||||
if(request.target() == "/")
|
const auto target = request.target();
|
||||||
|
if(target == "/")
|
||||||
{
|
{
|
||||||
return textHtml(request,
|
return textHtml(request,
|
||||||
"<!doctype html>"
|
"<!DOCTYPE html>"
|
||||||
"<html>"
|
"<html>"
|
||||||
"<head>"
|
"<head>"
|
||||||
"<meta charset=\"utf-8\">"
|
"<meta charset=\"utf-8\">"
|
||||||
@ -299,11 +328,15 @@ http::message_generator Server::handleHTTPRequest(http::request<http::string_bod
|
|||||||
"<title>Traintastic v" TRAINTASTIC_VERSION_FULL "</title>"
|
"<title>Traintastic v" TRAINTASTIC_VERSION_FULL "</title>"
|
||||||
"</head>"
|
"</head>"
|
||||||
"<body>"
|
"<body>"
|
||||||
"<h1>Traintastic v" TRAINTASTIC_VERSION_FULL "</h1>"
|
"<h1>Traintastic <small>v" TRAINTASTIC_VERSION_FULL "</small></h1>"
|
||||||
"</body>"
|
"</body>"
|
||||||
"</html>");
|
"</html>");
|
||||||
}
|
}
|
||||||
if(request.target() == "/version")
|
if(target == "/favicon.ico")
|
||||||
|
{
|
||||||
|
return binary(request, contentTypeImageXIcon, Resource::shared::gfx::appicon_ico);
|
||||||
|
}
|
||||||
|
if(target == "/version")
|
||||||
{
|
{
|
||||||
return textPlain(request, TRAINTASTIC_VERSION_FULL);
|
return textPlain(request, TRAINTASTIC_VERSION_FULL);
|
||||||
}
|
}
|
||||||
|
|||||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren