LCOV - code coverage report
Current view: top level - lib/FangFeedDiscovery - FeedFetcher.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 81.7 % 71 58
Test Date: 2026-04-19 00:35:54 Functions: 91.7 % 12 11

            Line data    Source code
       1              : #include "FeedFetcher.h"
       2              : 
       3              : #include <QDateTime>
       4              : #include <QtConcurrent>
       5              : 
       6              : #include "FeedParser.h"
       7              : 
       8           41 : FeedFetcher::FeedFetcher(QObject *parent)
       9           41 :     : FeedFetcher(nullptr, parent)
      10           41 : {}
      11              : 
      12           49 : FeedFetcher::FeedFetcher(QNetworkAccessManager* networkManager, QObject *parent) :
      13              :     FeedSource(parent),
      14           49 :     result(FeedFetchResult::OK), networkError(QNetworkReply::NetworkError::NoError),
      15           49 :     downloader(nullptr),
      16           49 :     permanentRedirect(false)
      17              : {
      18           49 :     WebDownloadConfig config;
      19           49 :     config.timeoutMs = 30000;
      20           49 :     config.maxRedirects = 10;
      21           49 :     config.useInactivityTimeout = false;
      22              : 
      23           49 :     downloader = new QWebDownload(config, this, networkManager);
      24           49 :     connect(downloader, &QWebDownload::finishedWithResult,
      25           49 :             this, &FeedFetcher::onDownloadResult);
      26              : 
      27           49 :     connect(&parseWatcher, &QFutureWatcher<FeedParseResult>::finished,
      28           57 :             this, [this] { workerDone(parseWatcher.result()); });
      29           49 : }
      30              : 
      31           90 : FeedFetcher::~FeedFetcher() = default;
      32              : 
      33            8 : void FeedFetcher::parse(const QUrl& url,
      34              :                        const QString& ifNoneMatch, const QString& ifModifiedSince)
      35              : {
      36            8 :     initParse(url);
      37            8 :     permanentRedirect = false;
      38              : 
      39            8 :     downloader->clearRequestHeaders();
      40            8 :     if (!ifNoneMatch.isEmpty()) {
      41            0 :         downloader->setRequestHeader("If-None-Match", ifNoneMatch.toUtf8());
      42              :     }
      43            8 :     if (!ifModifiedSince.isEmpty()) {
      44            0 :         downloader->setRequestHeader("If-Modified-Since", ifModifiedSince.toUtf8());
      45              :     }
      46              : 
      47            8 :     downloader->get(url);
      48            8 : }
      49              : 
      50            8 : void FeedFetcher::onDownloadResult(WebDownloadResult downloadResult)
      51              : {
      52            8 :     if (!downloadResult.ok()) {
      53            0 :         networkError = downloadResult.networkError;
      54            0 :         result = FeedFetchResult::NetworkError;
      55            0 :         emit done();
      56            0 :         return;
      57              :     }
      58              : 
      59              :     // 304 Not Modified: Content hasn't changed, nothing to do.
      60            8 :     if (downloadResult.statusCode == 304) {
      61            0 :         result = FeedFetchResult::NotModified;
      62            0 :         emit done();
      63            0 :         return;
      64              :     }
      65              : 
      66              :     // Success path.
      67            8 :     finalFeedURL = downloadResult.url;
      68            8 :     permanentRedirect = downloadResult.permanentRedirect;
      69            8 :     respEtag = QString::fromUtf8(downloadResult.responseHeader("ETag"));
      70            8 :     respLastModified = QString::fromUtf8(downloadResult.responseHeader("Last-Modified"));
      71              : 
      72              :     // Discard conditional headers if Last-Modified is in the future, since
      73              :     // they would cause requests to always return 304 Not Modified.
      74            8 :     QDateTime parsed = QLocale::c().toDateTime(respLastModified, "ddd, dd MMM yyyy HH:mm:ss 'GMT'");
      75            8 :     if (parsed.isValid() && parsed > QDateTime::currentDateTimeUtc()) {
      76            1 :         respEtag.clear();
      77            1 :         respLastModified.clear();
      78              :     }
      79              : 
      80           16 :     auto future = QtConcurrent::run([data = downloadResult.data] {
      81            8 :         return FeedParser::parse(data);
      82            8 :     });
      83            8 :     parseWatcher.setFuture(future);
      84            8 : }
      85              : 
      86            3 : FeedFetchResult FeedFetcher::getResult()
      87              : {
      88            3 :     return result;
      89              : }
      90              : 
      91            0 : std::shared_ptr<RawFeed> FeedFetcher::getFeed()
      92              : {
      93            0 :     return result == FeedFetchResult::OK ? feed : nullptr;
      94              : }
      95              : 
      96            8 : void FeedFetcher::workerDone(FeedParseResult parseResult)
      97              : {
      98            8 :     if (result != FeedFetchResult::InProgress) {
      99              :         // Already emitted a finished signal.  Nothing to dooooo.
     100            0 :         return;
     101              :     }
     102              : 
     103            8 :     if (parseResult.ok()) {
     104            8 :         feed = parseResult.feed();
     105            8 :         feed->url = finalFeedURL;
     106            8 :         result = FeedFetchResult::OK;
     107              :     } else {
     108            0 :         result = FeedFetchResult::ParseError;
     109              :     }
     110              : 
     111            8 :     emit done();
     112              : }
     113              : 
     114            8 : void FeedFetcher::initParse(const QUrl& url)
     115              : {
     116            8 :     result = FeedFetchResult::InProgress;
     117            8 :     networkError = QNetworkReply::NetworkError::NoError;
     118            8 :     finalFeedURL = url;
     119            8 :     respEtag = QString();
     120            8 :     respLastModified = QString();
     121            8 : }
        

Generated by: LCOV version 2.0-1