Improve connection handling

1. Previously unhandled connections will stay in pending state. It won't
be closed until timeout happened. This may lead to wasting system
resources. Now the (over-limit) connection is actively rejected.

2. When out-of-memory occurs here, reject the new connection instead of
throwing exception and crash.

3. Also clean up some unused bits.

PR #20961.
This commit is contained in:
Chocobo1 2024-06-20 12:13:27 +08:00 committed by sledgehammer999
parent c2cf898ccd
commit 0f5a27ed50
No known key found for this signature in database
GPG key ID: 6E4A2D025B7CC9A2
5 changed files with 32 additions and 34 deletions

View file

@ -40,7 +40,6 @@
#include <QRegularExpression>
#include <QThread>
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"

View file

@ -44,6 +44,7 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
, m_requestHandler(requestHandler)
{
m_socket->setParent(this);
connect(m_socket, &QAbstractSocket::disconnected, this, &Connection::closed);
// reserve common size for requests, don't use the max allowed size which is too big for
// memory constrained platforms
@ -62,11 +63,6 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
});
}
Connection::~Connection()
{
m_socket->close();
}
void Connection::read()
{
// reuse existing buffer and avoid unnecessary memory allocation/relocation
@ -182,11 +178,6 @@ bool Connection::hasExpired(const qint64 timeout) const
&& m_idleTimer.hasExpired(timeout);
}
bool Connection::isClosed() const
{
return (m_socket->state() == QAbstractSocket::UnconnectedState);
}
bool Connection::acceptsGzipEncoding(QString codings)
{
// [rfc7231] 5.3.4. Accept-Encoding

View file

@ -47,10 +47,11 @@ namespace Http
public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);
~Connection();
bool hasExpired(qint64 timeout) const;
bool isClosed() const;
signals:
void closed();
private:
static bool acceptsGzipEncoding(QString codings);

View file

@ -32,7 +32,10 @@
#include <algorithm>
#include <chrono>
#include <memory>
#include <new>
#include <QtLogging>
#include <QNetworkProxy>
#include <QSslCipher>
#include <QSslConfiguration>
@ -40,7 +43,6 @@
#include <QStringList>
#include <QTimer>
#include "base/algorithm.h"
#include "base/global.h"
#include "base/utils/net.h"
#include "base/utils/sslkey.h"
@ -113,32 +115,38 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
void Server::incomingConnection(const qintptr socketDescriptor)
{
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
QTcpSocket *serverSocket = nullptr;
if (m_https)
serverSocket = new QSslSocket(this);
else
serverSocket = new QTcpSocket(this);
std::unique_ptr<QTcpSocket> serverSocket = m_https ? std::make_unique<QSslSocket>(this) : std::make_unique<QTcpSocket>(this);
if (!serverSocket->setSocketDescriptor(socketDescriptor))
return;
if (m_connections.size() >= CONNECTIONS_LIMIT)
{
delete serverSocket;
qWarning("Too many connections. Exceeded CONNECTIONS_LIMIT (%d). Connection closed.", CONNECTIONS_LIMIT);
return;
}
if (m_https)
try
{
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
if (m_https)
{
auto *sslSocket = static_cast<QSslSocket *>(serverSocket.get());
sslSocket->setProtocol(QSsl::SecureProtocols);
sslSocket->setPrivateKey(m_key);
sslSocket->setLocalCertificateChain(m_certificates);
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
sslSocket->startServerEncryption();
}
auto *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.insert(c);
connect(serverSocket, &QAbstractSocket::disconnected, this, [c, this]() { removeConnection(c); });
auto *connection = new Connection(serverSocket.release(), m_requestHandler, this);
m_connections.insert(connection);
connect(connection, &Connection::closed, this, [this, connection] { removeConnection(connection); });
}
catch (const std::bad_alloc &exception)
{
// drop the connection instead of throwing exception and crash
qWarning("Failed to allocate memory for HTTP connection. Connection closed.");
return;
}
}
void Server::removeConnection(Connection *connection)

View file

@ -39,7 +39,6 @@
#include <QUrl>
#include <QVBoxLayout>
#include "base/algorithm.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentrystatus.h"