QtWebApp
httpconnectionhandlerpool.cpp
1 #ifndef QT_NO_SSL
2  #include <QSslSocket>
3  #include <QSslKey>
4  #include <QSslCertificate>
5  #include <QSslConfiguration>
6 #endif
7 #include <QDir>
8 #include "httpconnectionhandlerpool.h"
9 
10 using namespace stefanfrings;
11 
13  : QObject()
14 {
15  Q_ASSERT(settings!=0);
16  this->settings=settings;
17  this->requestHandler=requestHandler;
18  this->sslConfiguration=NULL;
19  loadSslConfig();
20  cleanupTimer.start(settings->value("cleanupInterval",1000).toInt());
21  connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup()));
22 }
23 
24 
26 {
27  // delete all connection handlers and wait until their threads are closed
28  foreach(HttpConnectionHandler* handler, pool)
29  {
30  delete handler;
31  }
32  delete sslConfiguration;
33  qDebug("HttpConnectionHandlerPool (%p): destroyed", this);
34 }
35 
36 
38 {
39  HttpConnectionHandler* freeHandler=0;
40  mutex.lock();
41  // find a free handler in pool
42  foreach(HttpConnectionHandler* handler, pool)
43  {
44  if (!handler->isBusy())
45  {
46  freeHandler=handler;
47  freeHandler->setBusy();
48  break;
49  }
50  }
51  // create a new handler, if necessary
52  if (!freeHandler)
53  {
54  int maxConnectionHandlers=settings->value("maxThreads",100).toInt();
55  if (pool.count()<maxConnectionHandlers)
56  {
57  freeHandler=new HttpConnectionHandler(settings,requestHandler,sslConfiguration);
58  freeHandler->setBusy();
59  pool.append(freeHandler);
60  }
61  }
62  mutex.unlock();
63  return freeHandler;
64 }
65 
66 
67 void HttpConnectionHandlerPool::cleanup()
68 {
69  int maxIdleHandlers=settings->value("minThreads",1).toInt();
70  int idleCounter=0;
71  mutex.lock();
72  foreach(HttpConnectionHandler* handler, pool)
73  {
74  if (!handler->isBusy())
75  {
76  if (++idleCounter > maxIdleHandlers)
77  {
78  delete handler;
79  pool.removeOne(handler);
80  long int poolSize=(long int)pool.size();
81  qDebug("HttpConnectionHandlerPool: Removed connection handler (%p), pool size is now %li",handler,poolSize);
82  break; // remove only one handler in each interval
83  }
84  }
85  }
86  mutex.unlock();
87 }
88 
89 
90 void HttpConnectionHandlerPool::loadSslConfig()
91 {
92  // If certificate and key files are configured, then load them
93  QString sslKeyFileName=settings->value("sslKeyFile","").toString();
94  QString sslCertFileName=settings->value("sslCertFile","").toString();
95  QString caCertFileName=settings->value("caCertFile","").toString();
96  bool verifyPeer=settings->value("verifyPeer","false").toBool();
97 
98  if (!sslKeyFileName.isEmpty() && !sslCertFileName.isEmpty())
99  {
100  #ifdef QT_NO_SSL
101  qWarning("HttpConnectionHandlerPool: SSL is not supported");
102  #else
103  // Convert relative fileNames to absolute, based on the directory of the config file.
104  QFileInfo configFile(settings->fileName());
105  #ifdef Q_OS_WIN32
106  if (QDir::isRelativePath(sslKeyFileName) && settings->format()!=QSettings::NativeFormat)
107  #else
108  if (QDir::isRelativePath(sslKeyFileName))
109  #endif
110  {
111  sslKeyFileName=QFileInfo(configFile.absolutePath(),sslKeyFileName).absoluteFilePath();
112  }
113 
114  #ifdef Q_OS_WIN32
115  if (QDir::isRelativePath(sslCertFileName) && settings->format()!=QSettings::NativeFormat)
116  #else
117  if (QDir::isRelativePath(sslCertFileName))
118  #endif
119  {
120  sslCertFileName=QFileInfo(configFile.absolutePath(),sslCertFileName).absoluteFilePath();
121  }
122 
123  // Load the SSL certificate
124  QFile certFile(sslCertFileName);
125  if (!certFile.open(QIODevice::ReadOnly))
126  {
127  qCritical("HttpConnectionHandlerPool: cannot open sslCertFile %s", qPrintable(sslCertFileName));
128  return;
129  }
130  QSslCertificate certificate(&certFile, QSsl::Pem);
131  certFile.close();
132 
133  // Load the key file
134  QFile keyFile(sslKeyFileName);
135  if (!keyFile.open(QIODevice::ReadOnly))
136  {
137  qCritical("HttpConnectionHandlerPool: cannot open sslKeyFile %s", qPrintable(sslKeyFileName));
138  return;
139  }
140  QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
141  keyFile.close();
142 
143  // Create the SSL configuration
144  sslConfiguration=new QSslConfiguration();
145  sslConfiguration->setProtocol(QSsl::AnyProtocol);
146  sslConfiguration->setLocalCertificate(certificate);
147  sslConfiguration->setPrivateKey(sslKey);
148 
149  // We can optionally use a CA certificate to validate the HTTP clients
150  if (!caCertFileName.isEmpty())
151  {
152  #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
153  qCritical("HttpConnectionHandlerPool: Using a caCertFile requires Qt 5.15 or newer");
154  #else
155 
156  // Convert relative fileName to absolute, based on the directory of the config file.
157  #ifdef Q_OS_WIN32
158  if (QDir::isRelativePath(sslCaCertFileName) && settings->format()!=QSettings::NativeFormat)
159  #else
160  if (QDir::isRelativePath(caCertFileName))
161  #endif
162  {
163  caCertFileName=QFileInfo(configFile.absolutePath(),caCertFileName).absoluteFilePath();
164  }
165 
166  // Load the CA cert file
167  QFile caCertFile(caCertFileName);
168  if (!caCertFile.open(QIODevice::ReadOnly))
169  {
170  qCritical("HttpConnectionHandlerPool: cannot open caCertFile %s", qPrintable(caCertFileName));
171  return;
172  }
173  QSslCertificate caCertificate(&caCertFile, QSsl::Pem);
174  caCertFile.close();
175 
176  // Configure SSL
177  sslConfiguration->addCaCertificate(caCertificate);
178  #endif
179  }
180 
181  // Enable or disable verification of the HTTP client
182  if (verifyPeer)
183  {
184  sslConfiguration->setPeerVerifyMode(QSslSocket::VerifyPeer);
185  }
186  else
187  {
188  sslConfiguration->setPeerVerifyMode(QSslSocket::VerifyNone);
189  }
190 
191  qDebug("HttpConnectionHandlerPool: SSL settings loaded");
192  #endif
193  }
194 }
HttpConnectionHandlerPool(const QSettings *settings, HttpRequestHandler *requestHandler)
Constructor.
HttpConnectionHandler * getConnectionHandler()
Get a free connection handler, or 0 if not available.
Alias for QSslConfiguration if OpenSSL is not supported.
bool isBusy()
Returns true, if this handler is in use.
void setBusy()
Mark this handler as busy.
The request handler generates a response for each HTTP request.