diff options
| author | Stefan Boberg <[email protected]> | 2022-02-07 14:13:48 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-02-07 14:13:48 +0100 |
| commit | 2a322aba6876ab30cdaa4db829e5ad5031cc7967 (patch) | |
| tree | 9afd1054e18c6d48dcda4e623da7a72cc095c0f7 | |
| parent | Missing override suffix compile fix (diff) | |
| parent | Added log output to make it clearer which lock file is in use (diff) | |
| download | zen-2a322aba6876ab30cdaa4db829e5ad5031cc7967.tar.xz zen-2a322aba6876ab30cdaa4db829e5ad5031cc7967.zip | |
Merging minor fixes to main
| -rw-r--r-- | zenserver-test/zenserver-test.cpp | 416 | ||||
| -rw-r--r-- | zenserver/zenserver.cpp | 2 | ||||
| -rw-r--r-- | zenutil/cache/cachepolicy.cpp | 32 | ||||
| -rw-r--r-- | zenutil/include/zenutil/cache/cachepolicy.h | 1 |
4 files changed, 26 insertions, 425 deletions
diff --git a/zenserver-test/zenserver-test.cpp b/zenserver-test/zenserver-test.cpp index 425f43946..b46106287 100644 --- a/zenserver-test/zenserver-test.cpp +++ b/zenserver-test/zenserver-test.cpp @@ -92,398 +92,6 @@ struct Concurrency }; #endif -/* - -___ ___ _________ _________ ________ ________ ___ ___ _______ ________ _________ -|\ \|\ \|\___ ___\\___ ___\\ __ \ |\ ____\|\ \ |\ \|\ ___ \ |\ ___ \|\___ ___\ -\ \ \\\ \|___ \ \_\|___ \ \_\ \ \|\ \ \ \ \___|\ \ \ \ \ \ \ __/|\ \ \\ \ \|___ \ \_| - \ \ __ \ \ \ \ \ \ \ \ \ ____\ \ \ \ \ \ \ \ \ \ \ \_|/_\ \ \\ \ \ \ \ \ - \ \ \ \ \ \ \ \ \ \ \ \ \ \___| \ \ \____\ \ \____\ \ \ \ \_|\ \ \ \\ \ \ \ \ \ - \ \__\ \__\ \ \__\ \ \__\ \ \__\ \ \_______\ \_______\ \__\ \_______\ \__\\ \__\ \ \__\ - \|__|\|__| \|__| \|__| \|__| \|_______|\|_______|\|__|\|_______|\|__| \|__| \|__| - -*/ - -class HttpConnectionPool; - -/** - * Http client connection - * - * Represents an established socket connection to a certain endpoint - */ - -class HttpClientConnection -{ - static HttpClientConnection* This(http_parser* Parser) { return (HttpClientConnection*)Parser->data; }; - -public: - HttpClientConnection(asio::io_context& IoContext, zen::Ref<HttpConnectionPool> Pool, asio::ip::tcp::socket&& InSocket) - : m_IoContext(IoContext) - , m_Pool(Pool) - , m_Resolver(IoContext) - , m_Socket(std::move(InSocket)) - { - } - ~HttpClientConnection() {} - - zen::Ref<HttpConnectionPool> ConnectionPool() { return m_Pool; } - void SetKeepAlive(bool NewState) { m_KeepAlive = NewState; } - - void Get(const std::string_view Server, int Port, const std::string_view Path) - { - ZEN_UNUSED(Port); - - http_parser_init(&m_HttpParser, HTTP_RESPONSE); - m_HttpParser.data = this; - - m_HttpParserSettings = http_parser_settings{ - .on_message_begin = [](http_parser* p) -> int { return This(p)->OnMessageBegin(); }, - .on_url = nullptr, - .on_status = nullptr, - .on_header_field = [](http_parser* p, const char* data, size_t size) { return This(p)->OnHeader(data, size); }, - .on_header_value = [](http_parser* p, const char* data, size_t size) { return This(p)->OnHeaderValue(data, size); }, - .on_headers_complete = [](http_parser* p) -> int { return This(p)->OnHeadersComplete(); }, - .on_body = [](http_parser* p, const char* data, size_t size) { return This(p)->OnBody(data, size); }, - .on_message_complete = [](http_parser* p) -> int { return This(p)->OnMessageComplete(); }, - .on_chunk_header = nullptr, - .on_chunk_complete = nullptr}; - - m_Headers.reserve(16); - - zen::ExtendableStringBuilder<256> RequestBody; - RequestBody << "GET "sv << Path << " HTTP/1.1\r\n"sv; - RequestBody << "Host: "sv << Server << "\r\n"sv; - RequestBody << "Accept: */*\r\n"sv; - RequestBody << "Connection: "sv << (m_KeepAlive ? "keep-alive"sv : "close"sv) << "\r\n\r\n"sv; // TODO: support keep-alive - - m_RequestBody = RequestBody; - - OnConnected(); - } - -private: - void Reset() {} - - void OnError(const asio::error_code& Error) - { - // Let EOF errors proceed. They're raised when sockets close. - if (Error == asio::error::eof) - { - return; - } - - zen::ThrowLastError(fmt::format("HTTP client error! '{}'", Error.message())); - } - - int OnHeader(const char* Data, size_t Bytes) - { - m_CurrentHeaderName = std::string_view(Data, Bytes); - return 0; - } - int OnHeaderValue(const char* Data, size_t Bytes) - { - m_Headers.emplace_back(HeaderEntry{m_CurrentHeaderName, {Data, Bytes}}); - return 0; - } - int OnHeadersComplete() - { - ZEN_DEBUG("Headers complete"); - return 0; - } - int OnMessageComplete() - { - if (http_should_keep_alive(&m_HttpParser)) - { - Reset(); - } - else - { - m_Socket.close(); - m_RequestState = RequestState::Done; - } - return 0; - } - int OnMessageBegin() { return 0; } - int OnBody(const char* Data, size_t Bytes) - { - ZEN_UNUSED(Data, Bytes); - return 0; - } - - void OnConnected() - { - // Send initial request payload - asio::async_write(m_Socket, - asio::const_buffer(m_RequestBody.data(), m_RequestBody.size()), - [this](const asio::error_code& Error, size_t Bytes) { - ZEN_UNUSED(Bytes); - if (Error) - { - return OnError(Error); - } - - OnRequestWritten(); - }); - } - - void OnRequestWritten() - { - asio::async_read(m_Socket, m_ResponseBuffer, asio::transfer_at_least(1), [this](const asio::error_code& Error, size_t Bytes) { - if (Error) - { - return OnError(Error); - } - - OnStatusLineRead(Bytes); - }); - } - - void OnStatusLineRead(size_t Bytes) - { - // Parse - - size_t rv = http_parser_execute(&m_HttpParser, &m_HttpParserSettings, (const char*)m_ResponseBuffer.data(), Bytes); - - ZEN_UNUSED(rv); - - if (m_HttpParser.http_errno != 0) - { - // Something bad! - - ZEN_ERROR("parse error {}", (uint32_t)m_HttpParser.http_errno); - } - - switch (m_RequestState) - { - case RequestState::Init: - asio::async_read(m_Socket, - m_ResponseBuffer, - asio::transfer_at_least(1), - [this](const asio::error_code& Error, size_t Bytes) { - if (Error) - { - return OnError(Error); - } - OnStatusLineRead(Bytes); - }); - return; - case RequestState::Done: - break; - } - } - -private: - asio::io_context& m_IoContext; - zen::Ref<HttpConnectionPool> m_Pool; - asio::ip::tcp::resolver m_Resolver; - asio::ip::tcp::socket m_Socket; - std::string m_Uri; - std::string m_RequestBody; // Initial request data - http_parser m_HttpParser{}; - http_parser_settings m_HttpParserSettings{}; - uint8_t m_ResponseIoBuffer[4096]; - asio::mutable_buffer m_ResponseBuffer{m_ResponseIoBuffer, sizeof m_ResponseIoBuffer}; - - enum class RequestState - { - Init, - Done - }; - - RequestState m_RequestState = RequestState::Init; - - struct HeaderEntry - { - std::string_view Name; - std::string_view Value; - }; - - std::string_view m_CurrentHeaderName; // Used while parsing headers - std::vector<HeaderEntry> m_Headers; - bool m_KeepAlive = false; -}; - -////////////////////////////////////////////////////////////////////////// - -class HttpConnectionPool : public zen::RefCounted -{ -public: - HttpConnectionPool(asio::io_context& Context, std::string_view HostName, uint16_t Port); - ~HttpConnectionPool(); - - std::unique_ptr<HttpClientConnection> GetConnection(); - void ReturnConnection(std::unique_ptr<HttpClientConnection>&& Connection); - -private: - zen::RwLock m_Lock; - asio::io_context& m_Context; - std::vector<HttpClientConnection*> m_AvailableConnections; - std::string m_HostName; - uint16_t m_Port; -}; - -HttpConnectionPool::HttpConnectionPool(asio::io_context& Context, std::string_view HostName, uint16_t Port) -: m_Context(Context) -, m_HostName(HostName) -, m_Port(Port) -{ -} - -HttpConnectionPool::~HttpConnectionPool() -{ - zen::RwLock::ExclusiveLockScope ScopedLock(m_Lock); - - for (auto $ : m_AvailableConnections) - { - delete $; - } -} - -std::unique_ptr<HttpClientConnection> -HttpConnectionPool::GetConnection() -{ - zen::RwLock::ExclusiveLockScope ScopedLock(m_Lock); - - if (m_AvailableConnections.empty()) - { - zen::StringBuilder<16> Service; - Service << int64_t(m_Port); - - asio::ip::tcp::resolver Resolver{m_Context}; - - asio::error_code ErrCode; - auto it = Resolver.resolve(m_HostName, Service, ErrCode); - auto itEnd = asio::ip::tcp::resolver::iterator(); - - if (ErrCode) - { - zen::ThrowLastError(fmt::format("Unabled to resolve '{}'", m_HostName)); - } - - asio::ip::tcp::socket Socket{m_Context}; - asio::connect(Socket, it, ErrCode); - - if (ErrCode) - { - zen::ThrowLastError(fmt::format("Failed connecting '{}:{}'", m_HostName, m_Port)); - } - - return std::make_unique<HttpClientConnection>(m_Context, this, std::move(Socket)); - } - - std::unique_ptr<HttpClientConnection> Connection{m_AvailableConnections.back()}; - m_AvailableConnections.pop_back(); - return Connection; -} - -void -HttpConnectionPool::ReturnConnection(std::unique_ptr<HttpClientConnection>&& Connection) -{ - zen::RwLock::ExclusiveLockScope ScopedLock(m_Lock); - m_AvailableConnections.emplace_back(Connection.release()); -} - -////////////////////////////////////////////////////////////////////////// - -class HttpContext -{ -public: - HttpContext(asio::io_context& Context) : m_Context(Context) {} - ~HttpContext() = default; - - std::unique_ptr<HttpClientConnection> GetConnection(std::string_view HostName, uint16_t Port) - { - return ConnectionPool(HostName, Port)->GetConnection(); - } - - void ReturnConnection(std::unique_ptr<HttpClientConnection> Connection) - { - Connection->ConnectionPool()->ReturnConnection(std::move(Connection)); - } - - zen::Ref<HttpConnectionPool> ConnectionPool(std::string_view HostName, uint16_t Port) - { - zen::RwLock::ExclusiveLockScope _(m_Lock); - ConnectionId ConnId{std::string(HostName), Port}; - - if (auto It = m_ConnectionPools.find(ConnId); It == end(m_ConnectionPools)) - { - // Not found - create new entry - - auto In = m_ConnectionPools.emplace(ConnId, new HttpConnectionPool(m_Context, HostName, Port)); - - return In.first->second; - } - else - { - return It->second; - } - } - -private: - asio::io_context& m_Context; - - struct ConnectionId - { - inline bool operator<(const ConnectionId& Rhs) const - { - if (HostName != Rhs.HostName) - { - return HostName < Rhs.HostName; - } - - return Port < Rhs.Port; - } - - std::string HostName; - uint16_t Port; - }; - - zen::RwLock m_Lock; - std::map<ConnectionId, zen::Ref<HttpConnectionPool>> m_ConnectionPools; -}; - -////////////////////////////////////////////////////////////////////////// - -class HttpClientRequest -{ -public: - HttpClientRequest(HttpContext& Context) : m_HttpContext(Context) {} - ~HttpClientRequest() - { - if (m_Connection) - { - m_HttpContext.ReturnConnection(std::move(m_Connection)); - } - } - - void Get(const std::string_view Url) - { - http_parser_url ParsedUrl; - int ErrCode = http_parser_parse_url(Url.data(), Url.size(), 0, &ParsedUrl); - - if (ErrCode) - { - ZEN_NOT_IMPLEMENTED(); - } - - if ((ParsedUrl.field_set & (UF_HOST | UF_PORT | UF_PATH)) != (UF_HOST | UF_PORT | UF_PATH)) - { - // Bad URL - } - - std::string_view HostName(Url.data() + ParsedUrl.field_data[UF_HOST].off, ParsedUrl.field_data[UF_HOST].len); - std::string_view Path(Url.data() + ParsedUrl.field_data[UF_PATH].off); - - m_Connection = m_HttpContext.GetConnection(HostName, ParsedUrl.port); - m_Connection->Get(HostName, ParsedUrl.port, Path); - } - -private: - HttpContext& m_HttpContext; - std::unique_ptr<HttpClientConnection> m_Connection; -}; - ////////////////////////////////////////////////////////////////////////// // // Custom logging -- test code, this should be tweaked @@ -729,30 +337,6 @@ main(int argc, char** argv) namespace zen::tests { -# if 1 -TEST_CASE("asio.http") -{ - std::filesystem::path TestDir = TestEnv.CreateNewTestDir(); - - ZenServerInstance Instance(TestEnv); - Instance.SetTestDir(TestDir); - Instance.SpawnServer(13337); - - ZEN_INFO("Waiting..."); - - Instance.WaitUntilReady(); - - // asio test - - asio::io_context IoContext; - HttpContext HttpCtx(IoContext); - HttpClientRequest Request(HttpCtx); - Request.Get("http://localhost:13337/test/hello"); - - IoContext.run(); -} -# endif - TEST_CASE("default.single") { std::filesystem::path TestDir = TestEnv.CreateNewTestDir(); diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp index c1a5fe507..f745f321a 100644 --- a/zenserver/zenserver.cpp +++ b/zenserver/zenserver.cpp @@ -847,6 +847,8 @@ ZenEntryPoint::Run() InitializeLogging(ServerOptions); + ZEN_INFO(ZEN_APP_NAME " - using lock file at '{}'", LockFilePath); + ZEN_INFO(ZEN_APP_NAME " - starting on port {}, version '{}'", ServerOptions.BasePort, ZEN_CFG_VERSION_BUILD_STRING_FULL); ZenServerState ServerState; diff --git a/zenutil/cache/cachepolicy.cpp b/zenutil/cache/cachepolicy.cpp index 7e6477cd4..8c10ea674 100644 --- a/zenutil/cache/cachepolicy.cpp +++ b/zenutil/cache/cachepolicy.cpp @@ -20,7 +20,7 @@ using namespace std::literals; namespace DerivedData::Private { - constexpr char CachePolicyDelimiter = ','; + constinit char CachePolicyDelimiter = ','; struct CachePolicyToTextData { @@ -28,7 +28,7 @@ namespace DerivedData::Private { std::string_view Text; }; - const CachePolicyToTextData CachePolicyToText[]{ + constinit CachePolicyToTextData CachePolicyToText[]{ // Flags with multiple bits are ordered by bit count to minimize token count in the text format. {CachePolicy::Default, "Default"sv}, {CachePolicy::Remote, "Remote"sv}, @@ -48,7 +48,7 @@ namespace DerivedData::Private { {CachePolicy::None, "None"sv}, }; - constexpr CachePolicy CachePolicyKnownFlags = + constinit CachePolicy CachePolicyKnownFlags = CachePolicy::Default | CachePolicy::SkipMeta | CachePolicy::SkipData | CachePolicy::PartialRecord | CachePolicy::KeepAlive; StringBuilderBase& CachePolicyToString(StringBuilderBase& Builder, CachePolicy Policy) @@ -120,11 +120,27 @@ ConvertToUpstream(CachePolicy Policy) // Set Local flags equal to downstream's Remote flags. // Delete Skip flags if StoreLocal is true, otherwise use the downstream value. // Use the downstream value for all other flags. - return (EnumHasAllFlags(Policy, CachePolicy::QueryRemote) ? CachePolicy::QueryLocal : CachePolicy::None) | - (EnumHasAllFlags(Policy, CachePolicy::StoreRemote) ? CachePolicy::StoreLocal : CachePolicy::None) | - (!EnumHasAllFlags(Policy, CachePolicy::StoreLocal) ? (Policy & (CachePolicy::SkipData | CachePolicy::SkipMeta)) - : CachePolicy::None) | - (Policy & ~(CachePolicy::Local | CachePolicy::SkipData | CachePolicy::SkipMeta)); + + CachePolicy UpstreamPolicy = CachePolicy::None; + + if (EnumHasAllFlags(Policy, CachePolicy::QueryRemote)) + { + UpstreamPolicy |= CachePolicy::QueryLocal; + } + + if (EnumHasAllFlags(Policy, CachePolicy::StoreRemote)) + { + UpstreamPolicy |= CachePolicy::StoreLocal; + } + + if (!EnumHasAllFlags(Policy, CachePolicy::StoreLocal)) + { + UpstreamPolicy |= (Policy & (CachePolicy::SkipData | CachePolicy::SkipMeta)); + } + + UpstreamPolicy |= Policy & ~(CachePolicy::Local | CachePolicy::SkipData | CachePolicy::SkipMeta); + + return UpstreamPolicy; } class Private::CacheRecordPolicyShared final : public Private::ICacheRecordPolicyShared diff --git a/zenutil/include/zenutil/cache/cachepolicy.h b/zenutil/include/zenutil/cache/cachepolicy.h index 3eb0fda66..efcc4fb49 100644 --- a/zenutil/include/zenutil/cache/cachepolicy.h +++ b/zenutil/include/zenutil/cache/cachepolicy.h @@ -10,7 +10,6 @@ #include <gsl/gsl-lite.hpp> #include <span> -#include <unordered_map> #define BACKWARDS_COMPATABILITY_JAN2022 1 #if BACKWARDS_COMPATABILITY_JAN2022 |