24 using namespace std::chrono_literals;
26 #define LOC QString(m_peer + ": ")
36 QSslSocket* sslsocket =
nullptr;
40 sslsocket =
new QSslSocket(
this);
68 LOG(VB_HTTP, LOG_INFO,
LOC +
"New connection");
71 m_timer.setTimerType(Qt::PreciseTimer);
83 auto service = MythHTTPService::Create<MythHTTPServices>();
86 if (services ==
nullptr)
88 LOG(VB_HTTP, LOG_ERR,
LOC +
"Failed to get root services handler.");
147 LOG(VB_HTTP, LOG_INFO,
LOC +
"Disconnected");
152 if (
Error != QAbstractSocket::RemoteHostClosedError)
153 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Error %1 (%2)").arg(
Error).arg(
m_socket->errorString()));
162 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Timed out waiting for activity");
167 if (
m_socket->state() == QAbstractSocket::ConnectedState)
174 LOG(VB_HTTP, LOG_INFO,
LOC +
"Timeout during write. Quitting.");
191 LOG(VB_HTTP, LOG_INFO,
LOC +
"Stop");
202 QThread::currentThread()->quit();
216 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"New request but queue is not empty");
224 LOG(VB_HTTP, LOG_WARNING,
LOC +
"HTTP error");
237 if (request->m_status !=
HTTPOK)
252 if (response ==
nullptr)
257 if (path == request->m_url.toString())
259 response = std::invoke(
function, request);
266 const QString& rpath = request->m_path;
269 if (response ==
nullptr)
271 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Processing: path '%1' file '%2'")
272 .arg(rpath, request->m_fileName));
276 if (service->Name() == rpath)
278 response = service->HTTPRequest(request);
286 if (response ==
nullptr)
293 auto instance = std::invoke(constructor);
294 response = instance->HTTPRequest(request);
304 if (response ==
nullptr)
311 response = std::invoke(
function, request);
319 if (response ==
nullptr)
333 if (response ==
nullptr || response->m_status ==
HTTPNotFound)
338 response = std::invoke(
function, request);
343 if (response ==
nullptr)
360 if (!Response || (
m_socket->state() != QAbstractSocket::ConnectedState))
365 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Responding but queue is not empty");
378 for (
const auto & header : std::as_const(Response->m_responseHeaders))
380 LOG(VB_HTTP, LOG_DEBUG, header->trimmed().constData());
385 if (!std::get_if<std::monostate>(&Response->m_response))
386 m_queue.push_back(Response->m_response);
388 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"No content in response");
395 if (
const auto * data = std::get_if<HTTPData>(&
content))
397 m_totalToSend += (*data)->m_partialSize > 0 ? (*data)->m_partialSize : (*data)->size();
400 else if (
const auto *
file = std::get_if<HTTPFile>(&
content))
402 m_totalToSend += (*file)->m_partialSize > 0 ? (*file)->m_partialSize : (*file)->size();
420 if (!(Socket && Response))
423 Response->Finalise(Config);
424 auto * socket =
new QTcpSocket();
425 socket->setSocketDescriptor(Socket);
426 for (
const auto & header : std::as_const(Response->m_responseHeaders))
427 socket->write(*header);
429 socket->disconnectFromHost();
435 auto chunkheader = [&](
const QByteArray& Data)
437 int64_t wrote =
m_socket->write(Data);
440 LOG(VB_GENERAL, LOG_ERR,
"Error writing chunk header");
461 int64_t buffered =
m_socket->bytesToWrite();
462 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Socket sent %1bytes (still to send %2)")
463 .arg(Written).arg(buffered));
470 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Draining buffers");
476 auto seconds =
static_cast<double>(
m_writeTime.nsecsElapsed()) / 1000000000.0;
477 auto rate =
static_cast<uint64_t
>(
static_cast<double>(
m_totalSent) / seconds);
478 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Wrote %1bytes in %2seconds (%3)")
491 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Write complete but queue not empty");
498 while ((available > 0) && !
m_queue.empty())
503 int64_t itemsize = 0;
506 auto * data = std::get_if<HTTPData>(&
m_queue.front());
507 auto *
file = std::get_if<HTTPFile>(&
m_queue.front());
512 written = (*data)->m_written;
513 itemsize = (*data)->m_partialSize > 0 ? (*data)->m_partialSize :
static_cast<int64_t
>((*data)->size());
514 towrite = std::min(itemsize - written, available);
516 HTTPMulti multipart {
nullptr,
nullptr };
517 if (!(*data)->m_ranges.empty())
520 chunkheader(QStringLiteral(
"%1\r\n").arg(towrite, 0, 16).toLatin1().constData());
522 wrote +=
m_socket->write(multipart.first->constData());
523 wrote +=
m_socket->write((*data)->constData() + written + offset, towrite);
524 if (multipart.second)
525 wrote +=
m_socket->write(multipart.second->constData());
532 written = (*file)->m_written;
533 itemsize = (*file)->m_partialSize > 0 ? (*file)->m_partialSize :
static_cast<int64_t
>((*file)->size());
534 towrite = std::min(itemsize - written, available);
535 HTTPMulti multipart {
nullptr,
nullptr };
536 if (!(*file)->m_ranges.empty())
540 if (offset != (*file)->pos())
541 (*file)->seek(offset);
543 int64_t writebufsize = std::min(itemsize,
static_cast<int64_t
>(
HTTP_CHUNKSIZE));
552 chunkheader(QStringLiteral(
"%1\r\n").arg(
read, 0, 16).toLatin1().constData());
554 wrote +=
m_socket->write(multipart.first->constData());
556 if (multipart.second)
557 wrote +=
m_socket->write(multipart.second->constData());
563 if (wrote < 0 ||
read < 0)
565 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Write error (Read: %1 Write: %2)")
566 .arg(
read).arg(wrote));
572 if (data) (*data)->m_written = written;
573 if (
file) (*file)->m_written = written;
578 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Sent %1 bytes (cum: %2 of %3)")
583 if (written >= itemsize)
586 chunkheader(
"0\r\n\r\n");
609 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create websocket");
617 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create websocket event listener");
644 if (Payloads.empty())
651 if (Payloads.empty())