aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-04-24 15:48:55 +0200
committerGitHub <[email protected]>2023-04-24 15:48:55 +0200
commit1a345a720f728438d0884c3ade4aa37d5c994701 (patch)
tree8c06d8142b9774913c5de8a661080c25e1e74b6c
parent0.2.5-pre4 (diff)
downloadzen-1a345a720f728438d0884c3ade4aa37d5c994701.tar.xz
zen-1a345a720f728438d0884c3ade4aa37d5c994701.zip
fixed dashboard file serving bug (#255)
a recent change which introduced support for specifying accept: implicitly via the file extension in the URI caused fallout in the dashboard which would fail to serve any content because the extension was stripped from the RelativeUri accessor. This change fixes that by retaining a copy of the Uri string view which includes the suffix additionally, in order to test this change with both asio/http.sys paths I made the path used for all tests configurable in zenserver-test which involved pulling in a change from sb/proto which makes testing configuration a bit more flexible
-rw-r--r--xmake.lua2
-rw-r--r--zen/zen.cpp2
-rw-r--r--zencore/include/zencore/testing.h54
-rw-r--r--zencore/testing.cpp52
-rw-r--r--zenhttp/httpasio.cpp5
-rw-r--r--zenhttp/httpsys.cpp13
-rw-r--r--zenhttp/include/zenhttp/httpserver.h2
-rw-r--r--zenserver-test/zenserver-test.cpp42
-rw-r--r--zenserver/frontend/frontend.cpp2
-rw-r--r--zenserver/zenserver.cpp2
-rw-r--r--zenutil/include/zenutil/zenserverprocess.h14
-rw-r--r--zenutil/zenserverprocess.cpp24
12 files changed, 177 insertions, 37 deletions
diff --git a/xmake.lua b/xmake.lua
index 1b6724044..cc9458bdd 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -111,6 +111,8 @@ if is_os("windows") then
set_description("Enable http.sys server")
option_end()
add_define_by_config("ZEN_WITH_HTTPSYS", "httpsys")
+else
+ add_defines("ZEN_WITH_HTTPSYS=0")
end
option("compute")
diff --git a/zen/zen.cpp b/zen/zen.cpp
index 5a34ffa80..9754f4434 100644
--- a/zen/zen.cpp
+++ b/zen/zen.cpp
@@ -27,7 +27,7 @@
#include <zenhttp/httpcommon.h>
#if ZEN_WITH_TESTS
-# define ZEN_TEST_WITH_RUNNER
+# define ZEN_TEST_WITH_RUNNER 1
# include <zencore/testing.h>
#endif
diff --git a/zencore/include/zencore/testing.h b/zencore/include/zencore/testing.h
index bd55524aa..a00ee3166 100644
--- a/zencore/include/zencore/testing.h
+++ b/zencore/include/zencore/testing.h
@@ -4,24 +4,64 @@
#include <zencore/zencore.h>
-#ifdef ZEN_TEST_WITH_RUNNER
-# define CATCH_CONFIG_RUNNER
+#include <memory>
+
+#ifndef ZEN_TEST_WITH_RUNNER
+# define ZEN_TEST_WITH_RUNNER 0
+#endif
+
+#if ZEN_TEST_WITH_RUNNER
# define DOCTEST_CONFIG_IMPLEMENT
#endif
#if ZEN_WITH_TESTS
# include <doctest/doctest.h>
-# define ZEN_RUN_TESTS(argC, argV) doctest::Context(argc, argv).run()
inline auto
Approx(auto Value)
{
return doctest::Approx(Value);
}
-#else
-# define ZEN_RUN_TESTS(argC, argV)
#endif
-#ifdef ZEN_TEST_WITH_RUNNER
-# undef CATCH_CONFIG_RUNNER
+/**
+ * Test runner helper
+ *
+ * This acts as a thin layer between the test app and the test
+ * framework, which is used to customize configuration logic
+ * and to set up logging.
+ *
+ * If you don't want to implement custom setup then the
+ * ZEN_RUN_TESTS macro can be used instead.
+ */
+
+#if ZEN_WITH_TESTS
+namespace zen::testing {
+
+class TestRunner
+{
+public:
+ TestRunner();
+ ~TestRunner();
+
+ int ApplyCommandLine(int argc, char const* const* argv);
+ int Run();
+
+private:
+ struct Impl;
+
+ std::unique_ptr<Impl> m_Impl;
+};
+
+# define ZEN_RUN_TESTS(argC, argV) \
+ [&] { \
+ zen::testing::TestRunner Runner; \
+ Runner.ApplyCommandLine(argC, argV); \
+ return Runner.Run(); \
+ }()
+
+} // namespace zen::testing
+#endif
+
+#if ZEN_TEST_WITH_RUNNER
# undef DOCTEST_CONFIG_IMPLEMENT
#endif
diff --git a/zencore/testing.cpp b/zencore/testing.cpp
new file mode 100644
index 000000000..15be4b716
--- /dev/null
+++ b/zencore/testing.cpp
@@ -0,0 +1,52 @@
+#include "zencore/testing.h"
+#include "zencore/logging.h"
+
+#if ZEN_WITH_TESTS
+
+namespace zen::testing {
+
+using namespace std::literals;
+
+struct TestRunner::Impl
+{
+ doctest::Context Session;
+};
+
+TestRunner::TestRunner()
+{
+ m_Impl = std::make_unique<Impl>();
+}
+
+TestRunner::~TestRunner()
+{
+}
+
+int
+TestRunner::ApplyCommandLine(int argc, char const* const* argv)
+{
+ m_Impl->Session.applyCommandLine(argc, argv);
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (argv[i] == "--debug"sv)
+ {
+ spdlog::set_level(spdlog::level::debug);
+ }
+ }
+
+ return 0;
+}
+
+int
+TestRunner::Run()
+{
+ int Rv = 0;
+
+ m_Impl->Session.run();
+
+ return Rv;
+}
+
+} // namespace zen::testing
+
+#endif
diff --git a/zenhttp/httpasio.cpp b/zenhttp/httpasio.cpp
index f270c9d2b..510b349f9 100644
--- a/zenhttp/httpasio.cpp
+++ b/zenhttp/httpasio.cpp
@@ -1077,8 +1077,9 @@ HttpAsioServerRequest::HttpAsioServerRequest(asio_http::HttpRequest& Request, Ht
std::string_view Uri = Request.Url();
Uri.remove_prefix(std::min(PrefixLength, static_cast<int>(Uri.size())));
- m_Uri = Uri;
- m_QueryString = Request.QueryString();
+ m_Uri = Uri;
+ m_UriWithExtension = Uri;
+ m_QueryString = Request.QueryString();
m_Verb = Request.RequestVerb();
m_ContentLength = Request.Body().Size();
diff --git a/zenhttp/httpsys.cpp b/zenhttp/httpsys.cpp
index 16ec135cd..0f4fe0a6d 100644
--- a/zenhttp/httpsys.cpp
+++ b/zenhttp/httpsys.cpp
@@ -1232,6 +1232,9 @@ HttpSysServerRequest::HttpSysServerRequest(HttpSysTransaction& Tx, HttpService&
std::string_view UriSuffix8{m_UriUtf8};
+ m_UriWithExtension = UriSuffix8; // Retain URI with extension for user access
+ m_Uri = UriSuffix8;
+
const size_t LastComponentIndex = UriSuffix8.find_last_of('/');
if (LastComponentIndex != std::string_view::npos)
@@ -1244,22 +1247,18 @@ HttpSysServerRequest::HttpSysServerRequest(HttpSysTransaction& Tx, HttpService&
if (LastDotIndex != std::string_view::npos)
{
UriSuffix8.remove_prefix(LastDotIndex + 1);
+ m_Uri.remove_suffix(UriSuffix8.size() + 1);
AcceptContentType = ParseContentType(UriSuffix8);
-
- if (AcceptContentType != HttpContentType::kUnknownContentType)
- {
- m_UriUtf8.RemoveSuffix((uint32_t)(UriSuffix8.size() + 1));
- }
}
}
else
{
m_UriUtf8.Reset();
+ m_Uri = {};
+ m_UriWithExtension = {};
}
- m_Uri = std::string_view(m_UriUtf8);
-
if (uint16_t QueryStringLength = HttpRequestPtr->CookedUrl.QueryStringLength)
{
--QueryStringLength; // We skip the leading question mark
diff --git a/zenhttp/include/zenhttp/httpserver.h b/zenhttp/include/zenhttp/httpserver.h
index 451a47b4a..3b9fa50b4 100644
--- a/zenhttp/include/zenhttp/httpserver.h
+++ b/zenhttp/include/zenhttp/httpserver.h
@@ -34,6 +34,7 @@ public:
// Synchronous operations
[[nodiscard]] inline std::string_view RelativeUri() const { return m_Uri; } // Returns URI without service prefix
+ [[nodiscard]] std::string_view RelativeUriWithExtension() const { return m_UriWithExtension; }
[[nodiscard]] inline std::string_view QueryString() const { return m_QueryString; }
struct QueryParams
@@ -118,6 +119,7 @@ protected:
HttpContentType m_AcceptType = HttpContentType::kUnknownContentType;
uint64_t m_ContentLength = ~0ull;
std::string_view m_Uri;
+ std::string_view m_UriWithExtension;
std::string_view m_QueryString;
mutable uint32_t m_RequestId = ~uint32_t(0);
mutable Oid m_SessionId = Oid::Zero;
diff --git a/zenserver-test/zenserver-test.cpp b/zenserver-test/zenserver-test.cpp
index 1f320f2f6..3195181d1 100644
--- a/zenserver-test/zenserver-test.cpp
+++ b/zenserver-test/zenserver-test.cpp
@@ -300,7 +300,7 @@ main()
return 0;
}
#elif 0
-//#include <restinio/all.hpp>
+// #include <restinio/all.hpp>
int
main()
@@ -317,6 +317,8 @@ zen::ZenServerEnvironment TestEnv;
int
main(int argc, char** argv)
{
+ using namespace std::literals;
+
# if ZEN_USE_MIMALLOC
mi_version();
# endif
@@ -333,11 +335,31 @@ main(int argc, char** argv)
std::filesystem::path ProgramBaseDir = std::filesystem::path(argv[0]).parent_path();
std::filesystem::path TestBaseDir = ProgramBaseDir.parent_path().parent_path() / ".test";
- TestEnv.InitializeForTest(ProgramBaseDir, TestBaseDir);
+ // This is pretty janky because we're passing most of the options through to the test
+ // framework, so we can't just use cxxopts (I think). This should ideally be cleaned up
+ // somehow in the future
+
+ std::string ServerClass;
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (argv[i] == "--http"sv)
+ {
+ if ((i + 1) < argc)
+ {
+ ServerClass = argv[++i];
+ }
+ }
+ }
+
+ TestEnv.InitializeForTest(ProgramBaseDir, TestBaseDir, ServerClass);
ZEN_INFO("Running tests...(base dir: '{}')", TestBaseDir);
- return ZEN_RUN_TESTS(argc, argv);
+ zen::testing::TestRunner Runner;
+ Runner.ApplyCommandLine(argc, argv);
+
+ return Runner.Run();
}
namespace zen::tests {
@@ -454,7 +476,7 @@ TEST_CASE("project.basic")
ZenServerInstance Instance1(TestEnv);
Instance1.SetTestDir(TestDir);
- Instance1.SpawnServer(PortNumber, "--http asio");
+ Instance1.SpawnServer(PortNumber);
Instance1.WaitUntilReady();
std::atomic<uint64_t> RequestCount{0};
@@ -1711,19 +1733,17 @@ TEST_CASE("zcache.failing.upstream")
using namespace std::literals;
using namespace utils;
- const uint16_t Upstream1PortNumber = 13338;
- ZenConfig Upstream1Cfg = ZenConfig::New(Upstream1PortNumber);
- Upstream1Cfg.Args += (" --http asio");
+ const uint16_t Upstream1PortNumber = 13338;
+ ZenConfig Upstream1Cfg = ZenConfig::New(Upstream1PortNumber);
ZenServerInstance Upstream1Server(TestEnv);
- const uint16_t Upstream2PortNumber = 13339;
- ZenConfig Upstream2Cfg = ZenConfig::New(Upstream2PortNumber);
- Upstream2Cfg.Args += (" --http asio");
+ const uint16_t Upstream2PortNumber = 13339;
+ ZenConfig Upstream2Cfg = ZenConfig::New(Upstream2PortNumber);
ZenServerInstance Upstream2Server(TestEnv);
std::vector<std::uint16_t> UpstreamPorts = {Upstream1PortNumber, Upstream2PortNumber};
ZenConfig LocalCfg = ZenConfig::NewWithThreadedUpstreams(UpstreamPorts, false);
- LocalCfg.Args += (" --http asio --upstream-thread-count 2");
+ LocalCfg.Args += (" --upstream-thread-count 2");
ZenServerInstance LocalServer(TestEnv);
const uint16_t LocalPortNumber = 13337;
const auto LocalUri = fmt::format("http://localhost:{}/z$", LocalPortNumber);
diff --git a/zenserver/frontend/frontend.cpp b/zenserver/frontend/frontend.cpp
index e0b27ac55..149d97924 100644
--- a/zenserver/frontend/frontend.cpp
+++ b/zenserver/frontend/frontend.cpp
@@ -75,7 +75,7 @@ HttpFrontendService::HandleRequest(zen::HttpServerRequest& Request)
{
using namespace std::literals;
- std::string_view Uri = Request.RelativeUri();
+ std::string_view Uri = Request.RelativeUriWithExtension();
for (; Uri[0] == '/'; Uri = Uri.substr(1))
;
if (Uri.empty())
diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp
index 443bb627e..5b861f1bc 100644
--- a/zenserver/zenserver.cpp
+++ b/zenserver/zenserver.cpp
@@ -50,7 +50,7 @@ ZEN_THIRD_PARTY_INCLUDES_END
// in some shared code into the executable
#if ZEN_WITH_TESTS
-# define ZEN_TEST_WITH_RUNNER
+# define ZEN_TEST_WITH_RUNNER 1
# include <zencore/testing.h>
#endif
diff --git a/zenutil/include/zenutil/zenserverprocess.h b/zenutil/include/zenutil/zenserverprocess.h
index 3ec4b19b0..1c204c144 100644
--- a/zenutil/include/zenutil/zenserverprocess.h
+++ b/zenutil/include/zenutil/zenserverprocess.h
@@ -20,19 +20,21 @@ public:
~ZenServerEnvironment();
void Initialize(std::filesystem::path ProgramBaseDir);
- void InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir);
+ void InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir, std::string_view ServerClass = "");
- std::filesystem::path CreateNewTestDir();
- std::filesystem::path ProgramBaseDir() const { return m_ProgramBaseDir; }
- std::filesystem::path GetTestRootDir(std::string_view Path);
- inline bool IsInitialized() const { return m_IsInitialized; }
- inline bool IsTestEnvironment() const { return m_IsTestInstance; }
+ std::filesystem::path CreateNewTestDir();
+ std::filesystem::path ProgramBaseDir() const { return m_ProgramBaseDir; }
+ std::filesystem::path GetTestRootDir(std::string_view Path);
+ inline bool IsInitialized() const { return m_IsInitialized; }
+ inline bool IsTestEnvironment() const { return m_IsTestInstance; }
+ inline std::string_view GetServerClass() const { return m_ServerClass; }
private:
std::filesystem::path m_ProgramBaseDir;
std::filesystem::path m_TestBaseDir;
bool m_IsInitialized = false;
bool m_IsTestInstance = false;
+ std::string m_ServerClass;
};
struct ZenServerInstance
diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp
index 659eb665b..5ecde343b 100644
--- a/zenutil/zenserverprocess.cpp
+++ b/zenutil/zenserverprocess.cpp
@@ -397,8 +397,12 @@ ZenServerEnvironment::Initialize(std::filesystem::path ProgramBaseDir)
}
void
-ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir)
+ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir,
+ std::filesystem::path TestBaseDir,
+ std::string_view ServerClass)
{
+ using namespace std::literals;
+
m_ProgramBaseDir = ProgramBaseDir;
m_TestBaseDir = TestBaseDir;
@@ -408,6 +412,19 @@ ZenServerEnvironment::InitializeForTest(std::filesystem::path ProgramBaseDir, st
m_IsTestInstance = true;
m_IsInitialized = true;
+
+ if (ServerClass.empty())
+ {
+#if ZEN_WITH_HTTPSYS
+ m_ServerClass = "httpsys"sv;
+#else
+ m_ServerClass = "asio"sv;
+#endif
+ }
+ else
+ {
+ m_ServerClass = ServerClass;
+ }
}
std::filesystem::path
@@ -516,6 +533,11 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr
CommandLine << " --child-id " << ChildEventName;
+ if (std::string_view ServerClass = m_Env.GetServerClass(); ServerClass.empty() == false)
+ {
+ CommandLine << " --http " << ServerClass;
+ }
+
if (BasePort)
{
CommandLine << " --port " << BasePort;