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 : std::as_const(list))
336 LOG(VB_HTTP, LOG_INFO, QString(
"HttpServer: Registering %1 extension path %2")
337 .arg(pExtension->
m_sName, base));
349 if (pExtension !=
nullptr )
355 for(
const QString& base : std::as_const(list))
372 bool bProcessed =
false;
374 LOG(VB_HTTP, LOG_DEBUG, QString(
"m_sBaseUrl: %1").arg( pRequest->
m_sBaseUrl ));
379 for (
int nIdx=0; nIdx < list.size() && !bProcessed; nIdx++ )
384 bProcessed = list[ nIdx ]->ProcessOptions(pRequest);
386 bProcessed = list[ nIdx ]->ProcessRequest(pRequest);
390 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer::DelegateRequest - "
391 "Unexpected Exception - "
392 "pExtension->ProcessRequest()."));
396 HttpServerExtensionList::iterator it =
m_extensions.begin();
403 bProcessed = (*it)->ProcessOptions(pRequest);
405 bProcessed = (*it)->ProcessRequest(pRequest);
409 LOG(VB_GENERAL, LOG_ERR, QString(
"HttpServer::DelegateRequest - "
410 "Unexpected Exception - "
411 "pExtension->ProcessRequest()."));
434 timeout = list.first()->GetSocketTimeout();
453 #ifndef QT_NO_OPENSSL
454 ,
const QSslConfiguration& sslConfig
457 : m_httpServer(httpServer), m_socket(sock),
458 m_socketTimeout(5s), m_connectionType(
type)
459 #ifndef QT_NO_OPENSSL
460 , m_sslConfig(sslConfig)
463 LOG(VB_HTTP, LOG_INFO, QString(
"HttpWorker(%1): New connection")
474 LOG(VB_HTTP, LOG_DEBUG,
475 QString(
"HttpWorker::run() socket=%1 -- begin").arg(
m_socket));
478 bool bTimeout =
false;
479 bool bKeepAlive =
true;
481 QTcpSocket *pSocket =
nullptr;
482 bool bEncrypted =
false;
487 #ifndef QT_NO_OPENSSL
488 auto *pSslSocket =
new QSslSocket();
489 if (pSslSocket->setSocketDescriptor(
m_socket)
493 pSslSocket->startServerEncryption();
494 if (pSslSocket->waitForEncrypted(5000))
496 LOG(VB_HTTP, LOG_INFO,
"SSL Handshake occurred, connection encrypted");
497 LOG(VB_HTTP, LOG_INFO, QString(
"Using %1 cipher").arg(pSslSocket->sessionCipher().name()));
502 LOG(VB_HTTP, LOG_WARNING,
"SSL Handshake FAILED, connection terminated");
504 pSslSocket =
nullptr;
510 pSslSocket =
nullptr;
514 pSocket = pSslSocket;
523 pSocket =
new QTcpSocket();
524 pSocket->setSocketDescriptor(
m_socket);
534 pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
535 int nRequestsHandled = 0;
540 pSocket->state() == QAbstractSocket::ConnectedState)
551 int64_t nBytes = pSocket->bytesAvailable();
562 if (pRequest !=
nullptr)
588 LOG(VB_HTTP, LOG_ERR,
"ParseRequest Failed.");
601 LOG(VB_HTTP, LOG_ERR,
602 QString(
"socket(%1) - Error returned from "
603 "SendResponse... Closing connection")
604 .arg(pSocket->socketDescriptor()));
618 LOG(VB_GENERAL, LOG_ERR,
619 "Error Creating BufferedSocketDeviceRequest");
631 LOG(VB_GENERAL, LOG_ERR,
632 "HttpWorkerThread::ProcessWork - Unexpected Exception.");
637 if ((pSocket->error() != QAbstractSocket::UnknownSocketError) &&
638 (!bKeepAlive || pSocket->error() != QAbstractSocket::SocketTimeoutError))
640 LOG(VB_HTTP, LOG_WARNING, QString(
"HttpWorker(%1): Error %2 (%3)")
642 .arg(pSocket->errorString())
643 .arg(pSocket->error()));
646 std::chrono::milliseconds writeTimeout = 5s;
649 pSocket->isValid() &&
650 pSocket->state() == QAbstractSocket::ConnectedState &&
651 pSocket->bytesToWrite() > 0)
653 LOG(VB_HTTP, LOG_DEBUG, QString(
"HttpWorker(%1): "
654 "Waiting for %2 bytes to be written "
655 "before closing the connection.")
657 .arg(pSocket->bytesToWrite()));
667 if (!pSocket->waitForBytesWritten(writeTimeout.count()))
669 LOG(VB_GENERAL, LOG_WARNING, QString(
"HttpWorker(%1): "
670 "Timed out waiting to write bytes to "
671 "the socket, waited %2 seconds")
673 .arg(writeTimeout.count() / 1000));
678 if (pSocket->bytesToWrite() > 0)
680 LOG(VB_HTTP, LOG_WARNING, QString(
"HttpWorker(%1): "
681 "Failed to write %2 bytes to "
684 .arg(pSocket->bytesToWrite())
685 .arg(pSocket->errorString()));
688 LOG(VB_HTTP, LOG_INFO, QString(
"HttpWorker(%1): Connection %2 closed. %3 requests were handled")
690 .arg(pSocket->socketDescriptor())
691 .arg(nRequestsHandled));
698 LOG(VB_HTTP, LOG_DEBUG,
"HttpWorkerThread::run() -- end");