Files
i2pd/libi2pd_client/I2PService.cpp
T

190 lines
5.4 KiB
C++

/*
* Copyright (c) 2013-2026, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
#include <boost/asio/error.hpp>
namespace i2p
{
namespace client
{
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519;
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)),
m_ReadyTimer(m_LocalDestination->GetService()), m_ReadyTimerTriggered(false),
m_ConnectTimeout(0), m_CloseIdleTime (0), m_LastActivityTime (0),
isUpdated (true)
{
m_LocalDestination->Acquire ();
}
I2PService::I2PService (i2p::data::SigningKeyType kt):
I2PService (i2p::client::context.CreateNewLocalDestination (false, kt))
{
}
I2PService::~I2PService ()
{
ClearHandlers ();
if (m_LocalDestination) m_LocalDestination->Release ();
}
void I2PService::ClearHandlers ()
{
if(m_ConnectTimeout)
m_ReadyTimer.cancel();
std::unique_lock<std::mutex> l(m_HandlersMutex);
for (auto it: m_Handlers)
it->Terminate ();
m_Handlers.clear();
}
void I2PService::SetConnectTimeout(uint64_t timeout)
{
m_ConnectTimeout = timeout;
}
void I2PService::SetCloseIdleTime (uint64_t idleTime)
{
if (idleTime > 0 && idleTime < I2P_SERVICE_MIN_CLOSE_IDLE_TIME) idleTime = I2P_SERVICE_MIN_CLOSE_IDLE_TIME;
m_CloseIdleTime = idleTime;
if (m_CloseIdleTime)
{
if (!m_IdleCheckTimer) m_IdleCheckTimer.reset (new boost::asio::steady_timer(m_LocalDestination->GetService ()));
ScheduleIdleCheckTimer ();
}
}
void I2PService::UpdateLastActivityTime ()
{
if (m_CloseIdleTime)
{
m_LastActivityTime = i2p::util::GetMonotonicMilliseconds ();
if (m_LocalDestination->IsIdling ())
{
m_LocalDestination->SetIsIdling (false);
ScheduleIdleCheckTimer ();
}
}
}
void I2PService::AddReadyCallback(ReadyCallback cb)
{
uint64_t now = i2p::util::GetMonotonicSeconds ();
uint64_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT;
LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now);
m_ReadyCallbacks.push_back({cb, tm});
if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer();
}
void I2PService::TriggerReadyCheckTimer()
{
m_ReadyTimer.expires_after(std::chrono::seconds (I2P_SERVICE_READINESS_CHECK_INTERVAL));
m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, shared_from_this (), std::placeholders::_1));
m_ReadyTimerTriggered = true;
}
void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec)
{
bool isReady = (!ec) ? m_LocalDestination->IsReady() : false;
if(ec || isReady)
{
for(auto & itr : m_ReadyCallbacks)
itr.first(ec);
m_ReadyCallbacks.clear();
}
else if(!isReady)
{
// expire timed out requests
uint64_t now = i2p::util::GetMonotonicSeconds ();
auto itr = m_ReadyCallbacks.begin();
while(itr != m_ReadyCallbacks.end())
{
if(itr->second != NEVER_TIMES_OUT && now >= itr->second)
{
itr->first(boost::asio::error::timed_out);
itr = m_ReadyCallbacks.erase(itr);
}
else
++itr;
}
}
if(!ec && m_ReadyCallbacks.size())
TriggerReadyCheckTimer();
else
m_ReadyTimerTriggered = false;
}
void I2PService::ScheduleIdleCheckTimer ()
{
if (!m_IdleCheckTimer || !m_CloseIdleTime) return;
m_IdleCheckTimer->expires_after(std::chrono::milliseconds (m_CloseIdleTime/2));
m_IdleCheckTimer->async_wait(std::bind(&I2PService::HandleIdleCheckTimer, shared_from_this (), std::placeholders::_1));
}
void I2PService::HandleIdleCheckTimer(const boost::system::error_code & ec)
{
if (ec != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetMonotonicMilliseconds ();
if (ts > m_LastActivityTime + m_CloseIdleTime)
m_LocalDestination->SetIsIdling (true);
else
ScheduleIdleCheckTimer ();
}
}
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port) {
assert(streamRequestComplete);
auto address = i2p::client::context.GetAddressBook ().GetAddress (dest);
if (address)
CreateStream(streamRequestComplete, address, port);
else
{
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
streamRequestComplete (nullptr);
}
}
void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr<const Address> address, uint16_t port)
{
if(m_ConnectTimeout && !m_LocalDestination->IsReady())
{
AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec)
{
if(ec)
{
LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message());
streamRequestComplete(nullptr);
}
else
{
if (address->IsIdentHash ())
this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port);
else
this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
});
}
else
{
if (address->IsIdentHash ())
m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port);
else
m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
}
}
}