mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-12-15 20:51:39 +03:00
Implement http persistence connection
Max simultaneous connection limit set to 500 This also release allocated memory of Connection instances at runtime instead of at program shutdown.
This commit is contained in:
parent
ae0a9d74c4
commit
0b28fb6c6b
4 changed files with 73 additions and 23 deletions
|
@ -29,14 +29,14 @@
|
||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QTcpSocket>
|
#include "connection.h"
|
||||||
#include <QDebug>
|
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include "types.h"
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "irequesthandler.h"
|
||||||
#include "requestparser.h"
|
#include "requestparser.h"
|
||||||
#include "responsegenerator.h"
|
#include "responsegenerator.h"
|
||||||
#include "irequesthandler.h"
|
|
||||||
#include "connection.h"
|
|
||||||
|
|
||||||
using namespace Http;
|
using namespace Http;
|
||||||
|
|
||||||
|
@ -46,27 +46,33 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
|
||||||
, m_requestHandler(requestHandler)
|
, m_requestHandler(requestHandler)
|
||||||
{
|
{
|
||||||
m_socket->setParent(this);
|
m_socket->setParent(this);
|
||||||
|
m_idleTimer.start();
|
||||||
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
|
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
|
||||||
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection()
|
Connection::~Connection()
|
||||||
{
|
{
|
||||||
|
m_socket->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::read()
|
void Connection::read()
|
||||||
{
|
{
|
||||||
m_receivedData.append(m_socket->readAll());
|
m_idleTimer.restart();
|
||||||
|
|
||||||
|
m_receivedData.append(m_socket->readAll());
|
||||||
Request request;
|
Request request;
|
||||||
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request);
|
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request);
|
||||||
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case RequestParser::IncompleteRequest:
|
case RequestParser::IncompleteRequest:
|
||||||
// Partial request waiting for the rest
|
// Partial request waiting for the rest
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestParser::BadRequest:
|
case RequestParser::BadRequest:
|
||||||
sendResponse(Response(400, "Bad Request"));
|
sendResponse(Response(400, "Bad Request"));
|
||||||
|
m_receivedData.clear();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestParser::NoError:
|
case RequestParser::NoError:
|
||||||
Environment env;
|
Environment env;
|
||||||
env.clientAddress = m_socket->peerAddress();
|
env.clientAddress = m_socket->peerAddress();
|
||||||
|
@ -74,6 +80,7 @@ void Connection::read()
|
||||||
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
|
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
|
||||||
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
|
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
|
||||||
sendResponse(response);
|
sendResponse(response);
|
||||||
|
m_receivedData.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +88,16 @@ void Connection::read()
|
||||||
void Connection::sendResponse(const Response &response)
|
void Connection::sendResponse(const Response &response)
|
||||||
{
|
{
|
||||||
m_socket->write(ResponseGenerator::generate(response));
|
m_socket->write(ResponseGenerator::generate(response));
|
||||||
m_socket->disconnectFromHost();
|
}
|
||||||
|
|
||||||
|
bool Connection::hasExpired(const qint64 timeout) const
|
||||||
|
{
|
||||||
|
return m_idleTimer.hasExpired(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Connection::isClosed() const
|
||||||
|
{
|
||||||
|
return (m_socket->state() == QAbstractSocket::UnconnectedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::acceptsGzipEncoding(const QString &encoding)
|
bool Connection::acceptsGzipEncoding(const QString &encoding)
|
||||||
|
|
|
@ -33,12 +33,12 @@
|
||||||
#ifndef HTTP_CONNECTION_H
|
#ifndef HTTP_CONNECTION_H
|
||||||
#define HTTP_CONNECTION_H
|
#define HTTP_CONNECTION_H
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QTcpSocket;
|
class QTcpSocket;
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
@ -53,6 +53,9 @@ namespace Http
|
||||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||||
~Connection();
|
~Connection();
|
||||||
|
|
||||||
|
bool hasExpired(qint64 timeout) const;
|
||||||
|
bool isClosed() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void read();
|
void read();
|
||||||
|
|
||||||
|
@ -63,6 +66,7 @@ namespace Http
|
||||||
QTcpSocket *m_socket;
|
QTcpSocket *m_socket;
|
||||||
IRequestHandler *m_requestHandler;
|
IRequestHandler *m_requestHandler;
|
||||||
QByteArray m_receivedData;
|
QByteArray m_receivedData;
|
||||||
|
QElapsedTimer m_idleTimer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,10 @@
|
||||||
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <QMutableListIterator>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
|
@ -41,6 +43,10 @@
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
|
||||||
|
static const int KEEP_ALIVE_DURATION = 7; // seconds
|
||||||
|
static const int CONNECTIONS_LIMIT = 500;
|
||||||
|
static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
|
||||||
|
|
||||||
using namespace Http;
|
using namespace Http;
|
||||||
|
|
||||||
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||||
|
@ -54,6 +60,10 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
QSslSocket::setDefaultCiphers(safeCipherList());
|
QSslSocket::setDefaultCiphers(safeCipherList());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QTimer *dropConnectionTimer = new QTimer(this);
|
||||||
|
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
|
||||||
|
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server()
|
Server::~Server()
|
||||||
|
@ -62,6 +72,8 @@ Server::~Server()
|
||||||
|
|
||||||
void Server::incomingConnection(qintptr socketDescriptor)
|
void Server::incomingConnection(qintptr socketDescriptor)
|
||||||
{
|
{
|
||||||
|
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
|
||||||
|
|
||||||
QTcpSocket *serverSocket;
|
QTcpSocket *serverSocket;
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
if (m_https)
|
if (m_https)
|
||||||
|
@ -70,20 +82,34 @@ void Server::incomingConnection(qintptr socketDescriptor)
|
||||||
#endif
|
#endif
|
||||||
serverSocket = new QTcpSocket(this);
|
serverSocket = new QTcpSocket(this);
|
||||||
|
|
||||||
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
|
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
|
||||||
#ifndef QT_NO_OPENSSL
|
delete serverSocket;
|
||||||
if (m_https) {
|
return;
|
||||||
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();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
new Connection(serverSocket, m_requestHandler, this);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
serverSocket->deleteLater();
|
#ifndef QT_NO_OPENSSL
|
||||||
|
if (m_https) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Connection *c = new Connection(serverSocket, m_requestHandler, this);
|
||||||
|
m_connections.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::dropTimedOutConnection()
|
||||||
|
{
|
||||||
|
QMutableListIterator<Connection *> i(m_connections);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
auto connection = i.next();
|
||||||
|
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
|
||||||
|
delete connection;
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,14 @@ namespace Http
|
||||||
void disableHttps();
|
void disableHttps();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void dropTimedOutConnection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void incomingConnection(qintptr socketDescriptor);
|
void incomingConnection(qintptr socketDescriptor);
|
||||||
|
|
||||||
IRequestHandler *m_requestHandler;
|
IRequestHandler *m_requestHandler;
|
||||||
|
QList<Connection *> m_connections; // for tracking persistence connections
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
QList<QSslCipher> safeCipherList() const;
|
QList<QSslCipher> safeCipherList() const;
|
||||||
|
|
Loading…
Reference in a new issue