Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions daemon/connect/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024-2025 Denis <denis@nzbget.com>
* Copyright (C) 2024-2026 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -172,27 +172,26 @@ bool Connection::Connect()
else
{
DoDisconnect();
m_status = csDisconnected;
}

return res;
}

bool Connection::Disconnect()
void Connection::Disconnect()
{
debug("Disconnecting");

if (m_status == csDisconnected)
{
return true;
return;
}

bool res = DoDisconnect();
DoDisconnect();

m_status = csDisconnected;
m_socket = INVALID_SOCKET;
m_bufAvail = 0;

return res;
}

bool Connection::Bind()
Expand Down Expand Up @@ -853,7 +852,7 @@ bool Connection::ConnectWithTimeout(void* address, int address_len)
return true;
}

bool Connection::DoDisconnect()
void Connection::DoDisconnect()
{
debug("Do disconnecting");

Expand Down Expand Up @@ -883,7 +882,6 @@ bool Connection::DoDisconnect()
}

m_status = csDisconnected;
return true;
}

void Connection::ReadBuffer(char** buffer, int *bufLen)
Expand Down
6 changes: 3 additions & 3 deletions daemon/connect/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024-2025 Denis <denis@nzbget.com>
* Copyright (C) 2024-2026 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -55,7 +55,7 @@ class Connection
static void Init();
static void Final();
virtual bool Connect();
virtual bool Disconnect();
virtual void Disconnect();
bool Bind();
bool Send(const char* buffer, int size);
bool Recv(char* buffer, int size);
Expand Down Expand Up @@ -130,7 +130,7 @@ class Connection
virtual void PrintError(const char* errMsg);
int GetLastNetworkError();
bool DoConnect();
bool DoDisconnect();
void DoDisconnect();
bool InitSocketOpts(SOCKET socket);
bool ConnectWithTimeout(void* address, int address_len);
#ifndef HAVE_GETADDRINFO
Expand Down
2 changes: 1 addition & 1 deletion daemon/nntp/ArticleDownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ void ArticleDownloader::Run()
}
}

FreeConnection(status == adFinished);
FreeConnection(status == adFinished || (status == adRetry && m_connection && m_connection->GetStatus() == Connection::csConnected));

if (m_articleWriter.GetDuplicate())
{
Expand Down
34 changes: 34 additions & 0 deletions daemon/nntp/NewsServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2026 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -35,3 +36,36 @@ NewsServer::NewsServer(int id, bool active, const char* name, const char* host,
m_name.Format("server%i", id);
}
}

void NewsServer::DelayConnect()
{
constexpr std::chrono::milliseconds CONNECTION_DELAY{15};
using Clock = std::chrono::steady_clock;

Clock::time_point deadline{Clock::time_point::max()};

{
std::lock_guard<std::mutex> lock(m_connectMutex);

auto now = Clock::now();
if (m_lastConnectTime == Clock::time_point::min())
{
m_lastConnectTime = now;
return;
}

if (now >= m_lastConnectTime + CONNECTION_DELAY)
{
m_lastConnectTime = now;
return;
}

m_lastConnectTime += CONNECTION_DELAY;
deadline = m_lastConnectTime;
}

if (deadline != Clock::time_point::max())
{
std::this_thread::sleep_until(deadline);
}
}
14 changes: 13 additions & 1 deletion daemon/nntp/NewsServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2025 Denis <denis@nzbget.com>
* Copyright (C) 2025-2026 Denis <denis@nzbget.com>
*
* 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
Expand All @@ -28,6 +28,8 @@
#ifndef NEWSSERVER_H
#define NEWSSERVER_H

#include <chrono>
#include <mutex>
#include "NString.h"

class NewsServer
Expand Down Expand Up @@ -62,6 +64,14 @@ class NewsServer
void SetBlockTime(time_t blockTime) { m_blockTime = blockTime; }
unsigned int GetCertVerificationLevel() const { return m_certVerificationfLevel; }

/**
* @brief Delays the connection attempt to enforce a minimum interval of 15ms between connections.
*
* This rate limiter prevents "502 Too Many Connections" and "481 Exceeded Maximum Connections"
* errors on Usenet servers by spacing out rapid connection attempts.
*/
void DelayConnect();

private:
int m_id;
int m_stateId = 0;
Expand All @@ -83,6 +93,8 @@ class NewsServer
bool m_optional = false;
time_t m_blockTime = 0;
unsigned int m_certVerificationfLevel;
std::chrono::steady_clock::time_point m_lastConnectTime{std::chrono::steady_clock::time_point::min()};
std::mutex m_connectMutex;
};

typedef std::vector<std::unique_ptr<NewsServer>> Servers;
Expand Down
10 changes: 8 additions & 2 deletions daemon/nntp/NntpConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024-2026 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -195,6 +196,11 @@ bool NntpConnection::Connect()
return true;
}

if (m_newsServer)
{
m_newsServer->DelayConnect();
}

if (!Connection::Connect())
{
return false;
Expand Down Expand Up @@ -227,14 +233,14 @@ bool NntpConnection::Connect()
return true;
}

bool NntpConnection::Disconnect()
void NntpConnection::Disconnect()
{
if (m_status == csConnected)
{
Request("quit\r\n");
m_activeGroup = nullptr;
}
return Connection::Disconnect();
Connection::Disconnect();
}

void NntpConnection::ReportErrorAnswer(const char* msgPrefix, const char* answer)
Expand Down
5 changes: 3 additions & 2 deletions daemon/nntp/NntpConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2026 Denis <denis@nzbget.com>
*
* 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
Expand Down Expand Up @@ -30,8 +31,8 @@ class NntpConnection : public Connection
{
public:
NntpConnection(NewsServer* newsServer);
virtual bool Connect();
virtual bool Disconnect();
bool Connect() override;
void Disconnect() override;
NewsServer* GetNewsServer() { return m_newsServer; }
const char* Request(const char* req);
const char* JoinGroup(const char* grp);
Expand Down
25 changes: 14 additions & 11 deletions daemon/nntp/ServerPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024 Denis <denis@nzbget.com>
* Copyright (C) 2024-2026 Denis <denis@nzbget.com>
*
* 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
Expand All @@ -23,8 +23,9 @@
#include "nzbget.h"
#include "ServerPool.h"
#include "Util.h"
#include "QueueCoordinator.h"

static const int CONNECTION_HOLD_SECODNS = 5;
static constexpr int CONNECTION_HOLD_SECONDS = 30;

void ServerPool::PooledConnection::SetFreeTimeNow()
{
Expand Down Expand Up @@ -113,7 +114,7 @@ void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");

Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);

NormalizeLevels();
m_levels.clear();
Expand Down Expand Up @@ -162,7 +163,7 @@ void ServerPool::InitConnections()
*/
NntpConnection* ServerPool::GetConnection(int level, NewsServer* wantServer, RawServerList* ignoreServers)
{
Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);

for (; level < (int)m_levels.size() && m_levels[level] > 0; level++)
{
Expand Down Expand Up @@ -256,7 +257,7 @@ void ServerPool::FreeConnection(NntpConnection* connection, bool used)
debug("Freeing used connection");
}

Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);

((PooledConnection*)connection)->SetInUse(false);
if (used)
Expand All @@ -274,7 +275,7 @@ void ServerPool::BlockServer(NewsServer* newsServer)
{
bool newBlock = false;
{
Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);
time_t curTime = Util::CurrentTime();
newBlock = newsServer->GetBlockTime() != curTime;
newsServer->SetBlockTime(curTime);
Expand All @@ -301,7 +302,7 @@ bool ServerPool::IsServerBlocked(NewsServer* newsServer)

void ServerPool::CloseUnusedConnections()
{
Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);

time_t curtime = Util::CurrentTime();

Expand Down Expand Up @@ -350,9 +351,11 @@ void ServerPool::CloseUnusedConnections()
}
}

// if there are no in-use connections on the level and the hold time out has
// expired - close all connections of the level.
if (!hasInUseConnections && inactiveTime > CONNECTION_HOLD_SECODNS)
// If there are no in-use connections on the level and the hold timeout has expired,
// close all connections of that level. For the primary level (level 0), we keep
// connections alive as long as the queue has more jobs
if (!hasInUseConnections && inactiveTime > CONNECTION_HOLD_SECONDS &&
(level > 0 || !g_QueueCoordinator || !g_QueueCoordinator->HasMoreJobs()))
{
for (PooledConnection* connection : &m_connections)
{
Expand Down Expand Up @@ -381,7 +384,7 @@ void ServerPool::LogDebugInfo()

info(" Max-Level: %i", m_maxNormLevel);

Guard guard(m_connectionsMutex);
std::lock_guard<std::mutex> guard(m_connectionsMutex);

time_t curTime = Util::CurrentTime();

Expand Down
14 changes: 6 additions & 8 deletions daemon/nntp/ServerPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2024 Denis <denis@nzbget.com>
* Copyright (C) 2024-2026 Denis <denis@nzbget.com>
*
* 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
Expand All @@ -24,12 +24,10 @@
#define SERVERPOOL_H

#include "Log.h"
#include "Container.h"
#include "Thread.h"
#include "NewsServer.h"
#include "NntpConnection.h"

class ServerPool : public Debuggable
class ServerPool final : public Debuggable
{
public:
typedef std::vector<NewsServer*> RawServerList;
Expand All @@ -50,10 +48,10 @@ class ServerPool : public Debuggable
bool IsServerBlocked(NewsServer* newsServer);

protected:
virtual void LogDebugInfo();
void LogDebugInfo() override;

private:
class PooledConnection : public NntpConnection
class PooledConnection final : public NntpConnection
{
public:
using NntpConnection::NntpConnection;
Expand All @@ -62,8 +60,8 @@ class ServerPool : public Debuggable
time_t GetFreeTime() { return m_freeTime; }
void SetFreeTimeNow();
private:
bool m_inUse = false;
time_t m_freeTime = 0;
bool m_inUse = false;
};

typedef std::vector<int> Levels;
Expand All @@ -73,8 +71,8 @@ class ServerPool : public Debuggable
RawServerList m_sortedServers;
Connections m_connections;
Levels m_levels;
std::mutex m_connectionsMutex;
int m_maxNormLevel = 0;
Mutex m_connectionsMutex;
int m_timeout = 60;
int m_retryInterval = 0;
int m_generation = 0;
Expand Down
Loading
Loading