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 <QRegularExpression>
#include <QThread> #include <QThread>
#include "base/algorithm.h"
#include "base/exceptions.h" #include "base/exceptions.h"
#include "base/global.h" #include "base/global.h"
#include "base/logger.h" #include "base/logger.h"

View file

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

View file

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

View file

@ -32,7 +32,10 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <memory>
#include <new>
#include <QtLogging>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QSslCipher> #include <QSslCipher>
#include <QSslConfiguration> #include <QSslConfiguration>
@ -40,7 +43,6 @@
#include <QStringList> #include <QStringList>
#include <QTimer> #include <QTimer>
#include "base/algorithm.h"
#include "base/global.h" #include "base/global.h"
#include "base/utils/net.h" #include "base/utils/net.h"
#include "base/utils/sslkey.h" #include "base/utils/sslkey.h"
@ -113,32 +115,38 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
void Server::incomingConnection(const qintptr socketDescriptor) void Server::incomingConnection(const qintptr socketDescriptor)
{ {
if (m_connections.size() >= CONNECTIONS_LIMIT) return; std::unique_ptr<QTcpSocket> serverSocket = m_https ? std::make_unique<QSslSocket>(this) : std::make_unique<QTcpSocket>(this);
QTcpSocket *serverSocket = nullptr;
if (m_https)
serverSocket = new QSslSocket(this);
else
serverSocket = new QTcpSocket(this);
if (!serverSocket->setSocketDescriptor(socketDescriptor)) 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; return;
} }
if (m_https) try
{ {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols); if (m_https)
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key); {
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates); auto *sslSocket = static_cast<QSslSocket *>(serverSocket.get());
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone); sslSocket->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption(); sslSocket->setPrivateKey(m_key);
} sslSocket->setLocalCertificateChain(m_certificates);
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
sslSocket->startServerEncryption();
}
auto *c = new Connection(serverSocket, m_requestHandler, this); auto *connection = new Connection(serverSocket.release(), m_requestHandler, this);
m_connections.insert(c); m_connections.insert(connection);
connect(serverSocket, &QAbstractSocket::disconnected, this, [c, this]() { removeConnection(c); }); 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) void Server::removeConnection(Connection *connection)

View file

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