]> git.sur5r.net Git - minitube/blob - lib/http/src/networkhttpreply.cpp
New upstream version 3.9.1
[minitube] / lib / http / src / networkhttpreply.cpp
1 #include "networkhttpreply.h"
2
3 NetworkHttpReply::NetworkHttpReply(const HttpRequest &req, Http &http)
4     : http(http), req(req), retryCount(0) {
5     if (req.url.isEmpty()) {
6         qWarning() << "Empty URL";
7     }
8
9     networkReply = http.networkReply(req);
10     setParent(networkReply);
11     setupReply();
12
13     readTimeoutTimer = new QTimer(this);
14     readTimeoutTimer->setInterval(http.getReadTimeout());
15     readTimeoutTimer->setSingleShot(true);
16     connect(readTimeoutTimer, SIGNAL(timeout()), SLOT(readTimeout()), Qt::UniqueConnection);
17     readTimeoutTimer->start();
18 }
19
20 void NetworkHttpReply::setupReply() {
21 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
22     connect(networkReply, &QNetworkReply::errorOccurred, this, &NetworkHttpReply::replyError,
23             Qt::UniqueConnection);
24 #else
25     connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
26             SLOT(replyError(QNetworkReply::NetworkError)), Qt::UniqueConnection);
27 #endif
28     connect(networkReply, SIGNAL(finished()), SLOT(replyFinished()), Qt::UniqueConnection);
29     connect(networkReply, SIGNAL(downloadProgress(qint64, qint64)),
30             SLOT(downloadProgress(qint64, qint64)), Qt::UniqueConnection);
31 }
32
33 QString NetworkHttpReply::errorMessage() {
34     return url().toString() + QLatin1Char(' ') + QString::number(statusCode()) + QLatin1Char(' ') +
35            reasonPhrase();
36 }
37
38 void NetworkHttpReply::emitError() {
39     const QString msg = errorMessage();
40 #ifndef QT_NO_DEBUG_OUTPUT
41     qDebug() << "Http:" << msg;
42     if (!req.body.isEmpty()) qDebug() << "Http:" << req.body;
43 #endif
44     emit error(msg);
45     emitFinished();
46 }
47
48 void NetworkHttpReply::emitFinished() {
49     readTimeoutTimer->stop();
50
51     // disconnect to avoid replyFinished() from being called
52     networkReply->disconnect();
53
54     emit finished(*this);
55
56     // bye bye my reply
57     // this will also delete this object and HttpReply as the QNetworkReply is their parent
58     networkReply->deleteLater();
59 }
60
61 void NetworkHttpReply::replyFinished() {
62 #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
63     QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
64     if (redirection.isValid()) {
65         HttpRequest redirectReq;
66         if (redirection.isRelative()) redirection = networkReply->url().resolved(redirection);
67         redirectReq.url = redirection;
68         qDebug() << "Redirected to" << redirectReq.url;
69         redirectReq.operation = req.operation;
70         redirectReq.body = req.body;
71         redirectReq.offset = req.offset;
72         QNetworkReply *redirectReply = http.networkReply(redirectReq);
73         setParent(redirectReply);
74         networkReply->deleteLater();
75         networkReply = redirectReply;
76         setupReply();
77         readTimeoutTimer->start();
78         return;
79     }
80 #endif
81
82     if (isSuccessful()) {
83         bytes = networkReply->readAll();
84         emit data(bytes);
85
86 #ifndef QT_NO_DEBUG_OUTPUT
87         if (!networkReply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool())
88             qDebug() << statusCode() << networkReply->url().toString();
89         else
90             qDebug() << "CACHE" << networkReply->url().toString();
91 #endif
92     }
93
94     emitFinished();
95 }
96
97 void NetworkHttpReply::replyError(QNetworkReply::NetworkError code) {
98     Q_UNUSED(code);
99     const int status = statusCode();
100     if (retryCount <= http.getMaxRetries() && status >= 500 && status < 600 &&
101         (networkReply->operation() == QNetworkAccessManager::GetOperation ||
102          networkReply->operation() == QNetworkAccessManager::HeadOperation)) {
103         qDebug() << "Retrying" << status << QVariant(req.operation).toString() << req.url;
104         networkReply->disconnect();
105         networkReply->deleteLater();
106         QNetworkReply *retryReply = http.networkReply(req);
107         setParent(retryReply);
108         networkReply = retryReply;
109         setupReply();
110         retryCount++;
111         readTimeoutTimer->start();
112     } else {
113         emitError();
114         return;
115     }
116 }
117
118 void NetworkHttpReply::downloadProgress(qint64 bytesReceived, qint64 /* bytesTotal */) {
119     // qDebug() << "Downloading" << bytesReceived << bytesTotal << networkReply->url();
120     if (bytesReceived > 0 && readTimeoutTimer->isActive()) {
121         readTimeoutTimer->stop();
122         disconnect(networkReply, SIGNAL(downloadProgress(qint64, qint64)), this,
123                    SLOT(downloadProgress(qint64, qint64)));
124     }
125 }
126
127 void NetworkHttpReply::readTimeout() {
128     qDebug() << "Timeout" << req.url;
129
130     if (!networkReply) return;
131
132     bool shouldRetry = (networkReply->operation() == QNetworkAccessManager::GetOperation ||
133                         networkReply->operation() == QNetworkAccessManager::HeadOperation) &&
134                        retryCount < http.getMaxRetries();
135
136     networkReply->disconnect();
137     networkReply->abort();
138     networkReply->deleteLater();
139
140     if (!shouldRetry) {
141         emitError();
142         emit finished(*this);
143         return;
144     }
145
146     retryCount++;
147     QNetworkReply *retryReply = http.networkReply(req);
148     setParent(retryReply);
149     networkReply = retryReply;
150     setupReply();
151     readTimeoutTimer->start();
152 }
153
154 QUrl NetworkHttpReply::url() const {
155     return networkReply->url();
156 }
157
158 int NetworkHttpReply::statusCode() const {
159     return networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
160 }
161
162 QString NetworkHttpReply::reasonPhrase() const {
163     return networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
164 }
165
166 const QList<QNetworkReply::RawHeaderPair> NetworkHttpReply::headers() const {
167     return networkReply->rawHeaderPairs();
168 }
169
170 QByteArray NetworkHttpReply::header(const QByteArray &headerName) const {
171     return networkReply->rawHeader(headerName);
172 }
173
174 QByteArray NetworkHttpReply::body() const {
175     return bytes;
176 }