24 using namespace std::chrono_literals;
26 #define LOC QString(m_peer + ": ")
36 QSslSocket* sslsocket =
nullptr;
40 sslsocket =
new QSslSocket(
this);
59 #if QT_VERSION < QT_VERSION_CHECK(5,15,0)
73 LOG(VB_HTTP, LOG_INFO,
LOC +
"New connection");
76 m_timer.setTimerType(Qt::PreciseTimer);
88 auto service = MythHTTPService::Create<MythHTTPServices>();
91 if (services ==
nullptr)
93 LOG(VB_HTTP, LOG_ERR,
LOC +
"Failed to get root services handler.");
152 LOG(VB_HTTP, LOG_INFO,
LOC +
"Disconnected");
157 if (
Error != QAbstractSocket::RemoteHostClosedError)
158 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Error %1 (%2)").arg(
Error).arg(
m_socket->errorString()));
167 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Timed out waiting for activity");
172 if (
m_socket->state() == QAbstractSocket::ConnectedState)
179 LOG(VB_HTTP, LOG_INFO,
LOC +
"Timeout during write. Quitting.");
196 LOG(VB_HTTP, LOG_INFO,
LOC +
"Stop");
207 QThread::currentThread()->quit();
221 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"New request but queue is not empty");
229 LOG(VB_HTTP, LOG_WARNING,
LOC +
"HTTP error");
242 if (request->m_status !=
HTTPOK)
257 if (response ==
nullptr)
262 if (path == request->m_url.toString())
264 response = std::invoke(
function, request);
271 const QString& rpath = request->m_path;
274 if (response ==
nullptr)
276 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Processing: path '%1' file '%2'")
277 .arg(rpath, request->m_fileName));
281 if (service->Name() == rpath)
283 response = service->HTTPRequest(request);
291 if (response ==
nullptr)
298 auto instance = std::invoke(constructor);
299 response = instance->HTTPRequest(request);
309 if (response ==
nullptr)
316 response = std::invoke(
function, request);
324 if (response ==
nullptr)
338 if (response ==
nullptr || response->m_status ==
HTTPNotFound)
343 response = std::invoke(
function, request);
348 if (response ==
nullptr)
365 if (!Response || (
m_socket->state() != QAbstractSocket::ConnectedState))
370 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Responding but queue is not empty");
383 for (
const auto & header : qAsConst(Response->m_responseHeaders))
385 LOG(VB_HTTP, LOG_DEBUG, header->trimmed().constData());
390 if (!std::get_if<std::monostate>(&Response->m_response))
391 m_queue.push_back(Response->m_response);
393 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"No content in response");
400 if (
const auto * data = std::get_if<HTTPData>(&
content))
402 m_totalToSend += (*data)->m_partialSize > 0 ? (*data)->m_partialSize : (*data)->size();
405 else if (
const auto *
file = std::get_if<HTTPFile>(&
content))
407 m_totalToSend += (*file)->m_partialSize > 0 ? (*file)->m_partialSize : (*file)->size();
425 if (!(Socket && Response))
428 Response->Finalise(Config);
429 auto * socket =
new QTcpSocket();
430 socket->setSocketDescriptor(Socket);
431 for (
const auto & header : qAsConst(Response->m_responseHeaders))
432 socket->write(*header);
434 socket->disconnectFromHost();
440 auto chunkheader = [&](
const QByteArray& Data)
442 int64_t wrote =
m_socket->write(Data);
445 LOG(VB_GENERAL, LOG_ERR,
"Error writing chunk header");
466 int64_t buffered =
m_socket->bytesToWrite();
467 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Socket sent %1bytes (still to send %2)")
468 .arg(Written).arg(buffered));
475 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Draining buffers");
481 auto seconds =
static_cast<double>(
m_writeTime.nsecsElapsed()) / 1000000000.0;
482 auto rate =
static_cast<uint64_t
>(
static_cast<double>(
m_totalSent) / seconds);
483 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Wrote %1bytes in %2seconds (%3)")
496 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Write complete but queue not empty");
503 while ((available > 0) && !
m_queue.empty())
508 int64_t itemsize = 0;
511 auto * data = std::get_if<HTTPData>(&
m_queue.front());
512 auto *
file = std::get_if<HTTPFile>(&
m_queue.front());
517 written = (*data)->m_written;
518 itemsize = (*data)->m_partialSize > 0 ? (*data)->m_partialSize :
static_cast<int64_t
>((*data)->size());
519 towrite = std::min(itemsize - written, available);
521 HTTPMulti multipart {
nullptr,
nullptr };
522 if (!(*data)->m_ranges.empty())
525 chunkheader(QStringLiteral(
"%1\r\n").arg(towrite, 0, 16).toLatin1().constData());
527 wrote +=
m_socket->write(multipart.first->constData());
528 wrote +=
m_socket->write((*data)->constData() + written + offset, towrite);
529 if (multipart.second)
530 wrote +=
m_socket->write(multipart.second->constData());
537 written = (*file)->m_written;
538 itemsize = (*file)->m_partialSize > 0 ? (*file)->m_partialSize :
static_cast<int64_t
>((*file)->size());
539 towrite = std::min(itemsize - written, available);
540 HTTPMulti multipart {
nullptr,
nullptr };
541 if (!(*file)->m_ranges.empty())
545 if (offset != (*file)->pos())
546 (*file)->seek(offset);
548 int64_t writebufsize = std::min(itemsize,
static_cast<int64_t
>(
HTTP_CHUNKSIZE));
557 chunkheader(QStringLiteral(
"%1\r\n").arg(
read, 0, 16).toLatin1().constData());
559 wrote +=
m_socket->write(multipart.first->constData());
561 if (multipart.second)
562 wrote +=
m_socket->write(multipart.second->constData());
568 if (wrote < 0 ||
read < 0)
570 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Write error (Read: %1 Write: %2)")
571 .arg(
read).arg(wrote));
577 if (data) (*data)->m_written = written;
578 if (
file) (*file)->m_written = written;
583 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Sent %1 bytes (cum: %2 of %3)")
588 if (written >= itemsize)
591 chunkheader(
"0\r\n\r\n");
614 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create websocket");
622 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create websocket event listener");
649 if (Payloads.empty())
656 if (Payloads.empty())