MythTV master
mythsingledownload.cpp
Go to the documentation of this file.
1
3#include "mythversion.h"
4#include "mythlogging.h"
5
6/*
7 * For simple one-at-a-time downloads, this routine (found at
8 * http://www.insanefactory.com/2012/08/qt-http-request-in-a-single-function/
9 * ) works well.
10 */
11
12#define LOC QString("MythSingleDownload: ")
13
14bool MythSingleDownload::DownloadURL(const QUrl &url, QByteArray *buffer,
15 std::chrono::seconds timeout,
16 uint redirs, qint64 maxsize, QString *final_url)
17{
18 m_lock.lock();
19
20 QEventLoop event_loop;
21 m_buffer = buffer;
22
23 // the HTTP request
24 QNetworkRequest req(url);
25 req.setRawHeader("User-Agent",
26 "MythTV v" + QByteArray(MYTH_BINARY_VERSION) + " MythSingleDownload");
27 m_replylock.lock();
28 m_reply = m_mgr.get(req);
29 m_replylock.unlock();
30
31 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
32 QNetworkRequest::AlwaysNetwork);
33
34 // "quit()" the event-loop, when the network request "finished()"
35 connect(&m_timer, &QTimer::timeout, &event_loop, &QEventLoop::quit);
36 connect(m_reply, &QNetworkReply::finished, &event_loop, &QEventLoop::quit);
37 connect(m_reply, &QNetworkReply::downloadProgress, this, &MythSingleDownload::Progress);
38
39 // Configure timeout and size limit
40 m_maxsize = maxsize;
41 m_timer.setSingleShot(true);
42 m_timer.start(timeout); // 30 secs. by default
43
44 bool ret = event_loop.exec() != 0; // blocks stack until quit() is called
45
46 disconnect(&m_timer, &QTimer::timeout, &event_loop, &QEventLoop::quit);
47 disconnect(m_reply, &QNetworkReply::finished, &event_loop, &QEventLoop::quit);
48 disconnect(m_reply, &QNetworkReply::downloadProgress, this, &MythSingleDownload::Progress);
49
50 if (ret)
51 {
52 LOG(VB_GENERAL, LOG_ERR, LOC + "eventloop failed");
53 }
54
55 m_replylock.lock();
56 if (!m_timer.isActive())
57 {
58 m_errorstring = "timed-out";
59 m_reply->abort();
60 ret = false;
61 }
62 else
63 {
64 m_timer.stop();
65 m_errorcode = m_reply->error();
66
67 QString redir = m_reply->attribute(
68 QNetworkRequest::RedirectionTargetAttribute).toUrl().toString();
69
70 if (!redir.isEmpty())
71 {
72 if (redirs <= 3)
73 {
74 LOG(VB_GENERAL, LOG_INFO, QString("%1 -> %2").arg(url.toString(), redir));
75 m_replylock.unlock();
76 m_lock.unlock();
77 return DownloadURL(redir, buffer, timeout, redirs + 1, maxsize, final_url);
78 }
79
80 LOG(VB_GENERAL, LOG_ERR, QString("%1: too many redirects").arg(url.toString()));
81 ret = false;
82 }
83 else if (m_errorcode == QNetworkReply::NoError)
84 {
85 if (final_url != nullptr)
86 *final_url = url.toString();
87 *m_buffer += m_reply->readAll();
88 m_errorstring.clear();
89 ret = true;
90 }
91 else
92 {
93 m_errorstring = m_reply->errorString();
94 ret = false;
95 }
96 }
97
98 m_replylock.unlock();
99 m_lock.unlock();
100
101 delete m_reply;
102 m_reply = nullptr;
103 m_buffer = nullptr;
104
105 return ret;
106}
107
109{
110 QMutexLocker replylock(&m_replylock);
111 if (m_reply)
112 {
113 LOG(VB_GENERAL, LOG_INFO, LOC + "Aborting download");
114 m_reply->abort();
115 }
116}
117
118void MythSingleDownload::Progress(qint64 bytesRead, qint64 /*totalBytes*/)
119{
120 if (m_maxsize && bytesRead>=m_maxsize)
121 {
122 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Reached specified max file size (%1 bytes)").arg(m_maxsize));
123 {
124 QMutexLocker replylock(&m_replylock);
125 *m_buffer += m_reply->read(m_maxsize);
126 }
127 m_maxsize=0;
128 Cancel();
129 }
130}
QNetworkAccessManager m_mgr
bool DownloadURL(const QUrl &url, QByteArray *buffer, std::chrono::seconds timeout=30s, uint redirs=0, qint64 maxsize=0, QString *final_url=nullptr)
QNetworkReply * m_reply
QNetworkReply::NetworkError m_errorcode
void Progress(qint64 bytesRead, qint64 totalBytes)
unsigned int uint
Definition: freesurround.h:24
@ quit
Definition: lirc_client.h:30
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC