Merge lp:~mandel/ubuntu-download-manager/follow-redirects into lp:ubuntu-download-manager

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 229
Merged at revision: 227
Proposed branch: lp:~mandel/ubuntu-download-manager/follow-redirects
Merge into: lp:ubuntu-download-manager
Diff against target: 397 lines (+258/-7)
8 files modified
ubuntu-download-manager-priv/downloads/file_download.cpp (+61/-7)
ubuntu-download-manager-priv/downloads/file_download.h (+3/-0)
ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.cpp (+6/-0)
ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.h (+2/-0)
ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.cpp (+2/-0)
ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.h (+3/-0)
ubuntu-download-manager-tests/downloads/test_download.cpp (+175/-0)
ubuntu-download-manager-tests/downloads/test_download.h (+6/-0)
To merge this branch: bzr merge lp:~mandel/ubuntu-download-manager/follow-redirects
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve
PS Jenkins bot continuous-integration Approve
Roberto Alsina (community) Approve
Review via email: mp+204666@code.launchpad.net

Commit message

Ensure that redirects are followed from udm unless we are in a loop. If the download gets in a redirect loop a network error is emitted.

Description of the change

Ensure that redirects are followed from udm unless we are in a loop. If the download gets in a redirect loop a network error is emitted.

To post a comment you must log in.
228. By Manuel de la Peña

Raise an error if we have file system issues.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Roberto Alsina (ralsina) :
review: Approve
229. By Manuel de la Peña

Fix some small space alignment issue in the code.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Mike McCracken (mikemc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu-download-manager-priv/downloads/file_download.cpp'
2--- ubuntu-download-manager-priv/downloads/file_download.cpp 2014-02-03 15:30:58 +0000
3+++ ubuntu-download-manager-priv/downloads/file_download.cpp 2014-02-04 16:23:22 +0000
4@@ -291,16 +291,54 @@
5 }
6
7 void
8-FileDownload::onFinished() {
9+FileDownload::onRedirect(QUrl redirect) {
10+ LOG(INFO) << "Following redirect to" << redirect;
11+ // update the _url value and perform a second request to try and get the data
12+ if (_visitedUrls.contains(redirect)) {
13+ // we are in a loop!!! we have to raise an error about this.
14+ LOG(WARNING) << "Redirect loop found";
15+ NetworkErrorStruct err(QNetworkReply::ContentNotFoundError);
16+ emit networkError(err);
17+ emitError(NETWORK_ERROR);
18+ return;
19+ }
20+ _visitedUrls.append(_url);
21+ _url = redirect;
22+
23+ // clean the reply
24+ disconnectFromReplySignals();
25+ _reply->deleteLater();
26+ _reply = nullptr;
27+
28+ // clean the local file
29+ cleanUpCurrentData();
30+
31+ // perform again the request but do not emit started signal
32+ _currentData = FileManager::instance()->createFile(_filePath);
33+ bool canWrite = _currentData->open(QIODevice::ReadWrite | QFile::Append);
34+
35+ if (!canWrite) {
36+ emitError(FILE_SYSTEM_ERROR);
37+ return;
38+ }
39+
40+ _reply = _requestFactory->get(buildRequest());
41+ _reply->setReadBufferSize(throttle());
42+
43+ connectToReplySignals();
44+}
45+
46+void
47+FileDownload::onDownloadCompleted() {
48 TRACE << _url;
49
50 // if the hash is present we check it
51 if (!_hash.isEmpty()) {
52 emit processing(filePath());
53 _currentData->reset();
54- QCryptographicHash hash(_algo);
55- // addData is smart enough to not load the entire file in memory
56- hash.addData(_currentData->device());
57+ QCryptographicHash hash(_algo);
58+ // addData is smart enough to not load the entire file in memory
59+ hash.addData(_currentData->device());
60 QString fileSig = QString(hash.result().toHex());
61
62 if (fileSig != _hash) {
63@@ -316,7 +354,8 @@
64 // finish signals once the command was done (or an error ocurred in
65 // the command execution.
66 if (metadata().contains(METADATA_COMMAND_KEY)) {
67- // just emit processing if we DO NOT have a hash because else we already emitted it.
68+ // just emit processing if we DO NOT have a hash because else we
69+ // already emitted it.
70 if (_hash.isEmpty()) {
71 emit processing(filePath());
72 }
73@@ -368,6 +407,21 @@
74 }
75
76 void
77+FileDownload::onFinished() {
78+ TRACE << _url;
79+ // the reply has finished but the resource might have been moved
80+ // and we must do a new request
81+ auto redirect = _reply->attribute(
82+ QNetworkRequest::RedirectionTargetAttribute).toUrl();
83+
84+ if(!redirect.isEmpty() && redirect != _url) {
85+ onRedirect(redirect);
86+ } else {
87+ onDownloadCompleted();
88+ }
89+}
90+
91+void
92 FileDownload::onSslErrors(const QList<QSslError>& errors) {
93 TRACE << errors;
94 if (!_reply->canIgnoreSslErrors(errors)) {
95@@ -539,7 +593,7 @@
96 if (!secondPart.isEmpty()) {
97 secondPart = "." + secondPart;
98 firstPart = path.left(path.size() - secondPart.size());
99- }
100+ }
101
102 // Try with an ever-increasing number suffix, until we've reached a file
103 // that does not yet exist.
104@@ -557,7 +611,7 @@
105
106 void
107 FileDownload::cleanUpCurrentData() {
108- bool success = true;
109+ bool success = true;
110 QFile::FileError error = QFile::NoError;
111 if (_currentData != nullptr) {
112 success = _currentData->remove();
113
114=== modified file 'ubuntu-download-manager-priv/downloads/file_download.h'
115--- ubuntu-download-manager-priv/downloads/file_download.h 2014-02-03 13:34:10 +0000
116+++ ubuntu-download-manager-priv/downloads/file_download.h 2014-02-04 16:23:22 +0000
117@@ -112,6 +112,8 @@
118 void init();
119 void onDownloadProgress(qint64 currentProgress, qint64);
120 void onError(QNetworkReply::NetworkError);
121+ void onRedirect(QUrl redirect);
122+ void onDownloadCompleted();
123 void onFinished();
124 void onSslErrors(const QList<QSslError>&);
125 void onProcessError(QProcess::ProcessError error);
126@@ -131,6 +133,7 @@
127 QCryptographicHash::Algorithm _algo;
128 NetworkReply* _reply = nullptr;
129 File* _currentData = nullptr;
130+ QList<QUrl> _visitedUrls;
131 };
132
133 } // Daemon
134
135=== modified file 'ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.cpp'
136--- ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.cpp 2014-01-27 16:36:44 +0000
137+++ ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.cpp 2014-02-04 16:23:22 +0000
138@@ -153,6 +153,12 @@
139 }
140
141 void
142+FakeNetworkReply::setAttribute(QNetworkRequest::Attribute code,
143+ QUrl url) {
144+ _attrs[code] = url;
145+}
146+
147+void
148 FakeNetworkReply::clearAttribute(QNetworkRequest::Attribute code) {
149 if (_attrs.contains(code)) {
150 _attrs.remove(code);
151
152=== modified file 'ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.h'
153--- ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.h 2014-01-27 16:36:44 +0000
154+++ ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/network_reply.h 2014-02-04 16:23:22 +0000
155@@ -65,6 +65,8 @@
156 int value);
157 void setAttribute(QNetworkRequest::Attribute code,
158 QString message);
159+ void setAttribute(QNetworkRequest::Attribute code,
160+ QUrl url);
161 void clearAttribute(QNetworkRequest::Attribute code);
162
163 private:
164
165=== modified file 'ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.cpp'
166--- ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.cpp 2014-01-24 11:54:24 +0000
167+++ ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.cpp 2014-02-04 16:23:22 +0000
168@@ -32,6 +32,7 @@
169 FakeRequestFactory::FakeRequestFactory(QObject *parent)
170 : RequestFactory(parent),
171 Fake() {
172+ qRegisterMetaType<NetworkReply*>("NetworkReply*");
173 }
174
175 NetworkReply*
176@@ -53,6 +54,7 @@
177 // if we are recording we do set the recording of the returned reply
178 reply->record();
179 }
180+ emit requestCreated(reply);
181 return reply;
182 }
183
184
185=== modified file 'ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.h'
186--- ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.h 2014-01-10 16:38:56 +0000
187+++ ubuntu-download-manager-test-lib/ubuntu/download_manager/tests/server/request_factory.h 2014-02-04 16:23:22 +0000
188@@ -50,6 +50,9 @@
189 NetworkReply* get(const QNetworkRequest& request);
190 QList<QSslCertificate> acceptedCertificates() override;
191 void setAcceptedCertificates(const QList<QSslCertificate>& certs) override;
192+
193+ signals:
194+ void requestCreated(NetworkReply*);
195 };
196
197 #endif // FAKE_QNETWORK_ACCESS_MANAGER_H
198
199=== modified file 'ubuntu-download-manager-tests/downloads/test_download.cpp'
200--- ubuntu-download-manager-tests/downloads/test_download.cpp 2014-02-03 13:34:10 +0000
201+++ ubuntu-download-manager-tests/downloads/test_download.cpp 2014-02-04 16:23:22 +0000
202@@ -1986,3 +1986,178 @@
203 QString("FILE SYSTEM ERROR: %1").arg(QFile::WriteError));
204 _fileManager->setFile(nullptr);
205 }
206+
207+void
208+TestDownload::testRedirectCycle() {
209+ // the test works as follows, create a request, pause and set the redirect
210+ // header, wait until the second request is created via the signal from the
211+ // request factory and set the redirect to a first url
212+ QUrl redirectUrl("http://redirect.example.com");
213+ _reqFactory->record();
214+ QScopedPointer<FileDownload> download(new FileDownload(_id, _path, _isConfined,
215+ _rootPath, _url, _metadata, _headers));
216+
217+ download->start(); // change state
218+ download->startDownload();
219+
220+ // we need to set the data before we pause!!!
221+ QList<MethodData> calledMethods = _reqFactory->calledMethods();
222+ QCOMPARE(1, calledMethods.count());
223+ FakeNetworkReply* reply = qobject_cast<FakeNetworkReply*>(
224+ calledMethods[0].params().outParams()[0]);
225+
226+ // set the attr for a redirect to a new url
227+ // makes the process to be executed
228+ reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute,
229+ redirectUrl);
230+ QSignalSpy replySpy(_reqFactory, SIGNAL(requestCreated(NetworkReply*)));
231+
232+ // use a spy to wait for the second reply
233+ reply->emitFinished();
234+
235+ QTRY_COMPARE(1, replySpy.count());
236+
237+ reply = qobject_cast<FakeNetworkReply*>(
238+ replySpy.takeFirst().at(0).value<NetworkReply*>());
239+
240+ download->pause();
241+
242+ // set the attr to be a loop
243+ reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute,
244+ _url);
245+
246+ // emit finished and an errors should be emitted
247+ QSignalSpy errorSpy(download.data(), SIGNAL(error(QString)));
248+ QSignalSpy networkErrorSpy(download.data(),
249+ SIGNAL(networkError(NetworkErrorStruct)));
250+
251+ reply->emitFinished();
252+
253+ QTRY_COMPARE(networkErrorSpy.count(), 1);
254+ QTRY_COMPARE(errorSpy.count(), 1);
255+}
256+
257+void
258+TestDownload::testSingleRedirect() {
259+ // ensure that a single redirect is followed
260+ QUrl redirectUrl("http://redirect.example.com");
261+ _reqFactory->record();
262+ QScopedPointer<FileDownload> download(new FileDownload(_id, _path, _isConfined,
263+ _rootPath, _url, _metadata, _headers));
264+
265+ download->start(); // change state
266+ download->startDownload();
267+
268+ // we need to set the data before we pause!!!
269+ QList<MethodData> calledMethods = _reqFactory->calledMethods();
270+ QCOMPARE(1, calledMethods.count());
271+ FakeNetworkReply* reply = qobject_cast<FakeNetworkReply*>(
272+ calledMethods[0].params().outParams()[0]);
273+
274+ // set the attr for a redirect to a new url
275+ // makes the process to be executed
276+ reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute,
277+ redirectUrl);
278+ QSignalSpy replySpy(_reqFactory, SIGNAL(requestCreated(NetworkReply*)));
279+
280+ // use a spy to wait for the second reply
281+ reply->emitFinished();
282+
283+ // ensure that a second request is performed
284+ QTRY_COMPARE(1, replySpy.count());
285+
286+ reply = qobject_cast<FakeNetworkReply*>(
287+ replySpy.takeFirst().at(0).value<NetworkReply*>());
288+
289+ download->pause();
290+
291+ QSignalSpy errorSpy(download.data(), SIGNAL(error(QString)));
292+ QSignalSpy networkErrorSpy(download.data(),
293+ SIGNAL(networkError(NetworkErrorStruct)));
294+ QSignalSpy finishedSpy(download.data(), SIGNAL(finished(QString)));
295+
296+ reply->emitFinished();
297+
298+ QTRY_COMPARE(networkErrorSpy.count(), 0);
299+ QTRY_COMPARE(errorSpy.count(), 0);
300+ QTRY_COMPARE(finishedSpy.count(), 1);
301+}
302+
303+void
304+TestDownload::testSeveralRedirects_data() {
305+ QTest::addColumn<QStringList>("urls");
306+
307+ QStringList first;
308+ first.append("http://first.com/resource");
309+ first.append("http://first.com/moved_resource");
310+ first.append("http://first.com/last");
311+ QTest::newRow("Three redirects") << first;
312+
313+ QStringList second;
314+ second.append("http://second.com/resource");
315+ second.append("http://second.com/moved1");
316+ second.append("http://second.com/moved2");
317+ second.append("http://second.com/moved3");
318+ second.append("http://second.com/moved4");
319+ QTest::newRow("Five redirects") << second;
320+
321+ QStringList third;
322+ third.append("http://third.com/resource");
323+ third.append("http://third.com/moved1");
324+ third.append("http://third.com/moved2");
325+ third.append("http://third.com/moved3");
326+ third.append("http://third.com/moved4");
327+ third.append("http://third.com/moved5");
328+ third.append("http://third.com/moved6");
329+ QTest::newRow("Seven redirects") << third;
330+
331+}
332+
333+void
334+TestDownload::testSeveralRedirects() {
335+ QFETCH(QStringList, urls);
336+ auto redirectCount = 0;
337+ _reqFactory->record();
338+ QScopedPointer<FileDownload> download(new FileDownload(_id, _path, _isConfined,
339+ _rootPath, _url, _metadata, _headers));
340+
341+ download->start(); // change state
342+ download->startDownload();
343+
344+ // we need to set the data before we pause!!!
345+ QList<MethodData> calledMethods = _reqFactory->calledMethods();
346+ QCOMPARE(1, calledMethods.count());
347+ FakeNetworkReply* reply = qobject_cast<FakeNetworkReply*>(
348+ calledMethods[0].params().outParams()[0]);
349+
350+ // redirect all the required times
351+ foreach(const QString& url, urls) {
352+ reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute,
353+ url);
354+ QSignalSpy replySpy(_reqFactory, SIGNAL(requestCreated(NetworkReply*)));
355+
356+ // use a spy to wait for the second reply
357+ reply->emitFinished();
358+
359+ // ensure that a second request is performed
360+ QTRY_COMPARE(1, replySpy.count());
361+
362+ reply = qobject_cast<FakeNetworkReply*>(
363+ replySpy.takeFirst().at(0).value<NetworkReply*>());
364+ redirectCount++;
365+ }
366+
367+ download->pause();
368+
369+ QSignalSpy errorSpy(download.data(), SIGNAL(error(QString)));
370+ QSignalSpy networkErrorSpy(download.data(),
371+ SIGNAL(networkError(NetworkErrorStruct)));
372+ QSignalSpy finishedSpy(download.data(), SIGNAL(finished(QString)));
373+
374+ reply->emitFinished();
375+
376+ QTRY_COMPARE(networkErrorSpy.count(), 0);
377+ QTRY_COMPARE(errorSpy.count(), 0);
378+ QTRY_COMPARE(finishedSpy.count(), 1);
379+ QCOMPARE(redirectCount, urls.count());
380+}
381
382=== modified file 'ubuntu-download-manager-tests/downloads/test_download.h'
383--- ubuntu-download-manager-tests/downloads/test_download.h 2014-01-27 16:36:44 +0000
384+++ ubuntu-download-manager-tests/downloads/test_download.h 2014-02-04 16:23:22 +0000
385@@ -154,6 +154,12 @@
386 void testFileSystemErrorProgress();
387 void testFileSystemErrorPause();
388
389+ // test different redirects
390+ void testRedirectCycle();
391+ void testSingleRedirect();
392+ void testSeveralRedirects_data();
393+ void testSeveralRedirects();
394+
395 private:
396 QString _id;
397 bool _isConfined;

Subscribers

People subscribed via source and target branches