From e64a7483717b99177c6c98658716ef161963b263 Mon Sep 17 00:00:00 2001 From: casey langen Date: Fri, 25 Mar 2022 22:02:25 -0700 Subject: [PATCH] Additional scaffolding. --- src/musikcore/CMakeLists.txt | 1 + src/musikcore/net/PiggyWebSocketClient.cpp | 117 ++++++++------------ src/musikcore/net/PiggyWebSocketClient.h | 35 ++---- src/musikcore/support/PiggyDebugBackend.cpp | 58 ++++++++++ src/musikcore/support/PiggyDebugBackend.h | 58 ++++++++++ src/musikcube/Main.cpp | 9 +- 6 files changed, 180 insertions(+), 98 deletions(-) create mode 100644 src/musikcore/support/PiggyDebugBackend.cpp create mode 100644 src/musikcore/support/PiggyDebugBackend.h diff --git a/src/musikcore/CMakeLists.txt b/src/musikcore/CMakeLists.txt index 47520f621..8309cc308 100644 --- a/src/musikcore/CMakeLists.txt +++ b/src/musikcore/CMakeLists.txt @@ -63,6 +63,7 @@ set(CORE_SOURCES ./support/Duration.cpp ./support/Common.cpp ./support/LastFm.cpp + ./support/PiggyDebugBackend.cpp ./support/Playback.cpp ./support/Preferences.cpp ./support/PreferenceKeys.cpp diff --git a/src/musikcore/net/PiggyWebSocketClient.cpp b/src/musikcore/net/PiggyWebSocketClient.cpp index 630f8406a..0a94bbbbc 100644 --- a/src/musikcore/net/PiggyWebSocketClient.cpp +++ b/src/musikcore/net/PiggyWebSocketClient.cpp @@ -36,8 +36,6 @@ #include #include -#include -#include #include #include @@ -50,43 +48,33 @@ using ClientMessage = PiggyWebSocketClient::ClientMessage; using Connection = PiggyWebSocketClient::Connection; using Message = PiggyWebSocketClient::Message; +static const int64_t kLatencyTimeoutMs = 30000; +static const int64_t kPingIntervalMs = 10000; +static const int kPingMessage = 6000; static const bool kDisableOfflineQueue = true; static std::atomic nextMessageId(0); -static inline std::string generateMessageId() { - return "integrated-websocket-client-" + std::to_string(nextMessageId.fetch_add(1)); +static inline std::string generateSessionId() { + return "musikcube-" + std::to_string(nextMessageId.fetch_add(1)); } -// static inline std::string createPingRequest() { -// const nlohmann::json authRequestJson = { -// { "name", "ping" }, -// { "type" , "request" }, -// { "id", generateMessageId() }, -// { "device_id", "remote-random-device" }, -// { "options", nlohmann::json() } -// }; -// return authRequestJson.dump(); -// } +static inline std::string createPingJson(const std::string& sessionId) { + const nlohmann::json authRequestJson = { + { "name", "ping" }, + { "sessionId", sessionId }, + { "data", nlohmann::json() } + }; + return authRequestJson.dump(); +} -// static inline bool extractRawQueryResult( -// nlohmann::json& responseJson, std::string& rawResult) -// { -// if (responseJson["name"].get() != "send_raw_query") { -// return false; -// } -// rawResult = responseJson["options"]["raw_query_data"].get(); -// return true; -// } - -PiggyWebSocketClient::PiggyWebSocketClient(IMessageQueue* messageQueue, Listener* listener) -: messageQueue(nullptr) { +PiggyWebSocketClient::PiggyWebSocketClient(IMessageQueue* messageQueue) +: messageQueue(nullptr) +, sessionId(generateSessionId()) { this->SetMessageQueue(messageQueue); rawClient = std::make_unique(io); - this->listener = listener; - - rawClient->SetMode(RawWebSocketClient::Mode::TLS); + rawClient->SetMode(RawWebSocketClient::Mode::PlainText); rawClient->SetOpenHandler([this](Connection connection) { this->SetState(State::Authenticating); @@ -138,28 +126,24 @@ void PiggyWebSocketClient::SetDisconnected(ConnectionError errorCode) { this->SetState(State::Disconnected); } -std::string PiggyWebSocketClient::EnqueueMessage(Message message) { +void PiggyWebSocketClient::EnqueueMessage(Message message) { std::unique_lockmutex)> lock(this->mutex); - if (kDisableOfflineQueue && this->state != State::Connected) { - return ""; - } if (!message) { - return ""; + return; + } + (*message)["sessionId"] = this->sessionId; + if (this->state != State::Connected) { + if (!kDisableOfflineQueue) { + this->pendingMessages.push_back(message); + } + return; } - auto messageId = generateMessageId(); - messageIdToMessage[messageId] = message; if (this->state == State::Connected) { this->rawClient->Send(this->connection, message->dump()); } - return messageId; } -void PiggyWebSocketClient::Connect( - const std::string& host, - unsigned short port, - const std::string& password, - bool useTls) -{ +void PiggyWebSocketClient::Connect(const std::string& host, unsigned short port, bool useTls) { auto newUri = "ws://" + host + ":" + std::to_string(port); if (newUri != this->uri || useTls != this->useTls || @@ -185,12 +169,9 @@ void PiggyWebSocketClient::Reconnect() { io.restart(); #endif - auto const prefs = Preferences::ForComponent(core::prefs::components::Settings); - auto const timeout = prefs->GetInt(core::prefs::keys::RemoteLibraryLatencyTimeoutMs, 5000); - this->SetState(State::Connecting); - this->thread = std::make_unique([&, timeout]() { + this->thread = std::make_unique([&]() { std::string uri; { @@ -203,7 +184,7 @@ void PiggyWebSocketClient::Reconnect() { ? RawWebSocketClient::Mode::TLS : RawWebSocketClient::Mode::PlainText; rawClient->SetMode(mode); - rawClient->SetPongTimeout(timeout); + rawClient->SetPongTimeout(kLatencyTimeoutMs); rawClient->Connect(uri); rawClient->Run(); } @@ -228,27 +209,17 @@ void PiggyWebSocketClient::Disconnect() { void PiggyWebSocketClient::InvalidatePendingMessages() { std::unique_lockmutex)> lock(this->mutex); - - // for (auto& kv : this->messageIdToMessage) { - // this->listener->OnClientQueryFailed( - // this, kv.first, kv.second, QueryError::Disconnected); - // } - - this->messageIdToMessage.clear(); + this->pendingMessages.clear(); } void PiggyWebSocketClient::SendPendingMessages() { std::unique_lockmutex)> lock(this->mutex); - // for (auto& kv : this->messageIdToMessage) { - // auto messageId = kv.first; - // auto query = kv.second; - // if (query) { - // this->rawClient->Send( - // this->connection, - // createSendRawQueryRequest(query->SerializeQuery(), messageId)); - // } - // } + for (auto& message : this->pendingMessages) { + this->rawClient->Send(this->connection, message->dump()); + } + + this->pendingMessages.clear(); } void PiggyWebSocketClient::SetState(State state) { @@ -270,7 +241,7 @@ void PiggyWebSocketClient::SetState(State state) { } this->state = state; - this->listener->OnClientStateChanged(this, state, oldState); + this->StateChanged(this, state, oldState); } } @@ -284,17 +255,17 @@ void PiggyWebSocketClient::SetMessageQueue(IMessageQueue* messageQueue) { this->messageQueue = messageQueue; if (this->messageQueue) { this->messageQueue->Register(this); - // this->messageQueue->Post(Message::Create(this, kPingMessage), kPingIntervalMs); + this->messageQueue->Post(runtime::Message::Create(this, kPingMessage), kPingIntervalMs); } } /* IMessageTarget */ void PiggyWebSocketClient::ProcessMessage(IMessage& message) { - // if (message.Type() == kPingMessage) { - // std::unique_lockmutex)> lock(this->mutex); - // if (this->state == State::Connected) { - // this->rawClient->Send(this->connection, createPingRequest()); - // } - // this->messageQueue->Post(Message::Create(this, kPingMessage), kPingIntervalMs); - // } + if (message.Type() == kPingMessage) { + std::unique_lockmutex)> lock(this->mutex); + if (this->state == State::Connected) { + this->rawClient->Send(this->connection, createPingJson(this->sessionId)); + } + this->messageQueue->Post(runtime::Message::Create(this, kPingMessage), kPingIntervalMs); + } } diff --git a/src/musikcore/net/PiggyWebSocketClient.h b/src/musikcore/net/PiggyWebSocketClient.h index e44de810a..9fe27bc29 100644 --- a/src/musikcore/net/PiggyWebSocketClient.h +++ b/src/musikcore/net/PiggyWebSocketClient.h @@ -38,8 +38,10 @@ #include #include +#include + #include -#include +#include #include #include @@ -66,36 +68,21 @@ namespace musik { namespace core { namespace net { enum class ConnectionError : int { None = 0, - InvalidPassword = 1, - IncompatibleVersion = 2, - ConnectionFailed = 3, - ClosedByServer = 4, + ConnectionFailed = 1, + ClosedByServer = 2, }; - class Listener { - public: - using Client = PiggyWebSocketClient; - using State = Client::State; - virtual void OnClientStateChanged(Client* client, State newState, State oldState) = 0; - }; - - PiggyWebSocketClient( - musik::core::runtime::IMessageQueue* messageQueue, - Listener* listener); + sigslot::signal3 StateChanged; + PiggyWebSocketClient(musik::core::runtime::IMessageQueue* messageQueue); PiggyWebSocketClient(const PiggyWebSocketClient&) = delete; virtual ~PiggyWebSocketClient(); - void Connect( - const std::string& host, - unsigned short port, - const std::string& password, - bool useTls); - + void Connect(const std::string& host, unsigned short port = 8347, bool useTls = false); void Reconnect(); void Disconnect(); - std::string EnqueueMessage(Message message); + void EnqueueMessage(Message message); State ConnectionState() const; ConnectionError LastConnectionError() const; @@ -112,16 +99,16 @@ namespace musik { namespace core { namespace net { ClientPtr rawClient; Connection connection; + const std::string& sessionId; boost::asio::io_service io; std::unique_ptr thread; mutable std::recursive_mutex mutex; bool useTls{ false }; std::string uri; - std::unordered_map messageIdToMessage; + std::deque pendingMessages; std::atomic quit{ false }; ConnectionError connectionError{ ConnectionError::None }; State state{ State::Disconnected }; - Listener* listener{ nullptr }; musik::core::runtime::IMessageQueue* messageQueue; }; diff --git a/src/musikcore/support/PiggyDebugBackend.cpp b/src/musikcore/support/PiggyDebugBackend.cpp new file mode 100644 index 000000000..584c205db --- /dev/null +++ b/src/musikcore/support/PiggyDebugBackend.cpp @@ -0,0 +1,58 @@ + +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "pch.hpp" +#include "PiggyDebugBackend.h" + +using namespace musik; + +PiggyDebugBackend::PiggyDebugBackend(Client client): client(client) { +} + +PiggyDebugBackend::~PiggyDebugBackend() { +} + +void PiggyDebugBackend::verbose(const std::string& tag, const std::string& string) { +} + +void PiggyDebugBackend::info(const std::string& tag, const std::string& string) { +} + +void PiggyDebugBackend::warning(const std::string& tag, const std::string& string) { +} + +void PiggyDebugBackend::error(const std::string& tag, const std::string& string) { +} + diff --git a/src/musikcore/support/PiggyDebugBackend.h b/src/musikcore/support/PiggyDebugBackend.h new file mode 100644 index 000000000..e7896eea7 --- /dev/null +++ b/src/musikcore/support/PiggyDebugBackend.h @@ -0,0 +1,58 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2004-2021 musikcube team +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the author nor the names of other contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +namespace musik { + + class PiggyDebugBackend : public musik::debug::IBackend { + public: + using Client = std::shared_ptr; + + PiggyDebugBackend(Client client); + virtual ~PiggyDebugBackend() override; + virtual void verbose(const std::string& tag, const std::string& string) override; + virtual void info(const std::string& tag, const std::string& string) override; + virtual void warning(const std::string& tag, const std::string& string) override; + virtual void error(const std::string& tag, const std::string& string) override; + + private: + Client client; + }; + +} diff --git a/src/musikcube/Main.cpp b/src/musikcube/Main.cpp index e6fc3a605..bf6246514 100644 --- a/src/musikcube/Main.cpp +++ b/src/musikcube/Main.cpp @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include @@ -72,6 +74,7 @@ using namespace musik; using namespace musik::core; using namespace musik::core::audio; +using namespace musik::core::net; using namespace musik::cube; using namespace cursespp; @@ -113,9 +116,13 @@ int main(int argc, char* argv[]) { std::string errorFn = core::GetDataDirectory() + "stderr.txt"; freopen(errorFn.c_str(), "w", stderr); + auto piggyClient = std::make_shared(&Window::MessageQueue()); + piggyClient->Connect("172.31.16.1"); + auto piggyLogger = new PiggyDebugBackend(piggyClient); + auto fileLogger = new debug::SimpleFileBackend(); auto consoleLogger = new ConsoleLogger(Window::MessageQueue()); - debug::Start({ fileLogger, consoleLogger }); + debug::Start({ fileLogger, consoleLogger, piggyLogger }); plugin::Init();