Browse Source

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.
adaptive-webui-19844
Chocobo1 8 years ago
parent
commit
0b28fb6c6b
  1. 32
      src/base/http/connection.cpp
  2. 8
      src/base/http/connection.h
  3. 50
      src/base/http/server.cpp
  4. 4
      src/base/http/server.h

32
src/base/http/connection.cpp

@ -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)

8
src/base/http/connection.h

@ -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;
}; };
} }

50
src/base/http/server.cpp

@ -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)) {
delete serverSocket;
return;
}
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
if (m_https) { if (m_https) {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols); static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key); static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates); static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone); static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption(); static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
#endif
new Connection(serverSocket, m_requestHandler, this);
} }
else { #endif
serverSocket->deleteLater();
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();
}
} }
} }

4
src/base/http/server.h

@ -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…
Cancel
Save