23 #include <sys/utsname.h>
28 #include <QScriptEngine>
30 #include <QSslConfiguration>
33 #include <QSslCertificate>
41 #include "libmythbase/mythversion.h"
61 QStringList allowedMethods;
63 allowedMethods.append(
"GET");
65 allowedMethods.append(
"HEAD");
67 allowedMethods.append(
"POST");
75 allowedMethods.append(
"OPTIONS");
79 allowedMethods.append(
"M-SEARCH");
81 allowedMethods.append(
"SUBSCRIBE");
83 allowedMethods.append(
"UNSUBSCRIBE");
85 allowedMethods.append(
"NOTIFY");
87 if (!allowedMethods.isEmpty())
93 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServerExtension::ProcessOptions(): "
94 "Error: No methods supported for "
95 "extension - %1").arg(
m_sName));
118 m_threadPool(
"HttpServerPool"),
119 m_privateToken(QUuid::createUuid().
toString())
122 int maxHttpWorkers = std::max(QThread::idealThreadCount() * 2, 2);
128 LOG(VB_HTTP, LOG_NOTICE, QString(
"HttpServer(): Max Thread Count %1")
138 .arg(LOBYTE(LOWORD(GetVersion())))
139 .arg(HIBYTE(LOWORD(GetVersion())));
141 struct utsname uname_info {};
142 uname( &uname_info );
144 .arg(uname_info.sysname, uname_info.release);
148 LOG(VB_HTTP, LOG_INFO, QString(
"HttpServer() - SharePath = %1")
186 #ifndef QT_NO_OPENSSL
187 m_sslConfig = QSslConfiguration::defaultConfiguration();
190 m_sslConfig.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation,
true);
191 m_sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
true);
193 QList<QSslCipher> availableCiphers = QSslConfiguration::supportedCiphers();
194 QList<QSslCipher> secureCiphers;
195 QList<QSslCipher>::iterator it;
196 for (it = availableCiphers.begin(); it != availableCiphers.end(); ++it)
199 if ((*it).usedBits() < 128)
202 if ((*it).name().startsWith(
"RC4") ||
203 (*it).name().startsWith(
"EXP") ||
204 (*it).name().startsWith(
"ADH") ||
205 (*it).name().contains(
"NULL"))
208 secureCiphers.append(*it);
215 if (hostKeyPath.isEmpty())
218 QFile hostKeyFile(hostKeyPath);
219 if (!hostKeyFile.exists() || !hostKeyFile.open(QIODevice::ReadOnly))
221 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: SSL Host key file (%1) does not exist or is not readable").arg(hostKeyPath));
225 #ifndef QT_NO_OPENSSL
226 QByteArray rawHostKey = hostKeyFile.readAll();
227 QSslKey hostKey = QSslKey(rawHostKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
228 if (!hostKey.isNull())
232 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: Unable to load host key from file (%1)").arg(hostKeyPath));
237 QSslCertificate hostCert;
238 QList<QSslCertificate> certList = QSslCertificate::fromPath(hostCertPath);
239 if (!certList.isEmpty())
240 hostCert = certList.first();
242 if (!hostCert.isNull())
244 if (hostCert.effectiveDate() > QDateTime::currentDateTime())
246 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: Host certificate start date in future (%1)").arg(hostCertPath));
250 if (hostCert.expiryDate() < QDateTime::currentDateTime())
252 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: Host certificate has expired (%1)").arg(hostCertPath));
260 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: Unable to load host cert from file (%1)").arg(hostCertPath));
265 QList< QSslCertificate > CACertList = QSslCertificate::fromPath(caCertPath);
267 if (!CACertList.isEmpty())
269 else if (!caCertPath.isEmpty())
270 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer: Unable to load CA cert file (%1)").arg(caCertPath));
291 if (mythVersion.startsWith(
"v"))
292 mythVersion = mythVersion.right(mythVersion.length() - 1);
304 auto *server = qobject_cast<PrivTcpServer *>(QObject::sender());
306 type = server->GetServerType();
310 #ifndef QT_NO_OPENSSL
314 QString(
"HttpServer%1").arg(socket));
323 if (pExtension !=
nullptr )
325 LOG(VB_HTTP, LOG_INFO, QString(
"HttpServer: Registering %1 extension").arg(pExtension->
m_sName));
333 for(
const QString& base : qAsConst(list))
346 if (pExtension !=
nullptr )
352 for(
const QString& base : qAsConst(list))
369 bool bProcessed =
false;
371 LOG(VB_HTTP, LOG_DEBUG, QString(
"m_sBaseUrl: %1").arg( pRequest->
m_sBaseUrl ));
376 for (
int nIdx=0; nIdx < list.size() && !bProcessed; nIdx++ )
381 bProcessed = list[ nIdx ]->ProcessOptions(pRequest);
383 bProcessed = list[ nIdx ]->ProcessRequest(pRequest);
387 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer::DelegateRequest - "
388 "Unexpected Exception - "
389 "pExtension->ProcessRequest()."));
393 HttpServerExtensionList::iterator it =
m_extensions.begin();
400 bProcessed = (*it)->ProcessOptions(pRequest);
402 bProcessed = (*it)->ProcessRequest(pRequest);
406 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer::DelegateRequest - "
407 "Unexpected Exception - "
408 "pExtension->ProcessRequest()."));
431 timeout = list.first()->GetSocketTimeout();
450 #ifndef QT_NO_OPENSSL
451 ,
const QSslConfiguration& sslConfig
454 : m_httpServer(httpServer), m_socket(sock),
455 m_socketTimeout(5s), m_connectionType(
type)
456 #ifndef QT_NO_OPENSSL
457 , m_sslConfig(sslConfig)
460 LOG(VB_HTTP, LOG_INFO, QString(
"HttpWorker(%1): New connection")
471 LOG(VB_HTTP, LOG_DEBUG,
472 QString(
"HttpWorker::run() socket=%1 -- begin").arg(
m_socket));
475 bool bTimeout =
false;
476 bool bKeepAlive =
true;
478 QTcpSocket *pSocket =
nullptr;
479 bool bEncrypted =
false;
484 #ifndef QT_NO_OPENSSL
485 auto *pSslSocket =
new QSslSocket();
486 if (pSslSocket->setSocketDescriptor(
m_socket)
490 pSslSocket->startServerEncryption();
491 if (pSslSocket->waitForEncrypted(5000))
493 LOG(VB_HTTP, LOG_INFO,
"SSL Handshake occurred, connection encrypted");
494 LOG(VB_HTTP, LOG_INFO, QString(
"Using %1 cipher").arg(pSslSocket->sessionCipher().name()));
499 LOG(VB_HTTP, LOG_WARNING,
"SSL Handshake FAILED, connection terminated");
501 pSslSocket =
nullptr;
507 pSslSocket =
nullptr;
511 pSocket = pSslSocket;
520 pSocket =
new QTcpSocket();
521 pSocket->setSocketDescriptor(
m_socket);
531 pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
532 int nRequestsHandled = 0;
537 pSocket->state() == QAbstractSocket::ConnectedState)
548 int64_t nBytes = pSocket->bytesAvailable();
559 if (pRequest !=
nullptr)
585 LOG(VB_HTTP, LOG_ERR,
"ParseRequest Failed.");
598 LOG(VB_HTTP, LOG_ERR,
599 QString(
"socket(%1) - Error returned from "
600 "SendResponse... Closing connection")
601 .arg(pSocket->socketDescriptor()));
615 LOG(VB_GENERAL, LOG_ERR,
616 "Error Creating BufferedSocketDeviceRequest");
628 LOG(VB_GENERAL, LOG_ERR,
629 "HttpWorkerThread::ProcessWork - Unexpected Exception.");
634 if ((pSocket->error() != QAbstractSocket::UnknownSocketError) &&
635 (!bKeepAlive || pSocket->error() != QAbstractSocket::SocketTimeoutError))
637 LOG(VB_HTTP, LOG_WARNING, QString(
"HttpWorker(%1): Error %2 (%3)")
639 .arg(pSocket->errorString())
640 .arg(pSocket->error()));
643 std::chrono::milliseconds writeTimeout = 5s;
646 pSocket->isValid() &&
647 pSocket->state() == QAbstractSocket::ConnectedState &&
648 pSocket->bytesToWrite() > 0)
650 LOG(VB_HTTP, LOG_DEBUG, QString(
"HttpWorker(%1): "
651 "Waiting for %2 bytes to be written "
652 "before closing the connection.")
654 .arg(pSocket->bytesToWrite()));
664 if (!pSocket->waitForBytesWritten(writeTimeout.count()))
666 LOG(VB_GENERAL, LOG_WARNING, QString(
"HttpWorker(%1): "
667 "Timed out waiting to write bytes to "
668 "the socket, waited %2 seconds")
670 .arg(writeTimeout.count() / 1000));
675 if (pSocket->bytesToWrite() > 0)
677 LOG(VB_HTTP, LOG_WARNING, QString(
"HttpWorker(%1): "
678 "Failed to write %2 bytes to "
681 .arg(pSocket->bytesToWrite())
682 .arg(pSocket->errorString()));
685 LOG(VB_HTTP, LOG_INFO, QString(
"HttpWorker(%1): Connection %2 closed. %3 requests were handled")
687 .arg(pSocket->socketDescriptor())
688 .arg(nRequestsHandled));
695 LOG(VB_HTTP, LOG_DEBUG,
"HttpWorkerThread::run() -- end");