/* Copyright 2016, Ableton AG, Berlin. All rights reserved. * * 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, see . * * If you would like to incorporate Link into a proprietary software application, * please contact . */ #pragma once #include #include #include #include #include #include #include #include namespace ableton { namespace link { template class PingResponder { using IoType = util::Injected; using Socket = typename IoType::type::template Socket; public: PingResponder(discovery::IpAddress address, SessionId sessionId, GhostXForm ghostXForm, Clock clock, IoType io) : mIo(io) , mpImpl(std::make_shared(std::move(address), std::move(sessionId), std::move(ghostXForm), std::move(clock), std::move(io))) { mpImpl->listen(); } PingResponder(const PingResponder&) = delete; PingResponder(PingResponder&&) = delete; void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) { mpImpl->mSessionId = std::move(sessionId); mpImpl->mGhostXForm = std::move(xform); } discovery::UdpEndpoint endpoint() const { return mpImpl->mSocket.endpoint(); } discovery::IpAddress address() const { return endpoint().address(); } Socket socket() const { return mpImpl->mSocket; } private: struct Impl : std::enable_shared_from_this { Impl(discovery::IpAddress address, SessionId sessionId, GhostXForm ghostXForm, Clock clock, IoType io) : mSessionId(std::move(sessionId)) , mGhostXForm(std::move(ghostXForm)) , mClock(std::move(clock)) , mLog(channel(io->log(), "gateway@" + address.to_string())) , mSocket(io->template openUnicastSocket(address)) { } void listen() { mSocket.receive(util::makeAsyncSafe(this->shared_from_this())); } // Operator to handle incoming messages on the interface template void operator()(const discovery::UdpEndpoint& from, const It begin, const It end) { using namespace discovery; // Decode Ping Message const auto result = link::v1::parseMessageHeader(begin, end); const auto& header = result.first; const auto payloadBegin = result.second; // Check Payload size const auto payloadSize = static_cast(std::distance(payloadBegin, end)); const auto maxPayloadSize = sizeInByteStream(makePayload(HostTime{}, PrevGHostTime{})); if (header.messageType == v1::kPing && payloadSize <= maxPayloadSize) { debug(mLog) << " Received ping message from " << from; try { reply(std::move(payloadBegin), std::move(end), from); } catch (const std::runtime_error& err) { info(mLog) << " Failed to send pong to " << from << ". Reason: " << err.what(); } } else { info(mLog) << " Received invalid Message from " << from << "."; } listen(); } template void reply(It begin, It end, const discovery::UdpEndpoint& to) { using namespace discovery; // Encode Pong Message const auto id = SessionMembership{mSessionId}; const auto currentGt = GHostTime{mGhostXForm.hostToGhost(mClock.micros())}; const auto pongPayload = makePayload(id, currentGt); v1::MessageBuffer pongBuffer; const auto pongMsgBegin = std::begin(pongBuffer); auto pongMsgEnd = v1::pongMessage(pongPayload, pongMsgBegin); // Append ping payload to pong message. pongMsgEnd = std::copy(begin, end, pongMsgEnd); const auto numBytes = static_cast(std::distance(pongMsgBegin, pongMsgEnd)); mSocket.send(pongBuffer.data(), numBytes, to); } SessionId mSessionId; GhostXForm mGhostXForm; Clock mClock; typename IoType::type::Log mLog; Socket mSocket; }; IoType mIo; std::shared_ptr mpImpl; }; } // namespace link } // namespace ableton