This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,494 @@
#include <vector>
#include <array>
#include <string>
#include <cstdint>
#include <thread>
#include <mutex>
#include <memory>
#include <map>
#include "ovCMessaging.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
static const std::map<CMessaging::ELibraryError, std::string> ERRORS_STRING =
{
{ CMessaging::NoError, "No error" },
{ CMessaging::Socket_NotConnected, "Not connected" },
{ CMessaging::Socket_FailedToConnect, "Failed to connect" },
{ CMessaging::Socket_ReceiveBufferFail, "Failed to receive the buffer" },
{ CMessaging::Socket_SendBufferFail, "Failed to send the buffer" },
{ CMessaging::Socket_NoIncomingClientConnection, "No incoming client connection before the timeout" },
{ CMessaging::Socket_NotReadyToSend, "Socket not ready to send the buffer" },
{ CMessaging::Socket_NoDataReceived, "No data received by the socket" },
{ CMessaging::Socket_FailedCloseClientConnection, "Failed to close the client connection" },
{ CMessaging::Socket_FailedToCloseConnection, "Failed to close the server connection" },
{ CMessaging::Socket_FailedConnectClient, "Failed to connect the client" },
{ CMessaging::Socket_ClientAlreadyConnected, "A client is already connected" },
{ CMessaging::Deserialize_BufferTooSmall, "Buffer received is too small to be unpacked" },
{ CMessaging::Deserialize_Header, "Fail to unpack the buffer to a Header" },
{ CMessaging::Deserialize_ProtocolVersionMessage, "Fail to unpack Protocol Version message" },
{ CMessaging::Deserialize_BoxDescriptionMessage, "Fail to unpack Box description message" },
{ CMessaging::Deserialize_EBMLMessage, "Fail to unpack EBML message" },
{ CMessaging::Deserialize_EndMessage, "Fail to unpack End message" },
{ CMessaging::Deserialize_ErrorMessage, "Fail to unpack error message" },
{ CMessaging::Deserialize_LogMessage, "Fail to unpack log message" },
{ CMessaging::Deserialize_AuthenticationMessage, "Fail to unpack Authentication message" },
{ CMessaging::Deserialize_MessageTypeNotSupported, "Message type not supported" },
{ CMessaging::BoxDescriptionAlreadyReceived, "Box Description already received" },
{ CMessaging::BoxDescriptionNotReceived, "Box description not received" },
{ CMessaging::BadAuthenticationReceived, "Authentication received is invalid" },
{ CMessaging::NoAuthenticationReceived, "No authentication received before the timeout" },
{ CMessaging::ThreadJoinFailed, "Failed to terminate the thread" }
};
CMessaging::CMessaging()
{
impl = new SMessagingImpl();
impl->m_nMessage = 0;
impl->m_Connection = nullptr;
impl->m_LastLibraryError = NoError;
impl->m_IsStopRequested = false;
impl->m_IsInErrorState = false;
impl->m_IsEndMessageReceived = false;
}
CMessaging::~CMessaging()
{
this->reset();
delete impl;
}
void CMessaging::reset() const
{
this->stopSyncing();
impl->m_nMessage = 0;
impl->m_IsInErrorState = false;
impl->m_IsStopRequested = false;
impl->m_LastLibraryError = NoError;
std::queue<std::pair<uint64_t, AuthenticationMessage>>().swap(impl->m_IncomingAuthentications);
std::queue<std::pair<uint64_t, CommunicationProtocolVersionMessage>>().swap(impl->m_IncomingCommunicationProtocolVersions);
std::queue<std::pair<uint64_t, BoxDescriptionMessage>>().swap(impl->m_IncomingBoxDescriptions);
std::queue<std::pair<uint64_t, EBMLMessage>>().swap(impl->m_IncomingEBMLs);
std::queue<std::pair<uint64_t, LogMessage>>().swap(impl->m_IncomingLogs);
std::queue<std::pair<uint64_t, ErrorMessage>>().swap(impl->m_IncomingErrors);
impl->m_RcvBuffer.clear();
impl->m_SendBuffer.clear();
impl->m_SendBuffer.reserve(impl->s_BufferSize);
impl->m_Connection = nullptr;
}
CMessaging::ELibraryError CMessaging::getLastError() const { return impl->m_LastLibraryError; }
void CMessaging::setLastError(const ELibraryError libraryError) const { impl->m_LastLibraryError = libraryError; }
bool CMessaging::push() const
{
if (!impl->m_SendBuffer.empty() && impl->m_Connection->isReadyToSend(1))
{
std::lock_guard<std::mutex> lock(impl->m_SendBufferMutex);
const uint_fast32_t result = impl->m_Connection->sendBufferBlocking(impl->m_SendBuffer.data(), impl->m_SendBuffer.size());
if (result == 0)
{
this->setLastError(Socket_SendBufferFail);
return false;
}
impl->m_SendBuffer.clear();
}
return true;
}
bool CMessaging::pull() const
{
if (impl->m_Connection == nullptr)
{
this->setLastError(Socket_NotConnected);
return false;
}
while (impl->m_Connection->isReadyToReceive(1))
{
const uint_fast32_t bytesReceived = uint_fast32_t(impl->m_Connection->receiveBuffer(impl->m_TempRcvBuffer.data(), impl->m_TempRcvBuffer.size()));
if (bytesReceived == 0)
{
this->setLastError(Socket_ReceiveBufferFail);
return false;
}
impl->m_RcvBuffer.insert(impl->m_RcvBuffer.end(), impl->m_TempRcvBuffer.cbegin(), impl->m_TempRcvBuffer.cbegin() + int(bytesReceived));
}
return true;
}
bool CMessaging::processIncomingMessages() const
{
size_t byteRead = 0;
while (!impl->m_RcvBuffer.empty())
{
if (!this->processBuffer(impl->m_RcvBuffer, byteRead))
{
impl->m_RcvBuffer.clear();
// Error set in the function
return false;
}
// If the processing succeed, we erase the buffer part processed.
if (byteRead != 0) { impl->m_RcvBuffer.erase(impl->m_RcvBuffer.begin(), impl->m_RcvBuffer.begin() + static_cast<const long>(byteRead)); }
else
{
// The processing succeed but the byte count read is 0 so more data is waited.
break;
}
}
return true;
}
bool CMessaging::processBuffer(const std::vector<uint8_t>& buffer, size_t& byteRead) const
{
byteRead = 0;
if (buffer.empty()) { return true; }
// First, we try to fromBytes the buffer to found header information
Header header;
if (!header.fromBytes(buffer, byteRead))
{
this->setLastError(Deserialize_Header);
byteRead = size_t(header.getSize());
return false;
}
if (buffer.size() < header.getSize())
{
byteRead = 0;
return true; // Just wait for more data
}
// Try to unpack the object according to the type given by the header.
switch (header.getType())
{
case MessageType_Authentication:
{
AuthenticationMessage authentication;
if (!authentication.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_AuthenticationMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncAuthMutex);
impl->m_IncomingAuthentications.emplace(header.getId(), authentication);
}
break;
case MessageType_ProtocolVersion:
{
CommunicationProtocolVersionMessage communicationProtocolVersion;
if (!communicationProtocolVersion.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ProtocolVersionMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncCommProVerMutex);
impl->m_IncomingCommunicationProtocolVersions.emplace(header.getId(), communicationProtocolVersion);
}
break;
case MessageType_BoxInformation:
{
BoxDescriptionMessage boxDescription;
if (!boxDescription.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_BoxDescriptionMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncBoxDescriptionMutex);
impl->m_IncomingBoxDescriptions.emplace(header.getId(), boxDescription);
}
break;
case MessageType_EBML:
{
EBMLMessage ebml;
if (!ebml.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_EBMLMessage);
return false;
}
if (ebml.getIndex() >= impl->m_BoxDesc.getOutputs()->size()) { this->pushMessage(ErrorMessage(Error_InvalidOutputIndex, header.getId())); }
std::lock_guard<std::mutex> lock(impl->m_IncEBMLMutex);
impl->m_IncomingEBMLs.emplace(header.getId(), ebml);
}
break;
case MessageType_Log:
{
LogMessage log;
if (!log.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_LogMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncLogMutex);
impl->m_IncomingLogs.emplace(header.getId(), log);
}
break;
case MessageType_End: { impl->m_IsEndMessageReceived = true; }
break;
case MessageType_Error:
{
ErrorMessage error;
if (!error.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ErrorMessage);
return false;
}
std::lock_guard<std::mutex> lock(impl->m_IncErrorsMutex);
impl->m_IncomingErrors.emplace(header.getId(), error);
}
break;
case MessageType_Time:
{
TimeMessage timeMessage;
if (!timeMessage.fromBytes(buffer, byteRead))
{
this->pushMessage(ErrorMessage(Error_BadMessage, header.getId()));
this->setLastError(Deserialize_ErrorMessage);
return false;
}
impl->m_Time = timeMessage.getTime();
}
break;
case MessageType_Sync: { impl->m_WasSyncMessageReceived = true; }
break;
case MessageType_Unknown:
case MessageType_Max:
this->pushMessage(ErrorMessage(Error_InvalidMessageType, header.getId()));
this->setLastError(Deserialize_MessageTypeNotSupported);
return false;
}
return true;
}
bool CMessaging::isInErrorState() const { return impl->m_IsInErrorState.load(); }
bool CMessaging::isEndReceived() { return impl->m_IsEndMessageReceived; }
uint64_t CMessaging::getTime() { return impl->m_Time; }
bool CMessaging::pushMessage(const Message& message) const
{
if (this->isInErrorState()) { return false; }
std::vector<uint8_t> messageBuffer = message.toBytes();
const Header header(message.getMessageType(), impl->m_nMessage++, messageBuffer.size());
std::vector<uint8_t> headerBuffer = header.toBytes();
std::lock_guard<std::mutex> lock(impl->m_SendBufferMutex);
impl->m_SendBuffer.insert(impl->m_SendBuffer.end(), headerBuffer.begin(), headerBuffer.end());
impl->m_SendBuffer.insert(impl->m_SendBuffer.end(), messageBuffer.begin(), messageBuffer.end());
return true;
}
std::string CMessaging::getErrorString(const ELibraryError error) { return ERRORS_STRING.at(error); }
bool CMessaging::isConnected() const
{
if (impl->m_Connection == nullptr) { return false; }
return impl->m_Connection->isConnected();
}
void CMessaging::sync() const
{
while (true)
{
if (!this->push())
{
impl->m_IsInErrorState = true;
break;
}
if (!this->pull())
{
impl->m_IsInErrorState = true;
break;
}
if (!this->processIncomingMessages())
{
impl->m_IsInErrorState = true;
break;
}
if (impl->m_IsStopRequested)
{
// Used to be sure to send the end message
this->pull();
break;
}
}
}
void CMessaging::setConnection(Socket::IConnection* connection) const { impl->m_Connection = connection; }
bool CMessaging::startSyncing()
{
impl->m_IsEndMessageReceived = false;
impl->m_SyncThread = std::thread(&CMessaging::sync, this);
impl->m_IsStopRequested = false;
return true;
}
bool CMessaging::stopSyncing() const
{
impl->m_IsStopRequested = true;
if (impl->m_SyncThread.joinable()) { impl->m_SyncThread.join(); }
return true;
}
bool CMessaging::popAuthentication(uint64_t& id, std::string& connectionID)
{
std::lock_guard<std::mutex> lock(impl->m_IncAuthMutex);
if (impl->m_IncomingAuthentications.empty()) { return false; }
id = impl->m_IncomingAuthentications.front().first;
connectionID = impl->m_IncomingAuthentications.front().second.getConnectionID();
impl->m_IncomingAuthentications.pop();
return true;
}
bool CMessaging::popBoxDescriptions(uint64_t& id, BoxDescriptionMessage& boxDescription)
{
std::lock_guard<std::mutex> lock(impl->m_IncBoxDescriptionMutex);
if (impl->m_IncomingBoxDescriptions.empty()) { return false; }
id = impl->m_IncomingBoxDescriptions.front().first;
boxDescription = impl->m_IncomingBoxDescriptions.front().second;
impl->m_IncomingBoxDescriptions.pop();
return true;
}
bool CMessaging::popCommunicationProtocolVersion(uint64_t& id, uint8_t& majorVersion, uint8_t& minorVersion)
{
std::lock_guard<std::mutex> lock(impl->m_IncCommProVerMutex);
if (impl->m_IncomingCommunicationProtocolVersions.empty()) { return false; }
id = impl->m_IncomingCommunicationProtocolVersions.front().first;
majorVersion = impl->m_IncomingCommunicationProtocolVersions.front().second.getMajorVersion();
minorVersion = impl->m_IncomingCommunicationProtocolVersions.front().second.getMinorVersion();
impl->m_IncomingEBMLs.pop();
return true;
}
bool CMessaging::popLog(uint64_t& id, ELogLevel& type, std::string& message)
{
std::lock_guard<std::mutex> lock(impl->m_IncLogMutex);
if (impl->m_IncomingLogs.empty()) { return false; }
id = impl->m_IncomingLogs.front().first;
type = impl->m_IncomingLogs.front().second.getType();
message = impl->m_IncomingLogs.front().second.getMessage();
impl->m_IncomingLogs.pop();
return true;
}
bool CMessaging::popError(uint64_t& id, EError& type, uint64_t& guiltyId)
{
std::lock_guard<std::mutex> lock(impl->m_IncErrorsMutex);
if (impl->m_IncomingErrors.empty()) { return false; }
id = impl->m_IncomingErrors.front().first;
type = impl->m_IncomingErrors.front().second.getType();
guiltyId = impl->m_IncomingErrors.front().second.getGuiltyId();
impl->m_IncomingErrors.pop();
return true;
}
bool CMessaging::popEBML(uint64_t& id, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
std::lock_guard<std::mutex> lock(impl->m_IncEBMLMutex);
if (impl->m_IncomingEBMLs.empty()) { return false; }
id = impl->m_IncomingEBMLs.front().first;
index = impl->m_IncomingEBMLs.front().second.getIndex();
startTime = impl->m_IncomingEBMLs.front().second.getStartTime();
endTime = impl->m_IncomingEBMLs.front().second.getEndTime();
ebml = impl->m_IncomingEBMLs.front().second.getEBML();
impl->m_IncomingEBMLs.pop();
return true;
}
bool CMessaging::popEnd(uint64_t& id)
{
std::lock_guard<std::mutex> lock(impl->m_IncEndMutex);
if (impl->m_IncomingEnds.empty()) { return false; }
id = impl->m_IncomingEnds.front().first;
impl->m_IncomingEnds.pop();
return true;
}
void CMessaging::setConnectionID(const std::string& connectionID) const { impl->m_ConnectionID = connectionID; }
bool CMessaging::waitForSyncMessage()
{
if (impl->m_WasSyncMessageReceived)
{
impl->m_WasSyncMessageReceived = false;
return true;
}
return false;
}
} // namespace Communication
@@ -0,0 +1,172 @@
#include "ovCMessagingClient.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
MessagingClient::MessagingClient() : CMessaging(), m_Client(Socket::createConnectionClient()) {}
MessagingClient::~MessagingClient()
{
this->close();
m_Client->release();
}
bool MessagingClient::connect(const std::string& uri, const size_t port)
{
this->reset();
if (!m_Client->connect(uri.c_str(), port))
{
this->setLastError(Socket_FailedToConnect);
return false;
}
this->setConnection(m_Client);
// Once the connection is done, the client push an authentication message to the server
if (!this->pushAuthentication(impl->m_ConnectionID))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
if (!this->startSyncing())
{
this->close();
return false;
}
// To work, the client need to receive box information message. So we wait to receive the box information message.
// A timeout of 10 seconds is set.
const std::chrono::time_point<std::chrono::system_clock> startClock = std::chrono::system_clock::now();
uint64_t packetId;
while (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startClock).count() < 10)
{
if (this->popBoxDescriptions(packetId, impl->m_BoxDesc))
{
m_BoxDescriptionReceived = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (!m_BoxDescriptionReceived)
{
this->close();
this->setLastError(BoxDescriptionNotReceived);
return false;
}
return true;
}
bool MessagingClient::close() const
{
this->pushMessage(EndMessage());
this->stopSyncing();
if (m_Client != nullptr)
{
if (!m_Client->close())
{
this->setLastError(Socket_FailedToCloseConnection);
return false;
}
}
return true;
}
size_t MessagingClient::getParameterCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getParameters()->size();
}
size_t MessagingClient::getInputCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getInputs()->size();
}
size_t MessagingClient::getOutputCount() const
{
if (!m_BoxDescriptionReceived) { return 0; }
return impl->m_BoxDesc.getOutputs()->size();
}
bool MessagingClient::getParameter(const size_t i, uint64_t& id, uint64_t& type, std::string& name, std::string& value) const
{
if (!m_BoxDescriptionReceived) { return false; }
const std::vector<Parameter>* parameters = impl->m_BoxDesc.getParameters();
if (parameters->size() <= i) { return false; }
id = parameters->at(i).getId();
type = parameters->at(i).getType();
name = parameters->at(i).getName();
value = parameters->at(i).getValue();
return true;
}
bool MessagingClient::getInput(const size_t i, uint64_t& id, uint64_t& type, std::string& name) const
{
if (!m_BoxDescriptionReceived) { return false; }
const std::vector<InputOutput>* inputs = impl->m_BoxDesc.getInputs();
if (inputs->size() <= i) { return false; }
id = inputs->at(i).getId();
type = inputs->at(i).getType();
name = inputs->at(i).getName();
return true;
}
bool MessagingClient::getOutput(const size_t i, uint64_t& id, uint64_t& type, std::string& name) const
{
if (!m_BoxDescriptionReceived)
{
const_cast<MessagingClient*>(this)->setLastError(BoxDescriptionNotReceived);
return false;
}
const std::vector<InputOutput>* outputs = impl->m_BoxDesc.getOutputs();
if (outputs->size() <= i) { return false; }
id = outputs->at(i).getId();
type = outputs->at(i).getType();
name = outputs->at(i).getName();
return true;
}
bool MessagingClient::popError(uint64_t& packetId, EError& type, uint64_t& guiltyId) { return CMessaging::popError(packetId, type, guiltyId); }
bool MessagingClient::popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
return CMessaging::popEBML(packetId, index, startTime, endTime, ebml);
}
bool MessagingClient::pushAuthentication(const std::string& connectionID) const { return this->pushMessage(AuthenticationMessage(connectionID)); }
bool MessagingClient::pushLog(const ELogLevel logLevel, const std::string& log) const { return this->pushMessage(LogMessage(logLevel, log)); }
bool MessagingClient::pushEBML(const size_t index, const uint64_t startTime, const uint64_t endTime,
const std::shared_ptr<const std::vector<uint8_t>>& ebml) const
{
return this->pushMessage(EBMLMessage(index, startTime, endTime, ebml));
}
bool MessagingClient::pushSync() const { return this->pushMessage(SyncMessage()); }
bool MessagingClient::waitForSyncMessage() { return CMessaging::waitForSyncMessage(); }
} // namespace Communication
@@ -0,0 +1,65 @@
#pragma once
#include "ovCMessaging.h"
#include "ovCMessagingProtocol.h"
#include <vector>
#include <array>
#include <string>
#include <cstdint>
#include <thread>
#include <mutex>
#include <queue>
#include <atomic>
namespace Communication {
struct CMessaging::SMessagingImpl
{
std::string m_ConnectionID;
BoxDescriptionMessage m_BoxDesc;
std::atomic<uint64_t> m_Time;
uint64_t m_nMessage = 0;
std::mutex m_IncAuthMutex;
std::queue<std::pair<uint64_t, AuthenticationMessage>> m_IncomingAuthentications;
std::mutex m_IncCommProVerMutex;
std::queue<std::pair<uint64_t, CommunicationProtocolVersionMessage>> m_IncomingCommunicationProtocolVersions;
std::mutex m_IncBoxDescriptionMutex;
std::queue<std::pair<uint64_t, BoxDescriptionMessage>> m_IncomingBoxDescriptions;
std::mutex m_IncEBMLMutex;
std::queue<std::pair<uint64_t, EBMLMessage>> m_IncomingEBMLs;
std::mutex m_IncLogMutex;
std::queue<std::pair<uint64_t, LogMessage>> m_IncomingLogs;
std::mutex m_IncErrorsMutex;
std::queue<std::pair<uint64_t, ErrorMessage>> m_IncomingErrors;
std::mutex m_IncEndMutex;
std::queue<std::pair<uint64_t, EndMessage>> m_IncomingEnds;
static const size_t s_BufferSize = 1024 * 64; //< Empirical value
std::vector<uint8_t> m_RcvBuffer;
std::array<uint8_t, s_BufferSize> m_TempRcvBuffer;
std::mutex m_SendBufferMutex;
std::vector<uint8_t> m_SendBuffer;
Socket::IConnection* m_Connection = nullptr;
mutable std::atomic<ELibraryError> m_LastLibraryError;
std::thread m_SyncThread;
std::atomic<bool> m_IsStopRequested;
std::atomic<bool> m_IsInErrorState;
std::atomic<bool> m_IsEndMessageReceived;
std::atomic<bool> m_WasSyncMessageReceived;
};
} // namespace Communication
@@ -0,0 +1,565 @@
#include <array>
#include <string>
#include <algorithm>
#include <cstring>
#include "ovCMessaging.h"
namespace Communication {
/**
* \brief Copy a string to buffer
*
* \param [out] dest The buffer destination
* \param [out] bufferIndex The index of the buffer where the beginning of the string must be copied.
* \param value The string to copy.
*
* \retval True if it succeeds
* \retval False if it fails.
*/
static bool copyTobuffer(std::vector<uint8_t>& dest, size_t& bufferIndex, const std::string& value)
{
if (dest.size() < bufferIndex + value.size()) { return false; }
memcpy(dest.data() + bufferIndex, value.data(), value.size());
bufferIndex += value.size();
return true;
}
/**
* \brief Copy a value to a buffer.
*
* \param [out] dest Destination for the.
* \param [out] bufferIndex Zero-based index of the buffer.
* \param value The value.
*
* \return True if it succeeds, false if it fails.
*/
template <class T>
static bool copyTobuffer(std::vector<uint8_t>& dest, size_t& bufferIndex, const T& value)
{
if (dest.size() < bufferIndex + sizeof(value)) { return false; }
memcpy(dest.data() + bufferIndex, &value, sizeof(value));
bufferIndex += sizeof(value);
return true;
}
template <class T>
static bool copyToVariable(const std::vector<uint8_t>& src, const size_t bufferIndex, T& destVariable)
{
if (src.size() < bufferIndex + sizeof(destVariable)) { return false; }
memcpy(&destVariable, src.data() + bufferIndex, sizeof(destVariable));
return true;
}
/**
* \brief Convert a buffer to a string
*
* \param src The buffer
* \param bufferIndex The index where to start the convertion.
* \param size The size of the string.
* \param [out] string The string.
*
* \retval True if it succeeds
* \retval False if it fails.
*
* \sa copyToVariable
*/
static bool copyToString(const std::vector<uint8_t>& src, const size_t bufferIndex, const size_t size, std::string& string)
{
if (src.size() < bufferIndex + size) { return false; }
string = std::string(src.begin() + static_cast<const long>(bufferIndex),
src.begin() + static_cast<const long>(bufferIndex) + static_cast<const long>(size));
return true;
}
/******************************************************************************
*
* Header
*
******************************************************************************/
Header::Header() : m_type(MessageType_Unknown), m_id(std::numeric_limits<decltype(m_id)>::max()) { m_isValid = false; }
Header::Header(const EMessageType type, const uint64_t id, const size_t size) : m_type(type), m_id(id), m_size(size) { m_isValid = true; }
std::vector<uint8_t> Header::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_size);
return buffer;
}
bool Header::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
uint8_t typeInt;
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, typeInt)) { return false; }
if (typeInt >= MessageType_Max) { return false; }
m_type = EMessageType(typeInt);
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, m_size)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Authentication
*
******************************************************************************/
std::vector<uint8_t> AuthenticationMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_connectionID.size());
size_t bufferIndex = 0;
const size_t size = m_connectionID.size();
copyTobuffer(buffer, bufferIndex, size);
copyTobuffer(buffer, bufferIndex, m_connectionID);
return buffer;
}
bool AuthenticationMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
size_t passPhraseSize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, passPhraseSize)) { return false; }
if (!copyToString(buffer, bufferIndex + CONNECTION_ID_INDEX, passPhraseSize, m_connectionID)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE + passPhraseSize;
return true;
}
/******************************************************************************
*
* Communication protocol version
*
******************************************************************************/
std::vector<uint8_t> CommunicationProtocolVersionMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_majorVersion);
copyTobuffer(buffer, bufferIndex, m_minorVersion);
return buffer;
}
bool CommunicationProtocolVersionMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + MAJOR_INDEX, m_majorVersion)) { return false; }
if (!copyToVariable(buffer, bufferIndex + MINOR_INDEX, m_minorVersion)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE;
return true;
}
/******************************************************************************
*
* Inout and output
*
******************************************************************************/
InputOutput::InputOutput()
: m_id(std::numeric_limits<decltype(m_id)>::max()), m_type(std::numeric_limits<decltype(m_type)>::max()), m_name(std::string()) { m_isValid = false; }
InputOutput::InputOutput(const uint64_t id, const size_t type, const std::string& name) : m_id(id), m_type(type), m_name(name) { m_isValid = true; }
std::vector<uint8_t> InputOutput::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_name.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_name.size());
copyTobuffer(buffer, bufferIndex, m_name);
return buffer;
}
bool InputOutput::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
size_t nameSize;
if (!copyToVariable(buffer, bufferIndex + NAME_SIZE_INDEX, nameSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, nameSize, m_name)) { return false; }
m_isValid = true;
bufferIndex += MINIMUM_SIZE + nameSize;
return true;
}
/******************************************************************************
*
* Parameter
*
******************************************************************************/
Parameter::Parameter()
: m_id(std::numeric_limits<decltype(m_id)>::max()), m_type(std::numeric_limits<decltype(m_type)>::max()), m_name(std::string()), m_value(std::string())
{
m_isValid = false;
}
Parameter::Parameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value)
: m_id(id), m_type(type), m_name(name), m_value(value) { m_isValid = true; }
std::vector<uint8_t> Parameter::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_name.size() + m_value.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_id);
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_name.size());
copyTobuffer(buffer, bufferIndex, m_value.size());
copyTobuffer(buffer, bufferIndex, m_name);
copyTobuffer(buffer, bufferIndex, m_value);
return buffer;
}
bool Parameter::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + ID_INDEX, m_id)) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
size_t nameSize;
if (!copyToVariable(buffer, bufferIndex + NAME_SIZE_INDEX, nameSize)) { return false; }
size_t valueSize;
if (!copyToVariable(buffer, bufferIndex + VALUE_SIZE_INDEX, valueSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, nameSize, m_name)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX + nameSize, valueSize, m_value)) { return false; }
bufferIndex += MINIMUM_SIZE + nameSize + valueSize;
return true;
}
/******************************************************************************
*
* Box description
*
******************************************************************************/
std::vector<uint8_t> BoxDescriptionMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_inputs.size());
copyTobuffer(buffer, bufferIndex, m_outputs.size());
copyTobuffer(buffer, bufferIndex, m_parameters.size());
for (const InputOutput& input : m_inputs)
{
std::vector<uint8_t> inputBuffer = input.toBytes();
buffer.insert(buffer.end(), inputBuffer.begin(), inputBuffer.end());
}
for (const InputOutput& output : m_outputs)
{
std::vector<uint8_t> outputBuffer = output.toBytes();
buffer.insert(buffer.end(), outputBuffer.begin(), outputBuffer.end());
}
for (const Parameter& parameter : m_parameters)
{
std::vector<uint8_t> parameterBuffer = parameter.toBytes();
buffer.insert(buffer.end(), parameterBuffer.begin(), parameterBuffer.end());
}
return buffer;
}
bool BoxDescriptionMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
size_t inputCount;
size_t outputCount;
size_t parameterCount;
if (!copyToVariable(buffer, bufferIndex + N_INPUT_INDEX, inputCount)) { return false; }
if (!copyToVariable(buffer, bufferIndex + N_OUTPUT_INDEX, outputCount)) { return false; }
if (!copyToVariable(buffer, bufferIndex + N_PARAMETER_INDEX, parameterCount)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_inputs.clear();
m_outputs.clear();
m_parameters.clear();
for (size_t i = 0; i < inputCount; ++i)
{
InputOutput input;
if (!input.fromBytes(buffer, bufferIndex)) { return false; }
m_inputs.push_back(input);
}
for (size_t i = 0; i < outputCount; ++i)
{
InputOutput output;
if (!output.fromBytes(buffer, bufferIndex)) { return false; }
m_outputs.push_back(output);
}
for (size_t i = 0; i < parameterCount; ++i)
{
Parameter parameter;
if (!parameter.fromBytes(buffer, bufferIndex)) { return false; }
m_parameters.push_back(parameter);
}
m_isValid = true;
return true;
}
EMessageType BoxDescriptionMessage::getMessageType() const { return MessageType_BoxInformation; }
bool BoxDescriptionMessage::addInput(const uint64_t id, const size_t type, const std::string& name)
{
const auto it = std::find_if(m_inputs.begin(), m_inputs.end(), [&id](const InputOutput& obj) { return obj.getId() == id; });
if (it != m_inputs.end()) { return false; }
m_inputs.emplace_back(id, type, name);
return true;
}
bool BoxDescriptionMessage::addOutput(const uint64_t id, const size_t type, const std::string& name)
{
const auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [&id](const InputOutput& obj) { return obj.getId() == id; });
if (it != m_outputs.end()) { return false; }
m_outputs.emplace_back(id, type, name);
return true;
}
bool BoxDescriptionMessage::addParameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value)
{
const auto it = std::find_if(m_parameters.begin(), m_parameters.end(), [&id](const Parameter& obj) { return obj.getId() == id; });
if (it != m_parameters.end()) { return false; }
m_parameters.emplace_back(id, type, name, value);
return true;
}
/******************************************************************************
*
* Packet part
*
******************************************************************************/
/******************************************************************************
*
* Log
*
******************************************************************************/
LogMessage::LogMessage() : m_type(LogLevel_Unknown) { m_isValid = false; }
LogMessage::LogMessage(const ELogLevel type, const std::string& message) : m_type(type), m_message(message) { m_isValid = true; }
std::vector<uint8_t> LogMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE + m_message.size());
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_message.size());
copyTobuffer(buffer, bufferIndex, m_message);
return buffer;
}
bool LogMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
// Type
uint8_t typeInt;
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, typeInt)) { return false; }
if (typeInt >= LogLevel_Max) { return false; }
m_type = ELogLevel(typeInt);
// Message size
size_t messageSize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, messageSize)) { return false; }
if (!copyToString(buffer, bufferIndex + NAME_INDEX, messageSize, m_message)) { return false; }
bufferIndex += MINIMUM_SIZE + messageSize;
m_isValid = true;
return true;
}
/******************************************************************************
*
* EBML
*
******************************************************************************/
EBMLMessage::EBMLMessage()
: m_ioIdx(std::numeric_limits<decltype(m_ioIdx)>::max()), m_startTime(std::numeric_limits<decltype(m_startTime)>::max()),
m_endTime(std::numeric_limits<decltype(m_endTime)>::max()) { m_isValid = false; }
EBMLMessage::EBMLMessage(const size_t index, const uint64_t startTime, const uint64_t endTime, const std::shared_ptr<const std::vector<uint8_t>>& ebml)
: m_ioIdx(index), m_startTime(startTime), m_endTime(endTime), m_EBML(ebml) { m_isValid = true; }
std::vector<uint8_t> EBMLMessage::toBytes() const
{
if (!m_isValid) { return std::vector<uint8_t>(); }
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_ioIdx);
copyTobuffer(buffer, bufferIndex, m_startTime);
copyTobuffer(buffer, bufferIndex, m_endTime);
copyTobuffer(buffer, bufferIndex, size_t(m_EBML->size()));
if (!m_EBML->empty()) { buffer.insert(buffer.end(), m_EBML->begin(), m_EBML->end()); }
return buffer;
}
bool EBMLMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + IO_INDEX_INDEX, m_ioIdx)) { return false; }
if (!copyToVariable(buffer, bufferIndex + START_TIME_INDEX, m_startTime)) { return false; }
if (!copyToVariable(buffer, bufferIndex + END_TIME_INDEX, m_endTime)) { return false; }
size_t EBMLsize;
if (!copyToVariable(buffer, bufferIndex + SIZE_INDEX, EBMLsize)) { return false; }
if (buffer.size() < bufferIndex + EBML_INDEX + EBMLsize) { return false; }
m_EBML.reset(new std::vector<uint8_t>(buffer.begin() + static_cast<const long>(bufferIndex) + EBML_INDEX,
buffer.begin() + static_cast<const long>(bufferIndex) + EBML_INDEX + EBMLsize));
bufferIndex += MINIMUM_SIZE + EBMLsize;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Error
*
******************************************************************************/
std::vector<uint8_t> ErrorMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_type);
copyTobuffer(buffer, bufferIndex, m_guiltyId);
return buffer;
}
bool ErrorMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + TYPE_INDEX, m_type)) { return false; }
if (!copyToVariable(buffer, bufferIndex + GUILTY_ID_INDEX, m_guiltyId)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
/******************************************************************************
*
* Time
*
******************************************************************************/
std::vector<uint8_t> TimeMessage::toBytes() const
{
std::vector<uint8_t> buffer(MINIMUM_SIZE);
size_t bufferIndex = 0;
copyTobuffer(buffer, bufferIndex, m_time);
return buffer;
}
bool TimeMessage::fromBytes(const std::vector<uint8_t>& buffer, size_t& bufferIndex)
{
m_isValid = false;
if (buffer.size() < bufferIndex + MINIMUM_SIZE) { return false; }
if (!copyToVariable(buffer, bufferIndex + TIME_INDEX, m_time)) { return false; }
bufferIndex += MINIMUM_SIZE;
m_isValid = true;
return true;
}
} // namespace Communication
@@ -0,0 +1,150 @@
#include "ovCMessagingServer.h"
#include "ovCMessagingImpl.hpp"
namespace Communication {
MessagingServer::~MessagingServer()
{
this->close();
m_Server->release();
}
bool MessagingServer::accept()
{
this->reset();
if (this->isConnected())
{
// A connection is already done to a client
this->setLastError(Socket_ClientAlreadyConnected);
return false;
}
if (!m_Server->isReadyToReceive())
{
this->setLastError(Socket_NoIncomingClientConnection);
return false;
}
m_Client = m_Server->accept();
if (m_Client == nullptr)
{
this->setLastError(Socket_NoIncomingClientConnection);
return false;
}
this->setConnection(m_Client);
this->startSyncing();
if (!impl->m_ConnectionID.empty())
{
// The server has to verify that the client is the one that is waited. So the server wait the authentication packet.
const std::chrono::time_point<std::chrono::system_clock> startClock = std::chrono::system_clock::now();
std::string connectionID;
uint64_t id = 0;
bool isAuthReceived = false;
while (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startClock).count() < 10)
{
if (this->popAuthentication(id, connectionID))
{
isAuthReceived = true;
if (connectionID != impl->m_ConnectionID)
{
this->pushError(Error_AuthenticationFail, id);
this->close();
this->setLastError(BadAuthenticationReceived);
return false;
}
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (!isAuthReceived)
{
this->pushError(Error_AuthenticationRequested, id);
this->close();
this->setLastError(NoAuthenticationReceived);
return false;
}
}
if (!this->pushMessage(CommunicationProtocolVersionMessage(s_CommunicationProtocol_MajorVersion, s_CommunicationProtocol_MinorVersion)))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
if (!this->pushMessage(impl->m_BoxDesc))
{
// Error set in the function
const ELibraryError error = this->getLastError();
this->close();
this->setLastError(error);
return false;
}
return true;
}
bool MessagingServer::close()
{
this->pushMessage(EndMessage());
this->stopSyncing();
bool errorRaised = false;
if (m_Client != nullptr)
{
if (!m_Client->close())
{
errorRaised = true;
this->setLastError(Socket_FailedToCloseConnection);
}
m_Client->release();
m_Client = nullptr;
}
if (!m_Server->close())
{
this->setLastError(Socket_FailedToCloseConnection);
errorRaised = true;
}
return !errorRaised;
}
bool MessagingServer::addParameter(const uint64_t id, const size_t type, const std::string& name, const std::string& value) const
{
return impl->m_BoxDesc.addParameter(id, type, name, value);
}
bool MessagingServer::addInput(const uint64_t id, const size_t type, const std::string& name) const { return impl->m_BoxDesc.addInput(id, type, name); }
bool MessagingServer::addOutput(const uint64_t id, const size_t type, const std::string& name) const { return impl->m_BoxDesc.addOutput(id, type, name); }
bool MessagingServer::popLog(uint64_t& packetId, ELogLevel& type, std::string& message) { return CMessaging::popLog(packetId, type, message); }
bool MessagingServer::popEBML(uint64_t& packetId, size_t& index, uint64_t& startTime, uint64_t& endTime, std::shared_ptr<const std::vector<uint8_t>>& ebml)
{
return CMessaging::popEBML(packetId, index, startTime, endTime, ebml);
}
bool MessagingServer::pushError(const EError error, const uint64_t guiltyId) const { return this->pushMessage(ErrorMessage(error, guiltyId)); }
bool MessagingServer::pushEBML(const size_t index, const uint64_t startTime, const uint64_t endTime,
const std::shared_ptr<const std::vector<uint8_t>>& ebml) const
{
return this->pushMessage(EBMLMessage(index, startTime, endTime, ebml));
}
} // namespace Communication