/* 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