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,5,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
53{
55 pRequest->m_nResponseStatus = 200; // OK
56 // RFC 2616 Sect. 9.2 - If no response body is included, the response
57 // MUST include a Content-Length field with a
58 // field-value of "0".
59 pRequest->SetResponseHeader("Content-Length", "0");
60
61 QStringList allowedMethods;
63 allowedMethods.append("GET");
65 allowedMethods.append("HEAD");
67 allowedMethods.append("POST");
68// if (m_nSupportedMethods & RequestTypePut)
69// allowedMethods.append("PUT");
70// if (m_nSupportedMethods & RequestTypeDelete)
71// allowedMethods.append("DELETE");
72// if (m_nSupportedMethods & RequestTypeConnect)
73// allowedMethods.append("CONNECT");
75 allowedMethods.append("OPTIONS");
76// if (m_nSupportedMethods & RequestTypeTrace)
77// allowedMethods.append("TRACE");
79 allowedMethods.append("M-SEARCH");
81 allowedMethods.append("SUBSCRIBE");
83 allowedMethods.append("UNSUBSCRIBE");
85 allowedMethods.append("NOTIFY");
86
87 if (!allowedMethods.isEmpty())
88 {
89 pRequest->SetResponseHeader("Allow", allowedMethods.join(", "));
90 return true;
91 }
92
93 LOG(VB_GENERAL, LOG_ERR, QString("HttpServerExtension::ProcessOptions(): "
94 "Error: No methods supported for "
95 "extension - %1").arg(m_sName));
96
97 return false;
98}
99
100
103//
104// HttpServer Class Implementation
105//
108
111
113//
115
117 m_sSharePath(GetShareDir()),
118 m_threadPool("HttpServerPool"),
119 m_privateToken(QUuid::createUuid().toString()) // Cryptographically random and sufficiently long enough to act as a secure token
120{
121 // Number of connections processed concurrently
122 int maxHttpWorkers = std::max(QThread::idealThreadCount() * 2, 2); // idealThreadCount can return -1
123 // Don't allow more connections than we can process, it causes browsers
124 // to open lots of new connections instead of reusing existing ones
125 setMaxPendingConnections(maxHttpWorkers);
126 m_threadPool.setMaxThreadCount(maxHttpWorkers);
127
128 LOG(VB_HTTP, LOG_NOTICE, QString("HttpServer(): Max Thread Count %1")
130
131 // ----------------------------------------------------------------------
132 // Build Platform String
133 // ----------------------------------------------------------------------
134 {
135 QMutexLocker locker(&s_platformLock);
136#ifdef Q_OS_WINDOWS
137 s_platform = QString("Windows/%1.%2")
138 .arg(LOBYTE(LOWORD(GetVersion())))
139 .arg(HIBYTE(LOWORD(GetVersion())));
140#else
141 struct utsname uname_info {};
142 uname( &uname_info );
143 s_platform = QString("%1/%2")
144 .arg(uname_info.sysname, uname_info.release);
145#endif
146 }
147
148 LOG(VB_HTTP, LOG_INFO, QString("HttpServer() - SharePath = %1")
149 .arg(m_sSharePath));
150
151 // -=>TODO: Load Config XML
152 // -=>TODO: Load & initialize - HttpServerExtensions
153
155}
156
158//
160
162{
163 m_rwlock.lockForWrite();
164 m_running = false;
165 m_rwlock.unlock();
166
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
204 QString hostKeyPath = gCoreContext->GetSetting("hostSSLKey", "");
205
206 if (hostKeyPath.isEmpty()) // No key, assume no SSL
207 return;
208
209 QString hostCertPath = gCoreContext->GetSetting("hostSSLCertificate", "");
210 QSslCertificate hostCert;
211 QList<QSslCertificate> certList = QSslCertificate::fromPath(hostCertPath);
212 if (!certList.isEmpty())
213 hostCert = certList.first();
214
215 if (!hostCert.isNull())
216 {
217 if (hostCert.effectiveDate() > QDateTime::currentDateTime())
218 {
219 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Host certificate start date in future (%1)").arg(hostCertPath));
220 return;
221 }
222
223 if (hostCert.expiryDate() < QDateTime::currentDateTime())
224 {
225 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Host certificate has expired (%1)").arg(hostCertPath));
226 return;
227 }
228
229 m_sslConfig.setLocalCertificate(hostCert);
230 }
231 else
232 {
233 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Unable to load host cert from file (%1)").arg(hostCertPath));
234 return;
235 }
236
237 QFile hostKeyFile(hostKeyPath);
238 if (!hostKeyFile.exists() || !hostKeyFile.open(QIODevice::ReadOnly))
239 {
240 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: SSL Host key file (%1) does not exist or is not readable").arg(hostKeyPath));
241 return;
242 }
243
244 QByteArray rawHostKey = hostKeyFile.readAll();
245 QSslKey hostKey = QSslKey(rawHostKey, hostCert.publicKey().algorithm(), QSsl::Pem, QSsl::PrivateKey);
246 if (!hostKey.isNull())
247 {
248 m_sslConfig.setPrivateKey(hostKey);
249 }
250 else
251 {
252 LOG(VB_GENERAL, LOG_ERR, QString("HttpServer: Unable to load host key from file (%1)").arg(hostKeyPath));
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 {
411 pRequest->m_nResponseStatus = 404;
412 pRequest->m_response.write( pRequest->GetResponsePage() );
413 }
414}
415
417{
418 int timeout = -1;
419
420 m_rwlock.lockForRead();
421 QList< HttpServerExtension* > list = m_basePaths.values( pRequest->m_sBaseUrl );
422 if (!list.isEmpty())
423 timeout = list.first()->GetSocketTimeout();
424 m_rwlock.unlock();
425
426 if (timeout < 0)
427 timeout = gCoreContext->GetNumSetting("HTTP/KeepAliveTimeoutSecs", 10);
428
429 return timeout;
430}
431
434//
435// HttpWorkerThread Class Implementation
436//
439
440HttpWorker::HttpWorker(HttpServer &httpServer, qintptr sock,
442#ifndef QT_NO_OPENSSL
443 , const QSslConfiguration& sslConfig
444#endif
445)
446 : m_httpServer(httpServer), m_socket(sock),
447 m_socketTimeout(5s), m_connectionType(type)
448#ifndef QT_NO_OPENSSL
449 , m_sslConfig(sslConfig)
450#endif
451{
452 LOG(VB_HTTP, LOG_INFO, QString("HttpWorker(%1): New connection")
453 .arg(m_socket));
454}
455
457//
459
461{
462 LOG(VB_HTTP, LOG_DEBUG,
463 QString("HttpWorker::run() socket=%1 -- begin").arg(m_socket));
464
465 bool bKeepAlive = true;
466 HTTPRequest *pRequest = nullptr;
467 QTcpSocket *pSocket = nullptr;
468 bool bEncrypted = false;
469 MythTimer attempt_time;
470 static constexpr std::chrono::milliseconds k_poll_interval {1ms};
471
473 {
474
475#ifndef QT_NO_OPENSSL
476 auto *pSslSocket = new QSslSocket();
477 if (pSslSocket->setSocketDescriptor(m_socket)
478 && gCoreContext->CheckSubnet(pSslSocket))
479 {
480 pSslSocket->setSslConfiguration(m_sslConfig);
481 pSslSocket->startServerEncryption();
482 attempt_time.start();
483 while (m_httpServer.IsRunning() && attempt_time.elapsed() < 5s && !pSslSocket->isEncrypted())
484 {
485 pSslSocket->waitForEncrypted(k_poll_interval.count());
486 }
487 if (pSslSocket->isEncrypted())
488 {
489 LOG(VB_HTTP, LOG_INFO, "SSL Handshake occurred, connection encrypted");
490 LOG(VB_HTTP, LOG_INFO, QString("Using %1 cipher").arg(pSslSocket->sessionCipher().name()));
491 bEncrypted = true;
492 }
493 else
494 {
495 LOG(VB_HTTP, LOG_WARNING, "SSL Handshake FAILED, connection terminated");
496 delete pSslSocket;
497 pSslSocket = nullptr;
498 }
499 }
500 else
501 {
502 delete pSslSocket;
503 pSslSocket = nullptr;
504 }
505
506 if (pSslSocket)
507 pSocket = pSslSocket;
508 else
509 return;
510#else
511 return;
512#endif
513 }
514 else // Plain old unencrypted socket
515 {
516 pSocket = new QTcpSocket();
517 pSocket->setSocketDescriptor(m_socket);
518 if (!gCoreContext->CheckSubnet(pSocket))
519 {
520 delete pSocket;
521 pSocket = nullptr;
522 return;
523 }
524
525 }
526
527 pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
528 int nRequestsHandled = 0; // Allow debugging of keep-alive and connection re-use
529
530 try
531 {
532 while (m_httpServer.IsRunning() && bKeepAlive && pSocket->isValid() &&
533 pSocket->state() == QAbstractSocket::ConnectedState)
534 {
535 // We set a timeout on keep-alive connections to avoid blocking
536 // new clients from connecting - Default at time of writing was
537 // 5 seconds for initial connection, then up to 10 seconds of idle
538 // time between each subsequent request on the same connection
539 attempt_time.start();
540 while (m_httpServer.IsRunning() && pSocket->isValid()
541 && pSocket->state() == QAbstractSocket::ConnectedState
542 && pSocket->bytesAvailable() <= 0
543 && attempt_time.elapsed() < m_socketTimeout
544 )
545 {
546 pSocket->waitForReadyRead(k_poll_interval.count());
547 }
548
549 if (!m_httpServer.IsRunning() || pSocket->bytesAvailable() <= 0)
550 break;
551
552 {
553 // ----------------------------------------------------------
554 // See if this is a valid request
555 // ----------------------------------------------------------
556
557 pRequest = new BufferedSocketDeviceRequest( pSocket );
558 if (pRequest != nullptr)
559 {
560 pRequest->m_bEncrypted = bEncrypted;
561 if ( pRequest->ParseRequest() )
562 {
563 bKeepAlive = pRequest->GetKeepAlive();
564 // The timeout is defined by the Server/Server Extension
565 // but must appear in the response headers
566 auto nTimeout = std::chrono::seconds(m_httpServer.GetSocketTimeout(pRequest));
567 pRequest->SetKeepAliveTimeout(nTimeout);
568 m_socketTimeout = nTimeout; // Converts to milliseconds
569
570 // ------------------------------------------------------
571 // Request Parsed... Pass on to Main HttpServer class to
572 // delegate processing to HttpServerExtensions.
573 // ------------------------------------------------------
574 if ((pRequest->m_nResponseStatus != 400) &&
575 (pRequest->m_nResponseStatus != 401) &&
576 (pRequest->m_nResponseStatus != 403) &&
577 pRequest->m_eType != RequestTypeUnknown)
579
580 nRequestsHandled++;
581 }
582 else
583 {
584 LOG(VB_HTTP, LOG_ERR, "ParseRequest Failed.");
585
586 pRequest->m_nResponseStatus = 501;
587 pRequest->m_response.write( pRequest->GetResponsePage() );
588 bKeepAlive = false;
589 }
590
591 // -------------------------------------------------------
592 // Always MUST send a response.
593 // -------------------------------------------------------
594 if (pRequest->SendResponse() < 0)
595 {
596 bKeepAlive = false;
597 LOG(VB_HTTP, LOG_ERR,
598 QString("socket(%1) - Error returned from "
599 "SendResponse... Closing connection")
600 .arg(pSocket->socketDescriptor()));
601 }
602
603 // -------------------------------------------------------
604 // Check to see if a PostProcess was registered
605 // -------------------------------------------------------
606 if ( pRequest->m_pPostProcess != nullptr )
608
609 delete pRequest;
610 pRequest = nullptr;
611 }
612 else
613 {
614 LOG(VB_GENERAL, LOG_ERR,
615 "Error Creating BufferedSocketDeviceRequest");
616 bKeepAlive = false;
617 }
618 }
619 }
620 }
621 catch(...)
622 {
623 LOG(VB_GENERAL, LOG_ERR,
624 "HttpWorkerThread::ProcessWork - Unexpected Exception.");
625 }
626
627 delete pRequest;
628
629 if ((pSocket->error() != QAbstractSocket::UnknownSocketError) &&
630 (!bKeepAlive || pSocket->error() != QAbstractSocket::SocketTimeoutError)) // This 'error' isn't an error when keep-alive is active
631 {
632 LOG(VB_HTTP, LOG_WARNING, QString("HttpWorker(%1): Error %2 (%3)")
633 .arg(m_socket)
634 .arg(pSocket->errorString())
635 .arg(pSocket->error()));
636 }
637
638 std::chrono::milliseconds writeTimeout = 5s;
639 // Make sure any data in the buffer is flushed before the socket is closed
640 if (pSocket->bytesToWrite() > 0)
641 {
642 LOG(VB_HTTP, LOG_DEBUG,
643 QString("HttpWorker(%1): Waiting for %2 bytes to be written before closing the connection.")
644 .arg(m_socket).arg(pSocket->bytesToWrite())
645 );
646 }
647 attempt_time.start();
648 while (m_httpServer.IsRunning() &&
649 pSocket->isValid() &&
650 pSocket->state() == QAbstractSocket::ConnectedState &&
651 pSocket->bytesToWrite() > 0 &&
652 attempt_time.elapsed() < writeTimeout
653 )
654 {
655 // If the client stops reading for longer than 'writeTimeout' then
656 // stop waiting for them. We can't afford to leave the socket
657 // connected indefinately, it could be used by another client.
658 //
659 // NOTE: Some clients deliberately stall as a way of 'pausing' A/V
660 // streaming. We should create a new server extension or adjust the
661 // timeout according to the User-Agent, instead of increasing the
662 // standard timeout. However we should ALWAYS have a timeout.
663 pSocket->waitForBytesWritten(k_poll_interval.count());
664 }
665
666 if (pSocket->bytesToWrite() > 0)
667 {
668 LOG(VB_HTTP, LOG_WARNING, QString("HttpWorker(%1): "
669 "Failed to write %2 bytes to "
670 "socket, (%3)")
671 .arg(m_socket)
672 .arg(pSocket->bytesToWrite())
673 .arg(pSocket->errorString()));
674 }
675
676 LOG(VB_HTTP, LOG_INFO, QString("HttpWorker(%1): Connection %2 closed. %3 requests were handled")
677 .arg(m_socket)
678 .arg(pSocket->socketDescriptor())
679 .arg(nRequestsHandled));
680
681 pSocket->close();
682 delete pSocket;
683 pSocket = nullptr;
684
685 LOG(VB_HTTP, LOG_DEBUG, QString("HttpWorker::run() socket=%1 -- end").arg(m_socket));
686}
687
688
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:251
IPostProcess * m_pPostProcess
Definition: httprequest.h:160
bool m_bEncrypted
Definition: httprequest.h:143
HttpRequestType m_eType
Definition: httprequest.h:122
bool GetKeepAlive() const
Definition: httprequest.h:233
QString m_sBaseUrl
Definition: httprequest.h:129
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:52
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:416
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:161
void RegisterExtension(HttpServerExtension *pExtension)
Definition: httpserver.cpp:313
HttpWorker(HttpServer &httpServer, qintptr sock, PoolServerType type, const QSslConfiguration &sslConfig)
Definition: httpserver.cpp:440
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:460
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)
void waitForDone(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:60
@ ResponseTypeHTML
Definition: httprequest.h:81
@ ResponseTypeHeader
Definition: httprequest.h:88
@ RequestTypeMSearch
Definition: httprequest.h:59
@ RequestTypeSubscribe
Definition: httprequest.h:60
@ RequestTypeNotify
Definition: httprequest.h:62
@ RequestTypePost
Definition: httprequest.h:52
@ RequestTypeOptions
Definition: httprequest.h:56
@ RequestTypeUnsubscribe
Definition: httprequest.h:61
@ RequestTypeGet
Definition: httprequest.h:50
@ RequestTypeHead
Definition: httprequest.h:51
@ RequestTypeUnknown
Definition: httprequest.h:48
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