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 : }
|