From 0b28fb6c6b0f4278cf8bb021a8c18314b591192b Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Tue, 11 Apr 2017 12:07:17 +0800 Subject: [PATCH] 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. --- src/base/http/connection.cpp | 32 ++++++++++++++++------ src/base/http/connection.h | 8 ++++-- src/base/http/server.cpp | 52 +++++++++++++++++++++++++++--------- src/base/http/server.h | 4 +++ 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/base/http/connection.cpp b/src/base/http/connection.cpp index ad757817c..ce81c6807 100644 --- a/src/base/http/connection.cpp +++ b/src/base/http/connection.cpp @@ -29,14 +29,14 @@ * Contact : chris@qbittorrent.org */ -#include -#include +#include "connection.h" + #include -#include "types.h" +#include + +#include "irequesthandler.h" #include "requestparser.h" #include "responsegenerator.h" -#include "irequesthandler.h" -#include "connection.h" using namespace Http; @@ -46,27 +46,33 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj , m_requestHandler(requestHandler) { m_socket->setParent(this); + m_idleTimer.start(); connect(m_socket, SIGNAL(readyRead()), SLOT(read())); - connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater())); } Connection::~Connection() { + m_socket->close(); } void Connection::read() { - m_receivedData.append(m_socket->readAll()); + m_idleTimer.restart(); + m_receivedData.append(m_socket->readAll()); Request request; RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); + switch (err) { case RequestParser::IncompleteRequest: // Partial request waiting for the rest break; + case RequestParser::BadRequest: sendResponse(Response(400, "Bad Request")); + m_receivedData.clear(); break; + case RequestParser::NoError: Environment env; env.clientAddress = m_socket->peerAddress(); @@ -74,6 +80,7 @@ void Connection::read() if (acceptsGzipEncoding(request.headers["accept-encoding"])) response.headers[HEADER_CONTENT_ENCODING] = "gzip"; sendResponse(response); + m_receivedData.clear(); break; } } @@ -81,7 +88,16 @@ void Connection::read() void Connection::sendResponse(const Response &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) diff --git a/src/base/http/connection.h b/src/base/http/connection.h index 95498b1e6..134d30c92 100644 --- a/src/base/http/connection.h +++ b/src/base/http/connection.h @@ -33,12 +33,12 @@ #ifndef HTTP_CONNECTION_H #define HTTP_CONNECTION_H +#include #include + #include "types.h" -QT_BEGIN_NAMESPACE class QTcpSocket; -QT_END_NAMESPACE namespace Http { @@ -53,6 +53,9 @@ namespace Http Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); ~Connection(); + bool hasExpired(qint64 timeout) const; + bool isClosed() const; + private slots: void read(); @@ -63,6 +66,7 @@ namespace Http QTcpSocket *m_socket; IRequestHandler *m_requestHandler; QByteArray m_receivedData; + QElapsedTimer m_idleTimer; }; } diff --git a/src/base/http/server.cpp b/src/base/http/server.cpp index 35be36d98..b3cd02168 100644 --- a/src/base/http/server.cpp +++ b/src/base/http/server.cpp @@ -30,8 +30,10 @@ #include "server.h" +#include #include #include +#include #ifndef QT_NO_OPENSSL #include @@ -41,6 +43,10 @@ #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; Server::Server(IRequestHandler *requestHandler, QObject *parent) @@ -54,6 +60,10 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent) #ifndef QT_NO_OPENSSL QSslSocket::setDefaultCiphers(safeCipherList()); #endif + + QTimer *dropConnectionTimer = new QTimer(this); + connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection); + dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000); } Server::~Server() @@ -62,6 +72,8 @@ Server::~Server() void Server::incomingConnection(qintptr socketDescriptor) { + if (m_connections.size() >= CONNECTIONS_LIMIT) return; + QTcpSocket *serverSocket; #ifndef QT_NO_OPENSSL if (m_https) @@ -70,20 +82,34 @@ void Server::incomingConnection(qintptr socketDescriptor) #endif serverSocket = new QTcpSocket(this); - if (serverSocket->setSocketDescriptor(socketDescriptor)) { -#ifndef QT_NO_OPENSSL - if (m_https) { - static_cast(serverSocket)->setProtocol(QSsl::SecureProtocols); - static_cast(serverSocket)->setPrivateKey(m_key); - static_cast(serverSocket)->setLocalCertificateChain(m_certificates); - static_cast(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone); - static_cast(serverSocket)->startServerEncryption(); - } -#endif - new Connection(serverSocket, m_requestHandler, this); + if (!serverSocket->setSocketDescriptor(socketDescriptor)) { + delete serverSocket; + return; } - else { - serverSocket->deleteLater(); + +#ifndef QT_NO_OPENSSL + if (m_https) { + static_cast(serverSocket)->setProtocol(QSsl::SecureProtocols); + static_cast(serverSocket)->setPrivateKey(m_key); + static_cast(serverSocket)->setLocalCertificateChain(m_certificates); + static_cast(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone); + static_cast(serverSocket)->startServerEncryption(); + } +#endif + + Connection *c = new Connection(serverSocket, m_requestHandler, this); + m_connections.append(c); +} + +void Server::dropTimedOutConnection() +{ + QMutableListIterator i(m_connections); + while (i.hasNext()) { + auto connection = i.next(); + if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) { + delete connection; + i.remove(); + } } } diff --git a/src/base/http/server.h b/src/base/http/server.h index 31f33b735..d9b9ae19e 100644 --- a/src/base/http/server.h +++ b/src/base/http/server.h @@ -60,10 +60,14 @@ namespace Http void disableHttps(); #endif + private slots: + void dropTimedOutConnection(); + private: void incomingConnection(qintptr socketDescriptor); IRequestHandler *m_requestHandler; + QList m_connections; // for tracking persistence connections #ifndef QT_NO_OPENSSL QList safeCipherList() const;