5 QNetworkAccessManager *createNetworkAccessManager() {
6 QNetworkAccessManager *nam = new QNetworkAccessManager();
10 QNetworkAccessManager *networkAccessManager() {
11 static QMap<QThread *, QNetworkAccessManager *> nams;
12 QThread *t = QThread::currentThread();
13 QMap<QThread *, QNetworkAccessManager *>::const_iterator i = nams.constFind(t);
14 if (i != nams.constEnd()) return i.value();
15 QNetworkAccessManager *nam = createNetworkAccessManager();
20 static int defaultReadTimeout = 10000;
23 Http::Http() : requestHeaders(getDefaultRequestHeaders()), readTimeout(defaultReadTimeout) {}
25 void Http::setRequestHeaders(const QMap<QByteArray, QByteArray> &headers) {
26 requestHeaders = headers;
29 QMap<QByteArray, QByteArray> &Http::getRequestHeaders() {
30 return requestHeaders;
33 void Http::addRequestHeader(const QByteArray &name, const QByteArray &value) {
34 requestHeaders.insert(name, value);
37 void Http::setReadTimeout(int timeout) {
38 readTimeout = timeout;
41 Http &Http::instance() {
42 static Http *i = new Http();
46 const QMap<QByteArray, QByteArray> &Http::getDefaultRequestHeaders() {
47 static const QMap<QByteArray, QByteArray> defaultRequestHeaders = [] {
48 QMap<QByteArray, QByteArray> h;
49 h.insert("Accept-Charset", "utf-8");
50 h.insert("Connection", "Keep-Alive");
53 return defaultRequestHeaders;
56 void Http::setDefaultReadTimeout(int timeout) {
57 defaultReadTimeout = timeout;
60 QNetworkReply *Http::networkReply(const HttpRequest &req) {
61 QNetworkRequest request(req.url);
63 QMap<QByteArray, QByteArray> &headers = requestHeaders;
64 if (!req.headers.isEmpty()) headers = req.headers;
66 QMap<QByteArray, QByteArray>::const_iterator it;
67 for (it = headers.constBegin(); it != headers.constEnd(); ++it)
68 request.setRawHeader(it.key(), it.value());
71 request.setRawHeader("Range", QString("bytes=%1-").arg(req.offset).toUtf8());
73 QNetworkAccessManager *manager = networkAccessManager();
75 QNetworkReply *networkReply = 0;
76 switch (req.operation) {
77 case QNetworkAccessManager::GetOperation:
78 networkReply = manager->get(request);
81 case QNetworkAccessManager::HeadOperation:
82 networkReply = manager->head(request);
85 case QNetworkAccessManager::PostOperation:
86 networkReply = manager->post(request, req.body);
90 qWarning() << "Unknown operation:" << req.operation;
96 QObject *Http::request(const HttpRequest &req) {
97 return new NetworkHttpReply(req, *this);
100 QObject *Http::request(const QUrl &url,
101 QNetworkAccessManager::Operation operation,
102 const QByteArray &body,
106 req.operation = operation;
112 QObject *Http::get(const QUrl &url) {
113 return request(url, QNetworkAccessManager::GetOperation);
116 QObject *Http::head(const QUrl &url) {
117 return request(url, QNetworkAccessManager::HeadOperation);
120 QObject *Http::post(const QUrl &url, const QMap<QString, QString> ¶ms) {
122 QMapIterator<QString, QString> i(params);
123 while (i.hasNext()) {
125 body += QUrl::toPercentEncoding(i.key()) + '=' + QUrl::toPercentEncoding(i.value()) + '&';
129 req.operation = QNetworkAccessManager::PostOperation;
131 req.headers = requestHeaders;
132 req.headers.insert("Content-Type", "application/x-www-form-urlencoded");
136 QObject *Http::post(const QUrl &url, const QByteArray &body, const QByteArray &contentType) {
139 req.operation = QNetworkAccessManager::PostOperation;
141 req.headers = requestHeaders;
142 QByteArray cType = contentType;
143 if (cType.isEmpty()) cType = "application/x-www-form-urlencoded";
144 req.headers.insert("Content-Type", cType);
148 NetworkHttpReply::NetworkHttpReply(const HttpRequest &req, Http &http)
149 : http(http), req(req), retryCount(0) {
150 if (req.url.isEmpty()) {
151 qWarning() << "Empty URL";
154 networkReply = http.networkReply(req);
155 setParent(networkReply);
158 readTimeoutTimer = new QTimer(this);
159 readTimeoutTimer->setInterval(http.getReadTimeout());
160 readTimeoutTimer->setSingleShot(true);
161 connect(readTimeoutTimer, SIGNAL(timeout()), SLOT(readTimeout()), Qt::UniqueConnection);
162 readTimeoutTimer->start();
165 void NetworkHttpReply::setupReply() {
166 connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
167 SLOT(replyError(QNetworkReply::NetworkError)), Qt::UniqueConnection);
168 connect(networkReply, SIGNAL(finished()), SLOT(replyFinished()), Qt::UniqueConnection);
169 connect(networkReply, SIGNAL(downloadProgress(qint64, qint64)),
170 SLOT(downloadProgress(qint64, qint64)), Qt::UniqueConnection);
173 QString NetworkHttpReply::errorMessage() {
174 return url().toString() + QLatin1Char(' ') + QString::number(statusCode()) + QLatin1Char(' ') +
178 void NetworkHttpReply::emitError() {
179 const QString msg = errorMessage();
180 #ifndef QT_NO_DEBUG_OUTPUT
181 qDebug() << "Http:" << msg;
182 if (!req.body.isEmpty()) qDebug() << "Http:" << req.body;
188 void NetworkHttpReply::emitFinished() {
189 readTimeoutTimer->stop();
191 // disconnect to avoid replyFinished() from being called
192 networkReply->disconnect();
194 emit finished(*this);
197 // this will also delete this object and HttpReply as the QNetworkReply is their parent
198 networkReply->deleteLater();
201 void NetworkHttpReply::replyFinished() {
202 QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
203 if (redirection.isValid()) {
204 HttpRequest redirectReq;
205 redirectReq.url = redirection;
206 redirectReq.operation = req.operation;
207 redirectReq.body = req.body;
208 redirectReq.offset = req.offset;
209 QNetworkReply *redirectReply = http.networkReply(redirectReq);
210 setParent(redirectReply);
211 networkReply->deleteLater();
212 networkReply = redirectReply;
214 readTimeoutTimer->start();
218 if (isSuccessful()) {
219 bytes = networkReply->readAll();
222 #ifndef QT_NO_DEBUG_OUTPUT
223 if (!networkReply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool())
224 qDebug() << networkReply->url().toString() << statusCode();
226 qDebug() << "CACHE" << networkReply->url().toString();
233 void NetworkHttpReply::replyError(QNetworkReply::NetworkError code) {
235 const int status = statusCode();
236 if (retryCount <= 3 && status >= 500 && status < 600) {
237 qDebug() << "Retrying" << req.url;
238 networkReply->disconnect();
239 networkReply->deleteLater();
240 QNetworkReply *retryReply = http.networkReply(req);
241 setParent(retryReply);
242 networkReply = retryReply;
245 readTimeoutTimer->start();
252 void NetworkHttpReply::downloadProgress(qint64 bytesReceived, qint64 /* bytesTotal */) {
253 // qDebug() << "Downloading" << bytesReceived << bytesTotal << networkReply->url();
254 if (bytesReceived > 0 && readTimeoutTimer->isActive()) {
255 readTimeoutTimer->stop();
256 disconnect(networkReply, SIGNAL(downloadProgress(qint64, qint64)), this,
257 SLOT(downloadProgress(qint64, qint64)));
261 void NetworkHttpReply::readTimeout() {
262 if (!networkReply) return;
263 networkReply->disconnect();
264 networkReply->abort();
265 networkReply->deleteLater();
267 if (retryCount > 3 && (networkReply->operation() != QNetworkAccessManager::GetOperation &&
268 networkReply->operation() != QNetworkAccessManager::HeadOperation)) {
270 emit finished(*this);
274 qDebug() << "Timeout" << req.url;
275 QNetworkReply *retryReply = http.networkReply(req);
276 setParent(retryReply);
277 networkReply = retryReply;
280 readTimeoutTimer->start();
283 QUrl NetworkHttpReply::url() const {
284 return networkReply->url();
287 int NetworkHttpReply::statusCode() const {
288 return networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
291 QString NetworkHttpReply::reasonPhrase() const {
292 return networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
295 const QList<QNetworkReply::RawHeaderPair> NetworkHttpReply::headers() const {
296 return networkReply->rawHeaderPairs();
299 QByteArray NetworkHttpReply::header(const QByteArray &headerName) const {
300 return networkReply->rawHeader(headerName);
303 QByteArray NetworkHttpReply::body() const {