MythTV master
httpserver.cpp
Go to the documentation of this file.
1
2// Program Name: httpserver.cpp
3// Created : Oct. 1, 2005
4//
5// Purpose : HTTP 1.1 Mini Server Implmenetation
6// Used for UPnp/AV implementation & status information
7//
8// Copyright (c) 2005 David Blain <dblain@mythtv.org>
9// 2014 Stuart Morgan <smorgan@mythtv.org>
10//
11// Licensed under the GPL v2 or later, see LICENSE for details
12//
14
15// Own headers
16#include "httpserver.h"
17
18// ANSI C headers
19#include <cmath>
20#include <thread>
21
22#include <QtGlobal>
23#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
24#include <QtSystemDetection>
25#endif
26// POSIX headers
27#ifndef Q_OS_WINDOWS
28#include <sys/utsname.h>
29#endif
30
31// Qt headers
32#include <QSslConfiguration>
33#include <QSslSocket>
34#include <QSslCipher>
35#include <QSslCertificate>
36#include <QThread>
37#include <QUuid>
38
39// MythTV headers
40#include "libmythbase/compat.h"
45#include "libmythbase/mythversion.h"
46
47#include "upnputil.h"
48#include "htmlserver.h"
49
54{
56 pRequest->m_nResponseStatus = 200; // OK
57 // RFC 2616 Sect. 9.2 - If no response body is included, the response
58 // MUST include a Content-Length field with a
59 // field-value of "0".
60 pRequest->SetResponseHeader("Content-Length", "0");
61
62 QStringList allowedMethods;
64 allowedMethods.append("GET");
66 allowedMethods.append("HEAD");
68 allowedMethods.append("POST");
69// if (m_nSupportedMethods & RequestTypePut)
70// allowedMethods.append("PUT");
71// if (m_nSupportedMethods & RequestTypeDelete)
72// allowedMethods.append("DELETE");
73// if (m_nSupportedMethods & RequestTypeConnect)
74// allowedMethods.append("CONNECT");
76 allowedMethods.append("OPTIONS");
77// if (m_nSupportedMethods & RequestTypeTrace)
78// allowedMethods.append("TRACE");
80 allowedMethods.append("M-SEARCH");
82 allowedMethods.append("SUBSCRIBE");
84 allowedMethods.append("UNSUBSCRIBE");
86 allowedMethods.append("NOTIFY");
87
88 if (!allowedMethods.isEmpty())
89 {
90 pRequest->SetResponseHeader("Allow", allowedMethods.join(", "));
91 return true;
92 }
93
94 LOG(VB_GENERAL, LOG_ERR, QString("HttpServerExtension::ProcessOptions(): "
95 "Error: No methods supported for "
96 "extension - %1").arg(m_sName));
97
98 return false;
99}
100
101
104//
105// HttpServer Class Implementation
106//
109
112
114//
116
118 m_sSharePath(GetShareDir()),
119 m_threadPool("HttpServerPool"),
120 m_privateToken(QUuid::createUuid().toString()) // Cryptographically random and sufficiently long enough to act as a secure token
121{
122 // Number of connections processed concurrently
123 int maxHttpWorkers = std::max(QThread::idealThreadCount() * 2, 2); // idealThreadCount can return -1
124 // Don't allow more connections than we can process, it causes browsers
125 // to open lots of new connections instead of reusing existing ones
126 setMaxPendingConnections(maxHttpWorkers);
127 m_threadPool.setMaxThreadCount(maxHttpWorkers);
128
129 LOG(VB_HTTP, LOG_NOTICE, QString("HttpServer(): Max Thread Count %1")
131
132 // ----------------------------------------------------------------------
133 // Build Platform String
134 // ----------------------------------------------------------------------
135 {
136 QMutexLocker locker(&s_platformLock);
137#ifdef Q_OS_WINDOWS
138 s_platform = QString("Windows/%1.%2")
139 .arg(LOBYTE(LOWORD(GetVersion())))
140 .arg(HIBYTE(LOWORD(GetVersion())));
141#else
142 struct utsname uname_info {};
143 uname( &uname_info );
144 s_platform = QString("%1/%2")
145 .arg(uname_info.sysname, uname_info.release);
146#endif
147 }
148
149 LOG(VB_HTTP, LOG_INFO, QString("HttpServer() - SharePath = %1")
150 .arg(m_sSharePath));
151
152 // -=>TODO: Load Config XML
153 // -=>TODO: Load & initialize - HttpServerExtensions
154
156}
157
159//
161
163{
164 m_rwlock.lockForWrite();
165 m_running = false;
166 m_rwlock.unlock();
167
169
170 while (!m_extensions.empty())
171 {
172 delete m_extensions.takeFirst();
173 }
174}
175
177{
178#ifndef QT_NO_OPENSSL
179 m_sslConfig = QSslConfiguration::defaultConfiguration();
180
181 m_sslConfig.setProtocol(QSsl::SecureProtocols); // Includes SSLv3 which is insecure, but can't be helped
182 m_sslConfig.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation, true); // Potential DoS multiplier
183 m_sslConfig.setSslOption(QSsl::SslOptionDisableCompression, true); // CRIME attack
184
185 QList<QSslCipher> availableCiphers = QSslConfiguration::supportedCiphers();
186 QList<QSslCipher> secureCiphers;
187 QList<QSslCipher>::iterator it;
188 for (it = availableCiphers.begin(); it != availableCiphers.end(); ++it)
189 {
190 // Remove weak ciphers from the cipher list
191 if ((*it).usedBits() < 128)
192 continue;
193
194 if ((*it).name().startsWith("RC4") || // Weak cipher
195 (*it).name().startsWith("EXP") || // Weak authentication
196 (*it).name().startsWith("ADH") || // No authentication
197 (*it).name().contains("NULL")) // No encryption
198 continue;
199
200 secureCiphers.append(*it);
201 }
202 m_sslConfig.setCiphers(secureCiphers);
203#endif
204
205 QString hostKeyPath = gCoreContext->GetSetting("hostSSLKey", "");
206
207 if (hostKeyPath.isEmpty()) // No key, assume no SSL
208 return;
209
210 QFile hostKeyFile(hostKeyPath);
211 if (!hostKeyFile.exists() || !hostKeyFile.open(QIODevice::ReadOnly))
212 {
213 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: SSL Host key file (%1) does not exist or is not readable").arg(hostKeyPath));
214 return;
215 }
216
217#ifndef QT_NO_OPENSSL
218 QByteArray rawHostKey = hostKeyFile.readAll();
219 QSslKey hostKey = QSslKey(rawHostKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
220 if (!hostKey.isNull())
221 m_sslConfig.setPrivateKey(hostKey);
222 else
223 {
224 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Unable to load host key from file (%1)").arg(hostKeyPath));
225 return;
226 }
227
228 QString hostCertPath = gCoreContext->GetSetting("hostSSLCertificate", "");
229 QSslCertificate hostCert;
230 QList<QSslCertificate> certList = QSslCertificate::fromPath(hostCertPath);
231 if (!certList.isEmpty())
232 hostCert = certList.first();
233
234 if (!hostCert.isNull())
235 {
236 if (hostCert.effectiveDate() > QDateTime::currentDateTime())
237 {
238 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Host certificate start date in future (%1)").arg(hostCertPath));
239 return;
240 }
241
242 if (hostCert.expiryDate() < QDateTime::currentDateTime())
243 {
244 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Host certificate has expired (%1)").arg(hostCertPath));
245 return;
246 }
247
248 m_sslConfig.setLocalCertificate(hostCert);
249 }
250 else
251 {
252 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Unable to load host cert from file (%1)").arg(hostCertPath));
253 return;
254 }
255
256 QString caCertPath = gCoreContext->GetSetting("caSSLCertificate", "");
257 QList< QSslCertificate > CACertList = QSslCertificate::fromPath(caCertPath);
258
259 if (!CACertList.isEmpty())
260 m_sslConfig.setCaCertificates(CACertList);
261 else if (!caCertPath.isEmpty()) // Only warn if a path was actually configured, this isn't an error otherwise
262 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Unable to load CA cert file (%1)").arg(caCertPath));
263#endif
264}
265
267//
269
271{
272 QMutexLocker locker(&s_platformLock);
273 return s_platform;
274}
275
277//
279
281{
282 QString mythVersion = GetMythSourceVersion();
283 if (mythVersion.startsWith("v"))
284 mythVersion = mythVersion.right(mythVersion.length() - 1); // Trim off the leading 'v'
285 return QString("MythTV/%2 %1 UPnP/1.0").arg(HttpServer::GetPlatform(),
286 mythVersion);
287}
288
290//
292
294{
296 auto *server = qobject_cast<PrivTcpServer *>(QObject::sender());
297 if (server)
298 type = server->GetServerType();
299
301 new HttpWorker(*this, socket, type
302#ifndef QT_NO_OPENSSL
304#endif
305 ),
306 QString("HttpServer%1").arg(socket));
307}
308
310//
312
314{
315 if (pExtension != nullptr )
316 {
317 LOG(VB_HTTP, LOG_INFO, QString("HttpServer: Registering %1 extension").arg(pExtension->m_sName));
318 m_rwlock.lockForWrite();
319 m_extensions.append( pExtension );
320
321 // Add to multimap for quick lookup.
322
323 QStringList list = pExtension->GetBasePaths();
324
325 for( const QString& base : std::as_const(list))
326 {
327 m_basePaths.insert( base, pExtension );
328 LOG(VB_HTTP, LOG_INFO, QString("HttpServer: Registering %1 extension path %2")
329 .arg(pExtension->m_sName, base));
330 }
331 m_rwlock.unlock();
332 }
333}
334
336//
338
340{
341 if (pExtension != nullptr )
342 {
343 m_rwlock.lockForWrite();
344
345 QStringList list = pExtension->GetBasePaths();
346
347 for( const QString& base : std::as_const(list))
348 m_basePaths.remove( base, pExtension );
349
350 m_extensions.removeAll(pExtension);
351
352 delete pExtension;
353
354 m_rwlock.unlock();
355 }
356}
357
359//
361
363{
364 bool bProcessed = false;
365
366 LOG(VB_HTTP, LOG_DEBUG, QString("m_sBaseUrl: %1").arg( pRequest->m_sBaseUrl ));
367 m_rwlock.lockForRead();
368
369 QList< HttpServerExtension* > list = m_basePaths.values( pRequest->m_sBaseUrl );
370
371 for (int nIdx=0; nIdx < list.size() && !bProcessed; nIdx++ )
372 {
373 try
374 {
375 if (pRequest->m_eType == RequestTypeOptions)
376 bProcessed = list[ nIdx ]->ProcessOptions(pRequest);
377 else
378 bProcessed = list[ nIdx ]->ProcessRequest(pRequest);
379 }
380 catch(...)
381 {
382 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer::DelegateRequest - "
383 "Unexpected Exception - "
384 "pExtension->ProcessRequest()."));
385 }
386 }
387
388 for (const auto& ext : std::as_const(m_extensions))
389 {
390 if (bProcessed)
391 break;
392 try
393 {
394 if (pRequest->m_eType == RequestTypeOptions)
395 bProcessed = ext->ProcessOptions(pRequest);
396 else
397 bProcessed = ext->ProcessRequest(pRequest);
398 }
399 catch(...)
400 {
401 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer::DelegateRequest - "
402 "Unexpected Exception - "
403 "pExtension->ProcessRequest()."));
404 }
405 }
406 m_rwlock.unlock();
407
408// if (!bProcessed)
409// bProcessed = m_pHtmlServer->ProcessRequest(pRequest);
410
411 if (!bProcessed)
412 {
414 pRequest->m_nResponseStatus = 404;
415 pRequest->m_response.write( pRequest->GetResponsePage() );
416 }
417}
418
420{
421 int timeout = -1;
422
423 m_rwlock.lockForRead();
424 QList< HttpServerExtension* > list = m_basePaths.values( pRequest->m_sBaseUrl );
425 if (!list.isEmpty())
426 timeout = list.first()->GetSocketTimeout();
427 m_rwlock.unlock();
428
429 if (timeout < 0)
430 timeout = gCoreContext->GetNumSetting("HTTP/KeepAliveTimeoutSecs", 10);
431
432 return timeout;
433}
434
437//
438// HttpWorkerThread Class Implementation
439//
442
443HttpWorker::HttpWorker(HttpServer &httpServer, qintptr sock,
445#ifndef QT_NO_OPENSSL
446 , const QSslConfiguration& sslConfig
447#endif
448)
449 : m_httpServer(httpServer), m_socket(sock),
450 m_socketTimeout(5s), m_connectionType(type)
451#ifndef QT_NO_OPENSSL
452 , m_sslConfig(sslConfig)
453#endif
454{
455 LOG(VB_HTTP, LOG_INFO, QString("HttpWorker(%1): New connection")
456 .arg(m_socket));
457}
458
460//
462
464{
465 LOG(VB_HTTP, LOG_DEBUG,
466 QString("HttpWorker::run() socket=%1 -- begin").arg(m_socket));
467
468 bool bKeepAlive = true;
469 HTTPRequest *pRequest = nullptr;
470 QTcpSocket *pSocket = nullptr;
471 bool bEncrypted = false;
472 MythTimer attempt_time;
473 static constexpr std::chrono::milliseconds k_poll_interval {1ms};
474
476 {
477
478#ifndef QT_NO_OPENSSL
479 auto *pSslSocket = new QSslSocket();
480 if (pSslSocket->setSocketDescriptor(m_socket)
481 && gCoreContext->CheckSubnet(pSslSocket))
482 {
483 pSslSocket->setSslConfiguration(m_sslConfig);
484 pSslSocket->startServerEncryption();
485 attempt_time.start();
486 while (m_httpServer.IsRunning() && attempt_time.elapsed() < 5s && !pSslSocket->isEncrypted())
487 {
488 pSslSocket->waitForEncrypted(k_poll_interval.count());
489 }
490 if (pSslSocket->isEncrypted())
491 {
492 LOG(VB_HTTP, LOG_INFO, "SSL Handshake occurred, connection encrypted");
493 LOG(VB_HTTP, LOG_INFO, QString("Using %1 cipher").arg(pSslSocket->sessionCipher().name()));
494 bEncrypted = true;
495 }
496 else
497 {
498 LOG(VB_HTTP, LOG_WARNING, "SSL Handshake FAILED, connection terminated");
499 delete pSslSocket;
500 pSslSocket = nullptr;
501 }
502 }
503 else
504 {
505 delete pSslSocket;
506 pSslSocket = nullptr;
507 }
508
509 if (pSslSocket)
510 pSocket = pSslSocket;
511 else
512 return;
513#else
514 return;
515#endif
516 }
517 else // Plain old unencrypted socket
518 {
519 pSocket = new QTcpSocket();
520 pSocket->setSocketDescriptor(m_socket);
521 if (!gCoreContext->CheckSubnet(pSocket))
522 {
523 delete pSocket;
524 pSocket = nullptr;
525 return;
526 }
527
528 }
529
530 pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
531 int nRequestsHandled = 0; // Allow debugging of keep-alive and connection re-use
532
533 try
534 {
535 while (m_httpServer.IsRunning() && bKeepAlive && pSocket->isValid() &&
536 pSocket->state() == QAbstractSocket::ConnectedState)
537 {
538 // We set a timeout on keep-alive connections to avoid blocking
539 // new clients from connecting - Default at time of writing was
540 // 5 seconds for initial connection, then up to 10 seconds of idle
541 // time between each subsequent request on the same connection
542 attempt_time.start();
543 while (m_httpServer.IsRunning() && pSocket->isValid()
544 && pSocket->state() == QAbstractSocket::ConnectedState
545 && pSocket->bytesAvailable() <= 0
546 && attempt_time.elapsed() < m_socketTimeout
547 )
548 {
549 pSocket->waitForReadyRead(k_poll_interval.count());
550 }
551
552 if (!m_httpServer.IsRunning() || pSocket->bytesAvailable() <= 0)
553 break;
554
555 {
556 // ----------------------------------------------------------
557 // See if this is a valid request
558 // ----------------------------------------------------------
559
560 pRequest = new BufferedSocketDeviceRequest( pSocket );
561 if (pRequest != nullptr)
562 {
563 pRequest->m_bEncrypted = bEncrypted;
564 if ( pRequest->ParseRequest() )
565 {
566 bKeepAlive = pRequest->GetKeepAlive();
567 // The timeout is defined by the Server/Server Extension
568 // but must appear in the response headers
569 auto nTimeout = std::chrono::seconds(m_httpServer.GetSocketTimeout(pRequest));
570 pRequest->SetKeepAliveTimeout(nTimeout);
571 m_socketTimeout = nTimeout; // Converts to milliseconds
572
573 // ------------------------------------------------------
574 // Request Parsed... Pass on to Main HttpServer class to
575 // delegate processing to HttpServerExtensions.
576 // ------------------------------------------------------
577 if ((pRequest->m_nResponseStatus != 400) &&
578 (pRequest->m_nResponseStatus != 401) &&
579 (pRequest->m_nResponseStatus != 403) &&
580 pRequest->m_eType != RequestTypeUnknown)
582
583 nRequestsHandled++;
584 }
585 else
586 {
587 LOG(VB_HTTP, LOG_ERR, "ParseRequest Failed.");
588
589 pRequest->m_nResponseStatus = 501;
590 pRequest->m_response.write( pRequest->GetResponsePage() );
591 bKeepAlive = false;
592 }
593
594 // -------------------------------------------------------
595 // Always MUST send a response.
596 // -------------------------------------------------------
597 if (pRequest->SendResponse() < 0)
598 {
599 bKeepAlive = false;
600 LOG(VB_HTTP, LOG_ERR,
601 QString("socket(%1) - Error returned from "
602 "SendResponse... Closing connection")
603 .arg(pSocket->socketDescriptor()));
604 }
605
606 // -------------------------------------------------------
607 // Check to see if a PostProcess was registered
608 // -------------------------------------------------------
609 if ( pRequest->m_pPostProcess != nullptr )
611
612 delete pRequest;
613 pRequest = nullptr;
614 }
615 else
616 {
617 LOG(VB_GENERAL, LOG_ERR,
618 "Error Creating BufferedSocketDeviceRequest");
619 bKeepAlive = false;
620 }
621 }
622 }
623 }
624 catch(...)
625 {
626 LOG(VB_GENERAL, LOG_ERR,
627 "HttpWorkerThread::ProcessWork - Unexpected Exception.");
628 }
629
630 delete pRequest;
631
632 if ((pSocket->error() != QAbstractSocket::UnknownSocketError) &&
633 (!bKeepAlive || pSocket->error() != QAbstractSocket::SocketTimeoutError)) // This 'error' isn't an error when keep-alive is active
634 {
635 LOG(VB_HTTP, LOG_WARNING, QString("HttpWorker(%1): Error %2 (%3)")
636 .arg(m_socket)
637 .arg(pSocket->errorString())
638 .arg(pSocket->error()));
639 }
640
641 std::chrono::milliseconds writeTimeout = 5s;
642 // Make sure any data in the buffer is flushed before the socket is closed
643 if (pSocket->bytesToWrite() > 0)
644 {
645 LOG(VB_HTTP, LOG_DEBUG,
646 QString("HttpWorker(%1): Waiting for %2 bytes to be written before closing the connection.")
647 .arg(m_socket).arg(pSocket->bytesToWrite())
648 );
649 }
650 attempt_time.start();
651 while (m_httpServer.IsRunning() &&
652 pSocket->isValid() &&
653 pSocket->state() == QAbstractSocket::ConnectedState &&
654 pSocket->bytesToWrite() > 0 &&
655 attempt_time.elapsed() < writeTimeout
656 )
657 {
658 // If the client stops reading for longer than 'writeTimeout' then
659 // stop waiting for them. We can't afford to leave the socket
660 // connected indefinately, it could be used by another client.
661 //
662 // NOTE: Some clients deliberately stall as a way of 'pausing' A/V
663 // streaming. We should create a new server extension or adjust the
664 // timeout according to the User-Agent, instead of increasing the
665 // standard timeout. However we should ALWAYS have a timeout.
666 pSocket->waitForBytesWritten(k_poll_interval.count());
667 }
668
669 if (pSocket->bytesToWrite() > 0)
670 {
671 LOG(VB_HTTP, LOG_WARNING, QString("HttpWorker(%1): "
672 "Failed to write %2 bytes to "
673 "socket, (%3)")
674 .arg(m_socket)
675 .arg(pSocket->bytesToWrite())
676 .arg(pSocket->errorString()));
677 }
678
679 LOG(VB_HTTP, LOG_INFO, QString("HttpWorker(%1): Connection %2 closed. %3 requests were handled")
680 .arg(m_socket)
681 .arg(pSocket->socketDescriptor())
682 .arg(nRequestsHandled));
683
684 pSocket->close();
685 delete pSocket;
686 pSocket = nullptr;
687
688 LOG(VB_HTTP, LOG_DEBUG, QString("HttpWorker::run() socket=%1 -- end").arg(m_socket));
689}
690
691
QByteArray GetResponsePage(void)
HttpResponseType m_eResponseType
Definition: httprequest.h:150
long m_nResponseStatus
Definition: httprequest.h:153
qint64 SendResponse(void)
bool ParseRequest()
void SetKeepAliveTimeout(std::chrono::seconds nTimeout)
Definition: httprequest.h:258
IPostProcess * m_pPostProcess
Definition: httprequest.h:160
bool m_bEncrypted
Definition: httprequest.h:143
HttpRequestType m_eType
Definition: httprequest.h:121
bool GetKeepAlive() const
Definition: httprequest.h:238
QString m_sBaseUrl
Definition: httprequest.h:128
void SetResponseHeader(const QString &sKey, const QString &sValue, bool replace=false)
QBuffer m_response
Definition: httprequest.h:158
virtual QStringList GetBasePaths()=0
virtual bool ProcessOptions(HTTPRequest *pRequest)
Handle an OPTIONS request.
Definition: httpserver.cpp:53
QMultiMap< QString, HttpServerExtension * > m_basePaths
Definition: httpserver.h:148
QString m_sSharePath
Definition: httpserver.h:149
uint GetSocketTimeout(HTTPRequest *pRequest) const
Get the idle socket timeout value for the relevant extension.
Definition: httpserver.cpp:419
void DelegateRequest(HTTPRequest *pRequest)
Definition: httpserver.cpp:362
static QMutex s_platformLock
Definition: httpserver.h:153
MThreadPool m_threadPool
Definition: httpserver.h:150
static QString GetServerVersion(void)
Definition: httpserver.cpp:280
void UnregisterExtension(HttpServerExtension *pExtension)
Definition: httpserver.cpp:339
void newTcpConnection(qintptr socket) override
Definition: httpserver.cpp:293
QReadWriteLock m_rwlock
Definition: httpserver.h:145
bool IsRunning(void) const
Definition: httpserver.h:133
HttpServerExtensionList m_extensions
Definition: httpserver.h:146
static QString s_platform
Definition: httpserver.h:154
static QString GetPlatform(void)
Definition: httpserver.cpp:270
bool m_running
Definition: httpserver.h:151
QSslConfiguration m_sslConfig
Definition: httpserver.h:157
void LoadSSLConfig()
Definition: httpserver.cpp:176
~HttpServer() override
Definition: httpserver.cpp:162
void RegisterExtension(HttpServerExtension *pExtension)
Definition: httpserver.cpp:313
HttpWorker(HttpServer &httpServer, qintptr sock, PoolServerType type, const QSslConfiguration &sslConfig)
Definition: httpserver.cpp:443
HttpServer & m_httpServer
Definition: httpserver.h:196
PoolServerType m_connectionType
Definition: httpserver.h:199
qintptr m_socket
Definition: httpserver.h:197
std::chrono::milliseconds m_socketTimeout
Definition: httpserver.h:198
void run(void) override
Definition: httpserver.cpp:463
QSslConfiguration m_sslConfig
Definition: httpserver.h:202
virtual void ExecutePostProcess()=0
int maxThreadCount(void) const
void setMaxThreadCount(int maxThreadCount)
void startReserved(QRunnable *runnable, const QString &debugName, std::chrono::milliseconds waitForAvailMS=0ms)
void Stop(void)
QString GetSetting(const QString &key, const QString &defaultval="")
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.
int GetNumSetting(const QString &key, int defaultval=0)
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
void setMaxPendingConnections(int n)
Definition: serverpool.h:94
unsigned int uint
Definition: compat.h:68
@ ResponseTypeHTML
Definition: httprequest.h:80
@ ResponseTypeHeader
Definition: httprequest.h:87
@ RequestTypeMSearch
Definition: httprequest.h:58
@ RequestTypeSubscribe
Definition: httprequest.h:59
@ RequestTypeNotify
Definition: httprequest.h:61
@ RequestTypePost
Definition: httprequest.h:51
@ RequestTypeOptions
Definition: httprequest.h:55
@ RequestTypeUnsubscribe
Definition: httprequest.h:60
@ RequestTypeGet
Definition: httprequest.h:49
@ RequestTypeHead
Definition: httprequest.h:50
@ RequestTypeUnknown
Definition: httprequest.h:47
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetShareDir(void)
Definition: mythdirs.cpp:283
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
const char * GetMythSourceVersion()
Definition: mythversion.cpp:7
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
PoolServerType
Definition: serverpool.h:30
@ kSSLServer
Definition: serverpool.h:33
@ kTCPServer
Definition: serverpool.h:31