KNewStuff

httpworker.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Dan Leinir Turthra Jensen <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "httpworker.h"
8 
9 #include "knewstuffcore_debug.h"
10 
11 #include <QFile>
12 #include <QMutex>
13 #include <QMutexLocker>
14 #include <QNetworkAccessManager>
15 #include <QNetworkDiskCache>
16 #include <QNetworkRequest>
17 #include <QNetworkReply>
18 #include <QStandardPaths>
19 #include <QStorageInfo>
20 
21 class HTTPWorkerNAM {
22 public:
23  HTTPWorkerNAM()
24  {
25  QMutexLocker locker(&mutex);
26  const QString cacheLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/knewstuff");
27  cache.setCacheDirectory(cacheLocation);
28  QStorageInfo storageInfo(cacheLocation);
29  cache.setMaximumCacheSize(qMin(50 * 1024 * 1024, (int)(storageInfo.bytesTotal() / 1000)));
30  nam.setCache(&cache);
31  }
33  QMutex mutex;
34 
35  QNetworkReply* get(const QNetworkRequest& request)
36  {
37  QMutexLocker locker(&mutex);
38  return nam.get(request);
39  }
40 
41 private:
42  QNetworkDiskCache cache;
43 };
44 
45 Q_GLOBAL_STATIC(HTTPWorkerNAM, s_httpWorkerNAM)
46 
47 using namespace KNSCore;
48 
49 class HTTPWorker::Private
50 {
51 public:
52  Private()
53  : jobType(GetJob)
54  , reply(nullptr)
55  {}
56  JobType jobType;
57  QUrl source;
58  QUrl destination;
59  QNetworkReply* reply;
60  QUrl redirectUrl;
61 
62  QFile dataFile;
63 };
64 
65 HTTPWorker::HTTPWorker(const QUrl& url, JobType jobType, QObject* parent)
66  : QObject(parent)
67  , d(new Private)
68 {
69  qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO;
70  d->jobType = jobType;
71  d->source = url;
72 }
73 
74 HTTPWorker::HTTPWorker(const QUrl& source, const QUrl& destination, KNSCore::HTTPWorker::JobType jobType, QObject* parent)
75  : QObject(parent)
76  , d(new Private)
77 {
78  qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO;
79  d->jobType = jobType;
80  d->source = source;
81  d->destination = destination;
82 }
83 
84 HTTPWorker::~HTTPWorker()
85 {
86  delete d;
87 }
88 
89 void HTTPWorker::setUrl(const QUrl& url)
90 {
91  d->source = url;
92 }
93 
94 void HTTPWorker::startRequest()
95 {
96  if(d->reply) {
97  // only run one request at a time...
98  return;
99  }
100 
101  QNetworkRequest request(d->source);
102  d->reply = s_httpWorkerNAM->get(request);
103  connect(d->reply, &QNetworkReply::readyRead, this, &HTTPWorker::handleReadyRead);
104  connect(d->reply, &QNetworkReply::finished, this, &HTTPWorker::handleFinished);
105  if(d->jobType == DownloadJob) {
106  d->dataFile.setFileName(d->destination.toLocalFile());
107  connect(this, &HTTPWorker::data, this, &HTTPWorker::handleData);
108  }
109 }
110 
111 void HTTPWorker::handleReadyRead()
112 {
113  QMutexLocker locker(&s_httpWorkerNAM->mutex);
114  if (d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isNull()) {
115  do {
116  emit data(d->reply->read(32768));
117  } while(!d->reply->atEnd());
118  }
119 }
120 
121 void HTTPWorker::handleFinished()
122 {
123  qCDebug(KNEWSTUFFCORE) << Q_FUNC_INFO << d->reply->url();
124  if (d->reply->error() != QNetworkReply::NoError) {
125  qCWarning(KNEWSTUFFCORE) << d->reply->errorString();
126  emit error(d->reply->errorString());
127  }
128 
129  // Check if the data was obtained from cache or not
130  QString fromCache = d->reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool() ? QStringLiteral("(cached)") : QStringLiteral("(NOT cached)");
131 
132  // Handle redirections
133  const QUrl possibleRedirectUrl = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
134  if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != d->redirectUrl) {
135  d->redirectUrl = d->reply->url().resolved(possibleRedirectUrl);
136  if (d->redirectUrl.scheme().startsWith(QLatin1String("http"))) {
137  qCDebug(KNEWSTUFFCORE) << d->reply->url().toDisplayString() << "was redirected to" << d->redirectUrl.toDisplayString() << fromCache << d->reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
138  d->reply->deleteLater();
139  QNetworkRequest request(d->redirectUrl);
140  d->reply = s_httpWorkerNAM->get(request);
141  connect(d->reply, &QNetworkReply::readyRead, this, &HTTPWorker::handleReadyRead);
142  connect(d->reply, &QNetworkReply::finished, this, &HTTPWorker::handleFinished);
143  return;
144  } else {
145  qCWarning(KNEWSTUFFCORE) << "Redirection to" << d->redirectUrl.toDisplayString() << "forbidden.";
146  }
147  }
148  else {
149  qCDebug(KNEWSTUFFCORE) << "Data for" << d->reply->url().toDisplayString() << "was fetched" << fromCache;
150  }
151 
152  if(d->dataFile.isOpen()) {
153  d->dataFile.close();
154  }
155 
156  d->redirectUrl.clear();
157  emit completed();
158 }
159 
160 void HTTPWorker::handleData(const QByteArray& data)
161 {
162  // It turns out that opening a file and then leaving it hanging without writing to it immediately will, at times
163  // leave you with a file that suddenly (seemingly magically) no longer exists. Thanks for that.
164  if(!d->dataFile.isOpen()) {
165  if(d->dataFile.open(QIODevice::WriteOnly)) {
166  qCDebug(KNEWSTUFFCORE) << "Opened file" << d->dataFile.fileName() << "for writing.";
167  }
168  else {
169  qCWarning(KNEWSTUFFCORE) << "Failed to open file for writing!";
170  emit error(QStringLiteral("Failed to open file %1 for writing!").arg(d->destination.toLocalFile()));
171  }
172  }
173  qCDebug(KNEWSTUFFCORE) << "Writing" << data.length() << "bytes of data to" << d->dataFile.fileName();
174  quint64 written = d->dataFile.write(data);
175  if(d->dataFile.error()) {
176  qCDebug(KNEWSTUFFCORE) << "File has error" << d->dataFile.errorString();
177  }
178  qCDebug(KNEWSTUFFCORE) << "Wrote" << written << "bytes. File is now size" << d->dataFile.size();
179 }
QString url(QUrl::FormattingOptions options) const const
QString writableLocation(QStandardPaths::StandardLocation type)
Contains the core functionality for handling interaction with NewStuff providers. ...
int length() const const
bool isEmpty() const const
int toInt(bool *ok, int base) const const
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
QNetworkReply * get(const QNetworkRequest &request)
void readyRead()
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Aug 11 2020 22:43:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.