1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qnetworkreplyimpl_p.h"
43 #include "qnetworkaccessbackend_p.h"
44 #include "qnetworkcookie.h"
45 #include "qabstractnetworkcache.h"
46 #include "QtCore/qcoreapplication.h"
47 #include "QtCore/qdatetime.h"
48 #include "QtNetwork/qsslconfiguration.h"
49 #include "QtNetwork/qnetworksession.h"
50 #include "qnetworkaccesshttpbackend_p.h"
51 #include "qnetworkaccessmanager_p.h"
52
53 #include <QtCore/QCoreApplication>
54
Q_DECLARE_METATYPE(QSharedPointer<char>)55 Q_DECLARE_METATYPE(QSharedPointer<char>)
56
57 QT_BEGIN_NAMESPACE
58
59 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
60 : backend(0), outgoingData(0),
61 copyDevice(0),
62 cacheEnabled(false), cacheSaveDevice(0),
63 notificationHandlingPaused(false),
64 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
65 httpStatusCode(0),
66 state(Idle)
67 , downloadBufferReadPosition(0)
68 , downloadBufferCurrentSize(0)
69 , downloadBufferMaximumSize(0)
70 , downloadBuffer(0)
71 {
72 }
73
_q_startOperation()74 void QNetworkReplyImplPrivate::_q_startOperation()
75 {
76 // ensure this function is only being called once
77 if (state == Working || state == Finished) {
78 qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
79 return;
80 }
81 state = Working;
82
83 // note: if that method is called directly, it cannot happen that the backend is 0,
84 // because we just checked via a qobject_cast that we got a http backend (see
85 // QNetworkReplyImplPrivate::setup())
86 if (!backend) {
87 error(QNetworkReplyImpl::ProtocolUnknownError,
88 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
89 finished();
90 return;
91 }
92
93 if (!backend->start()) {
94 #ifndef QT_NO_BEARERMANAGEMENT
95 // backend failed to start because the session state is not Connected.
96 // QNetworkAccessManager will call _q_startOperation again for us when the session
97 // state changes.
98 state = WaitingForSession;
99
100 QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
101
102 if (session) {
103 Q_Q(QNetworkReplyImpl);
104
105 QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
106 q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);
107
108 if (!session->isOpen())
109 session->open();
110 } else {
111 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
112 state = Working;
113 error(QNetworkReplyImpl::UnknownNetworkError,
114 QCoreApplication::translate("QNetworkReply", "Network session error."));
115 finished();
116 }
117 #else
118 qWarning("Backend start failed");
119 state = Working;
120 error(QNetworkReplyImpl::UnknownNetworkError,
121 QCoreApplication::translate("QNetworkReply", "backend start error."));
122 finished();
123 #endif
124 return;
125 }
126
127 if (backend && backend->isSynchronous()) {
128 state = Finished;
129 q_func()->setFinished(true);
130 } else {
131 if (state != Finished) {
132 if (operation == QNetworkAccessManager::GetOperation)
133 pendingNotifications.append(NotifyDownstreamReadyWrite);
134
135 handleNotifications();
136 }
137 }
138 }
139
_q_copyReadyRead()140 void QNetworkReplyImplPrivate::_q_copyReadyRead()
141 {
142 Q_Q(QNetworkReplyImpl);
143 if (state != Working)
144 return;
145 if (!copyDevice || !q->isOpen())
146 return;
147
148 // FIXME Optimize to use download buffer if it is a QBuffer.
149 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
150 // metaDataChanged ?
151
152 forever {
153 qint64 bytesToRead = nextDownstreamBlockSize();
154 if (bytesToRead == 0)
155 // we'll be called again, eventually
156 break;
157
158 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
159 QByteArray byteData;
160 byteData.resize(bytesToRead);
161 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
162 if (bytesActuallyRead == -1) {
163 byteData.clear();
164 backendNotify(NotifyCopyFinished);
165 break;
166 }
167
168 byteData.resize(bytesActuallyRead);
169 readBuffer.append(byteData);
170
171 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
172 backendNotify(NotifyCopyFinished);
173 bytesDownloaded += bytesActuallyRead;
174 break;
175 }
176
177 bytesDownloaded += bytesActuallyRead;
178 }
179
180 if (bytesDownloaded == lastBytesDownloaded) {
181 // we didn't read anything
182 return;
183 }
184
185 lastBytesDownloaded = bytesDownloaded;
186 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
187 if (preMigrationDownloaded != Q_INT64_C(-1))
188 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
189 pauseNotificationHandling();
190 // emit readyRead before downloadProgress incase this will cause events to be
191 // processed and we get into a recursive call (as in QProgressDialog).
192 emit q->readyRead();
193 emit q->downloadProgress(bytesDownloaded,
194 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
195 resumeNotificationHandling();
196 }
197
_q_copyReadChannelFinished()198 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
199 {
200 _q_copyReadyRead();
201 }
202
_q_bufferOutgoingDataFinished()203 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
204 {
205 Q_Q(QNetworkReplyImpl);
206
207 // make sure this is only called once, ever.
208 //_q_bufferOutgoingData may call it or the readChannelFinished emission
209 if (state != Buffering)
210 return;
211
212 // disconnect signals
213 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
214 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
215
216 // finally, start the request
217 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
218 }
219
_q_bufferOutgoingData()220 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
221 {
222 Q_Q(QNetworkReplyImpl);
223
224 if (!outgoingDataBuffer) {
225 // first call, create our buffer
226 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
227
228 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
229 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
230 }
231
232 qint64 bytesBuffered = 0;
233 qint64 bytesToBuffer = 0;
234
235 // read data into our buffer
236 forever {
237 bytesToBuffer = outgoingData->bytesAvailable();
238 // unknown? just try 2 kB, this also ensures we always try to read the EOF
239 if (bytesToBuffer <= 0)
240 bytesToBuffer = 2*1024;
241
242 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
243 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
244
245 if (bytesBuffered == -1) {
246 // EOF has been reached.
247 outgoingDataBuffer->chop(bytesToBuffer);
248
249 _q_bufferOutgoingDataFinished();
250 break;
251 } else if (bytesBuffered == 0) {
252 // nothing read right now, just wait until we get called again
253 outgoingDataBuffer->chop(bytesToBuffer);
254
255 break;
256 } else {
257 // don't break, try to read() again
258 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
259 }
260 }
261 }
262
263 #ifndef QT_NO_BEARERMANAGEMENT
_q_networkSessionConnected()264 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
265 {
266 Q_Q(QNetworkReplyImpl);
267
268 if (manager.isNull())
269 return;
270
271 QSharedPointer<QNetworkSession> session = manager->d_func()->getNetworkSession();
272 if (!session)
273 return;
274
275 if (session->state() != QNetworkSession::Connected)
276 return;
277
278 #ifndef QT_NO_NETWORKPROXY
279 // Re-set proxies here as new session might have changed them
280 proxyList = manager->d_func()->queryProxy(QNetworkProxyQuery(request.url()));
281 #endif
282
283 switch (state) {
284 case QNetworkReplyImplPrivate::Buffering:
285 case QNetworkReplyImplPrivate::Working:
286 case QNetworkReplyImplPrivate::Reconnecting:
287 // Migrate existing downloads to new network connection.
288 migrateBackend();
289 break;
290 case QNetworkReplyImplPrivate::WaitingForSession:
291 // Start waiting requests.
292 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
293 break;
294 default:
295 ;
296 }
297 }
298
_q_networkSessionFailed()299 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
300 {
301 // Abort waiting and working replies.
302 if (state == WaitingForSession || state == Working) {
303 state = Working;
304 error(QNetworkReplyImpl::UnknownNetworkError,
305 QCoreApplication::translate("QNetworkReply", "Network session error."));
306 finished();
307 }
308 }
309 #endif
310
setup(QNetworkAccessManager::Operation op,const QNetworkRequest & req,QIODevice * data)311 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
312 QIODevice *data)
313 {
314 Q_Q(QNetworkReplyImpl);
315
316 outgoingData = data;
317 request = req;
318 url = request.url();
319 operation = op;
320
321 q->QIODevice::open(QIODevice::ReadOnly);
322 // Internal code that does a HTTP reply for the synchronous Ajax
323 // in QtWebKit.
324 QVariant synchronousHttpAttribute = req.attribute(
325 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
326 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
327 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
328 if (synchronousHttpAttribute.toBool() && outgoingData) {
329 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
330 qint64 previousDataSize = 0;
331 do {
332 previousDataSize = outgoingDataBuffer->size();
333 outgoingDataBuffer->append(outgoingData->readAll());
334 } while (outgoingDataBuffer->size() != previousDataSize);
335 }
336
337 if (backend)
338 backend->setSynchronous(synchronousHttpAttribute.toBool());
339
340
341 if (outgoingData && backend && !backend->isSynchronous()) {
342 // there is data to be uploaded, e.g. HTTP POST.
343
344 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
345 // backend does not need upload buffering or
346 // fixed size non-sequential
347 // just start the operation
348 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
349 } else {
350 bool bufferingDisallowed =
351 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
352 false).toBool();
353
354 if (bufferingDisallowed) {
355 // if a valid content-length header for the request was supplied, we can disable buffering
356 // if not, we will buffer anyway
357 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
358 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
359 } else {
360 state = Buffering;
361 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
362 }
363 } else {
364 // _q_startOperation will be called when the buffering has finished.
365 state = Buffering;
366 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
367 }
368 }
369 } else {
370 // for HTTP, we want to send out the request as fast as possible to the network, without
371 // invoking methods in a QueuedConnection
372 #ifndef QT_NO_HTTP
373 if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
374 _q_startOperation();
375 } else {
376 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
377 }
378 #else
379 if (backend && backend->isSynchronous())
380 _q_startOperation();
381 else
382 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
383 #endif // QT_NO_HTTP
384 }
385 }
386
backendNotify(InternalNotifications notification)387 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
388 {
389 Q_Q(QNetworkReplyImpl);
390 if (!pendingNotifications.contains(notification))
391 pendingNotifications.enqueue(notification);
392
393 if (pendingNotifications.size() == 1)
394 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
395 }
396
handleNotifications()397 void QNetworkReplyImplPrivate::handleNotifications()
398 {
399 if (notificationHandlingPaused)
400 return;
401
402 NotificationQueue current = pendingNotifications;
403 pendingNotifications.clear();
404
405 if (state != Working)
406 return;
407
408 while (state == Working && !current.isEmpty()) {
409 InternalNotifications notification = current.dequeue();
410 switch (notification) {
411 case NotifyDownstreamReadyWrite:
412 if (copyDevice)
413 _q_copyReadyRead();
414 else
415 backend->downstreamReadyWrite();
416 break;
417
418 case NotifyCloseDownstreamChannel:
419 backend->closeDownstreamChannel();
420 break;
421
422 case NotifyCopyFinished: {
423 QIODevice *dev = copyDevice;
424 copyDevice = 0;
425 backend->copyFinished(dev);
426 break;
427 }
428 }
429 }
430 }
431
432 // Do not handle the notifications while we are emitting downloadProgress
433 // or readyRead
pauseNotificationHandling()434 void QNetworkReplyImplPrivate::pauseNotificationHandling()
435 {
436 notificationHandlingPaused = true;
437 }
438
439 // Resume notification handling
resumeNotificationHandling()440 void QNetworkReplyImplPrivate::resumeNotificationHandling()
441 {
442 Q_Q(QNetworkReplyImpl);
443 notificationHandlingPaused = false;
444 if (pendingNotifications.size() >= 1)
445 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
446 }
447
networkCache() const448 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
449 {
450 if (!backend)
451 return 0;
452 return backend->networkCache();
453 }
454
createCache()455 void QNetworkReplyImplPrivate::createCache()
456 {
457 // check if we can save and if we're allowed to
458 if (!networkCache()
459 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
460 || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
461 QNetworkRequest::PreferNetwork).toInt()
462 == QNetworkRequest::AlwaysNetwork)
463 return;
464 cacheEnabled = true;
465 }
466
isCachingEnabled() const467 bool QNetworkReplyImplPrivate::isCachingEnabled() const
468 {
469 return (cacheEnabled && networkCache() != 0);
470 }
471
setCachingEnabled(bool enable)472 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
473 {
474 Q_Q(QNetworkReplyImpl);
475 if (!enable && !cacheEnabled)
476 return; // nothing to do
477 if (enable && cacheEnabled)
478 return; // nothing to do either!
479
480 if (enable) {
481 if (bytesDownloaded) {
482 // refuse to enable in this case
483 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
484 return;
485 }
486
487 createCache();
488 } else {
489 // someone told us to turn on, then back off?
490 // ok... but you should make up your mind
491 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
492 "backend %s probably needs to be fixed",
493 backend->metaObject()->className());
494 networkCache()->remove(url);
495 cacheSaveDevice = 0;
496 cacheEnabled = false;
497 QObject::disconnect(networkCache(), SIGNAL(destroyed()), q, SLOT(_q_cacheDestroyed()));
498 }
499 }
500
_q_cacheDestroyed()501 void QNetworkReplyImplPrivate::_q_cacheDestroyed()
502 {
503 //destruction of cache invalidates cacheSaveDevice
504 cacheSaveDevice = 0;
505 cacheEnabled = false;
506 }
507
_q_cacheSaveDeviceAboutToClose()508 void QNetworkReplyImplPrivate::_q_cacheSaveDeviceAboutToClose()
509 {
510 // do not keep a dangling pointer to the device around (device
511 // is closing because e.g. QAbstractNetworkCache::remove() was called).
512 cacheSaveDevice = 0;
513 }
514
completeCacheSave()515 void QNetworkReplyImplPrivate::completeCacheSave()
516 {
517 Q_Q(QNetworkReplyImpl);
518 if (cacheEnabled) {
519 if (errorCode != QNetworkReplyImpl::NoError) {
520 networkCache()->remove(url);
521 } else if (cacheSaveDevice) {
522 networkCache()->insert(cacheSaveDevice);
523 }
524 QObject::disconnect(networkCache(), SIGNAL(destroyed()), q, SLOT(_q_cacheDestroyed()));
525 }
526 cacheSaveDevice = 0;
527 cacheEnabled = false;
528 }
529
emitUploadProgress(qint64 bytesSent,qint64 bytesTotal)530 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
531 {
532 Q_Q(QNetworkReplyImpl);
533 bytesUploaded = bytesSent;
534 pauseNotificationHandling();
535 emit q->uploadProgress(bytesSent, bytesTotal);
536 resumeNotificationHandling();
537 }
538
539
nextDownstreamBlockSize() const540 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
541 {
542 enum { DesiredBufferSize = 32 * 1024 };
543 if (readBufferMaxSize == 0)
544 return DesiredBufferSize;
545
546 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
547 }
548
initCacheSaveDevice()549 void QNetworkReplyImplPrivate::initCacheSaveDevice()
550 {
551 Q_Q(QNetworkReplyImpl);
552
553 // The disk cache does not support partial content, so don't even try to
554 // save any such content into the cache.
555 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
556 cacheEnabled = false;
557 return;
558 }
559
560 // save the meta data
561 QNetworkCacheMetaData metaData;
562 metaData.setUrl(url);
563 metaData = backend->fetchCacheMetaData(metaData);
564
565 // save the redirect request also in the cache
566 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
567 if (redirectionTarget.isValid()) {
568 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
569 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
570 metaData.setAttributes(attributes);
571 }
572
573 cacheSaveDevice = networkCache()->prepare(metaData);
574
575 if (cacheSaveDevice)
576 q->connect(cacheSaveDevice, SIGNAL(aboutToClose()), SLOT(_q_cacheSaveDeviceAboutToClose()));
577
578 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
579 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
580 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
581 "class %s probably needs to be fixed",
582 networkCache()->metaObject()->className());
583
584 networkCache()->remove(url);
585 cacheSaveDevice = 0;
586 cacheEnabled = false;
587 } else {
588 q->connect(networkCache(), SIGNAL(destroyed()), SLOT(_q_cacheDestroyed()));
589 }
590 }
591
592 // we received downstream data and send this to the cache
593 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
appendDownstreamData(QByteDataBuffer & data)594 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
595 {
596 Q_Q(QNetworkReplyImpl);
597 if (!q->isOpen())
598 return;
599
600 if (cacheEnabled && !cacheSaveDevice) {
601 initCacheSaveDevice();
602 }
603
604 qint64 bytesWritten = 0;
605 for (int i = 0; i < data.bufferCount(); i++) {
606 QByteArray const &item = data[i];
607
608 if (cacheSaveDevice)
609 cacheSaveDevice->write(item.constData(), item.size());
610 readBuffer.append(item);
611
612 bytesWritten += item.size();
613 }
614 data.clear();
615
616 bytesDownloaded += bytesWritten;
617 lastBytesDownloaded = bytesDownloaded;
618
619 appendDownstreamDataSignalEmissions();
620 }
621
appendDownstreamDataSignalEmissions()622 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
623 {
624 Q_Q(QNetworkReplyImpl);
625
626 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
627 if (preMigrationDownloaded != Q_INT64_C(-1))
628 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
629 pauseNotificationHandling();
630 // important: At the point of this readyRead(), the data parameter list must be empty,
631 // else implicit sharing will trigger memcpy when the user is reading data!
632 emit q->readyRead();
633 // emit readyRead before downloadProgress incase this will cause events to be
634 // processed and we get into a recursive call (as in QProgressDialog).
635 emit q->downloadProgress(bytesDownloaded,
636 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
637
638 resumeNotificationHandling();
639 // do we still have room in the buffer?
640 if (nextDownstreamBlockSize() > 0)
641 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
642 }
643
644 // this is used when it was fetched from the cache, right?
appendDownstreamData(QIODevice * data)645 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
646 {
647 Q_Q(QNetworkReplyImpl);
648 if (!q->isOpen())
649 return;
650
651 // read until EOF from data
652 if (copyDevice) {
653 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
654 "backend probly needs to be fixed");
655 return;
656 }
657
658 copyDevice = data;
659 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
660 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
661
662 // start the copy:
663 _q_copyReadyRead();
664 }
665
appendDownstreamData(const QByteArray & data)666 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
667 {
668 Q_UNUSED(data)
669 // TODO implement
670
671 // TODO call
672
673 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
674 }
675
downloadBufferDeleter(char * ptr)676 static void downloadBufferDeleter(char *ptr)
677 {
678 delete[] ptr;
679 }
680
getDownloadBuffer(qint64 size)681 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
682 {
683 Q_Q(QNetworkReplyImpl);
684
685 if (!downloadBuffer) {
686 // We are requested to create it
687 // Check attribute() if allocating a buffer of that size can be allowed
688 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
689 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
690 downloadBufferCurrentSize = 0;
691 downloadBufferMaximumSize = size;
692 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
693 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
694
695 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
696 }
697 }
698
699 return downloadBuffer;
700 }
701
setDownloadBuffer(QSharedPointer<char> sp,qint64 size)702 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
703 {
704 Q_Q(QNetworkReplyImpl);
705
706 downloadBufferPointer = sp;
707 downloadBuffer = downloadBufferPointer.data();
708 downloadBufferCurrentSize = 0;
709 downloadBufferMaximumSize = size;
710 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
711 }
712
713
appendDownstreamDataDownloadBuffer(qint64 bytesReceived,qint64 bytesTotal)714 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
715 {
716 Q_Q(QNetworkReplyImpl);
717 if (!q->isOpen())
718 return;
719
720 if (cacheEnabled && !cacheSaveDevice)
721 initCacheSaveDevice();
722
723 if (cacheSaveDevice && bytesReceived == bytesTotal) {
724 // if (lastBytesDownloaded == -1)
725 // lastBytesDownloaded = 0;
726 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
727
728 // Write everything in one go if we use a download buffer. might be more performant.
729 cacheSaveDevice->write(downloadBuffer, bytesTotal);
730 }
731
732 bytesDownloaded = bytesReceived;
733 lastBytesDownloaded = bytesReceived;
734
735 downloadBufferCurrentSize = bytesReceived;
736
737 // Only emit readyRead when actual data is there
738 // emit readyRead before downloadProgress incase this will cause events to be
739 // processed and we get into a recursive call (as in QProgressDialog).
740 if (bytesDownloaded > 0)
741 emit q->readyRead();
742 emit q->downloadProgress(bytesDownloaded, bytesTotal);
743 }
744
finished()745 void QNetworkReplyImplPrivate::finished()
746 {
747 Q_Q(QNetworkReplyImpl);
748
749 if (state == Finished || state == Aborted || state == WaitingForSession)
750 return;
751
752 pauseNotificationHandling();
753 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
754 if (preMigrationDownloaded != Q_INT64_C(-1))
755 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
756
757 if (!manager.isNull()) {
758 #ifndef QT_NO_BEARERMANAGEMENT
759 QSharedPointer<QNetworkSession> session (manager->d_func()->getNetworkSession());
760 if (session && session->state() == QNetworkSession::Roaming &&
761 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
762 // only content with a known size will fail with a temporary network failure error
763 if (!totalSize.isNull()) {
764 if (bytesDownloaded != totalSize) {
765 if (migrateBackend()) {
766 // either we are migrating or the request is finished/aborted
767 if (state == Reconnecting || state == WaitingForSession) {
768 resumeNotificationHandling();
769 return; // exit early if we are migrating.
770 }
771 } else {
772 error(QNetworkReply::TemporaryNetworkFailureError,
773 QNetworkReply::tr("Temporary network failure."));
774 }
775 }
776 }
777 }
778 #endif
779 }
780 resumeNotificationHandling();
781
782 state = Finished;
783 q->setFinished(true);
784
785 pendingNotifications.clear();
786
787 pauseNotificationHandling();
788 if (totalSize.isNull() || totalSize == -1) {
789 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
790 }
791
792 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
793 emit q->uploadProgress(0, 0);
794 resumeNotificationHandling();
795
796 // if we don't know the total size of or we received everything save the cache
797 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
798 completeCacheSave();
799
800 // note: might not be a good idea, since users could decide to delete us
801 // which would delete the backend too...
802 // maybe we should protect the backend
803 pauseNotificationHandling();
804 emit q->readChannelFinished();
805 emit q->finished();
806 resumeNotificationHandling();
807 }
808
error(QNetworkReplyImpl::NetworkError code,const QString & errorMessage)809 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
810 {
811 Q_Q(QNetworkReplyImpl);
812 // Can't set and emit multiple errors.
813 if (errorCode != QNetworkReply::NoError) {
814 qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
815 return;
816 }
817
818 errorCode = code;
819 q->setErrorString(errorMessage);
820
821 // note: might not be a good idea, since users could decide to delete us
822 // which would delete the backend too...
823 // maybe we should protect the backend
824 emit q->error(code);
825 }
826
metaDataChanged()827 void QNetworkReplyImplPrivate::metaDataChanged()
828 {
829 Q_Q(QNetworkReplyImpl);
830 // 1. do we have cookies?
831 // 2. are we allowed to set them?
832 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
833 && (static_cast<QNetworkRequest::LoadControl>
834 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
835 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
836 QList<QNetworkCookie> cookies =
837 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
838 QNetworkCookieJar *jar = manager->cookieJar();
839 if (jar)
840 jar->setCookiesFromUrl(cookies, url);
841 }
842 emit q->metaDataChanged();
843 }
844
redirectionRequested(const QUrl & target)845 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
846 {
847 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
848 }
849
sslErrors(const QList<QSslError> & errors)850 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
851 {
852 #ifndef QT_NO_OPENSSL
853 Q_Q(QNetworkReplyImpl);
854 emit q->sslErrors(errors);
855 #else
856 Q_UNUSED(errors);
857 #endif
858 }
859
QNetworkReplyImpl(QObject * parent)860 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
861 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
862 {
863 }
864
~QNetworkReplyImpl()865 QNetworkReplyImpl::~QNetworkReplyImpl()
866 {
867 Q_D(QNetworkReplyImpl);
868
869 // This code removes the data from the cache if it was prematurely aborted.
870 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
871 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
872 if (d->isCachingEnabled())
873 d->networkCache()->remove(url());
874 }
875
abort()876 void QNetworkReplyImpl::abort()
877 {
878 Q_D(QNetworkReplyImpl);
879 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
880 return;
881
882 // stop both upload and download
883 if (d->outgoingData)
884 disconnect(d->outgoingData, 0, this, 0);
885 if (d->copyDevice)
886 disconnect(d->copyDevice, 0, this, 0);
887
888 QNetworkReply::close();
889
890 if (d->state != QNetworkReplyImplPrivate::Finished) {
891 // call finished which will emit signals
892 d->error(OperationCanceledError, tr("Operation canceled"));
893 if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
894 d->state = QNetworkReplyImplPrivate::Working;
895 d->finished();
896 }
897 d->state = QNetworkReplyImplPrivate::Aborted;
898
899 // finished may access the backend
900 if (d->backend) {
901 d->backend->deleteLater();
902 d->backend = 0;
903 }
904 }
905
close()906 void QNetworkReplyImpl::close()
907 {
908 Q_D(QNetworkReplyImpl);
909 if (d->state == QNetworkReplyImplPrivate::Aborted ||
910 d->state == QNetworkReplyImplPrivate::Finished)
911 return;
912
913 // stop the download
914 if (d->backend)
915 d->backend->closeDownstreamChannel();
916 if (d->copyDevice)
917 disconnect(d->copyDevice, 0, this, 0);
918
919 QNetworkReply::close();
920
921 // call finished which will emit signals
922 d->error(OperationCanceledError, tr("Operation canceled"));
923 d->finished();
924 }
925
canReadLine() const926 bool QNetworkReplyImpl::canReadLine () const
927 {
928 Q_D(const QNetworkReplyImpl);
929 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
930 }
931
932
933 /*!
934 Returns the number of bytes available for reading with
935 QIODevice::read(). The number of bytes available may grow until
936 the finished() signal is emitted.
937 */
bytesAvailable() const938 qint64 QNetworkReplyImpl::bytesAvailable() const
939 {
940 // Special case for the "zero copy" download buffer
941 Q_D(const QNetworkReplyImpl);
942 if (d->downloadBuffer) {
943 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
944 return QNetworkReply::bytesAvailable() + maxAvail;
945 }
946
947 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
948 }
949
setReadBufferSize(qint64 size)950 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
951 {
952 Q_D(QNetworkReplyImpl);
953 if (size > d->readBufferMaxSize &&
954 size > d->readBuffer.byteAmount())
955 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
956
957 QNetworkReply::setReadBufferSize(size);
958
959 if (d->backend) {
960 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
961 d->backend->setReadBufferSize(size);
962 }
963 }
964
965 #ifndef QT_NO_OPENSSL
sslConfigurationImplementation() const966 QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
967 {
968 Q_D(const QNetworkReplyImpl);
969 QSslConfiguration config;
970 if (d->backend)
971 d->backend->fetchSslConfiguration(config);
972 return config;
973 }
974
setSslConfigurationImplementation(const QSslConfiguration & config)975 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
976 {
977 Q_D(QNetworkReplyImpl);
978 if (d->backend && !config.isNull())
979 d->backend->setSslConfiguration(config);
980 }
981
ignoreSslErrors()982 void QNetworkReplyImpl::ignoreSslErrors()
983 {
984 Q_D(QNetworkReplyImpl);
985 if (d->backend)
986 d->backend->ignoreSslErrors();
987 }
988
ignoreSslErrorsImplementation(const QList<QSslError> & errors)989 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
990 {
991 Q_D(QNetworkReplyImpl);
992 if (d->backend)
993 d->backend->ignoreSslErrors(errors);
994 }
995 #endif // QT_NO_OPENSSL
996
997 /*!
998 \internal
999 */
readData(char * data,qint64 maxlen)1000 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
1001 {
1002 Q_D(QNetworkReplyImpl);
1003
1004 // Special case code if we have the "zero copy" download buffer
1005 if (d->downloadBuffer) {
1006 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
1007 if (maxAvail == 0)
1008 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1009 // FIXME what about "Aborted" state?
1010 qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
1011 d->downloadBufferReadPosition += maxAvail;
1012 return maxAvail;
1013 }
1014
1015
1016 if (d->readBuffer.isEmpty())
1017 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1018 // FIXME what about "Aborted" state?
1019
1020 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
1021 if (maxlen == 1) {
1022 // optimization for getChar()
1023 *data = d->readBuffer.getChar();
1024 if (d->backend && readBufferSize())
1025 d->backend->emitReadBufferFreed(1);
1026 return 1;
1027 }
1028
1029 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1030 qint64 bytesRead = d->readBuffer.read(data, maxlen);
1031 if (d->backend && readBufferSize())
1032 d->backend->emitReadBufferFreed(bytesRead);
1033 return bytesRead;
1034 }
1035
1036 /*!
1037 \internal Reimplemented for internal purposes
1038 */
event(QEvent * e)1039 bool QNetworkReplyImpl::event(QEvent *e)
1040 {
1041 if (e->type() == QEvent::NetworkReplyUpdated) {
1042 d_func()->handleNotifications();
1043 return true;
1044 }
1045
1046 return QObject::event(e);
1047 }
1048
1049 /*
1050 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1051 true if the reply is migrated or it is not required; otherwise returns false.
1052 */
migrateBackend()1053 bool QNetworkReplyImplPrivate::migrateBackend()
1054 {
1055 Q_Q(QNetworkReplyImpl);
1056
1057 // Network reply is already finished or aborted, don't need to migrate.
1058 if (state == Finished || state == Aborted)
1059 return true;
1060
1061 // Request has outgoing data, not migrating.
1062 if (outgoingData)
1063 return false;
1064
1065 // Request is serviced from the cache, don't need to migrate.
1066 if (copyDevice)
1067 return true;
1068
1069 // Backend does not support resuming download.
1070 if (backend && !backend->canResume())
1071 return false;
1072
1073 state = QNetworkReplyImplPrivate::Reconnecting;
1074
1075 if (backend) {
1076 delete backend;
1077 backend = 0;
1078 }
1079
1080 cookedHeaders.clear();
1081 rawHeaders.clear();
1082
1083 preMigrationDownloaded = bytesDownloaded;
1084
1085 backend = manager->d_func()->findBackend(operation, request);
1086
1087 if (backend) {
1088 backend->setParent(q);
1089 backend->reply = this;
1090 backend->setResumeOffset(bytesDownloaded);
1091 }
1092
1093 #ifndef QT_NO_HTTP
1094 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
1095 _q_startOperation();
1096 } else {
1097 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1098 }
1099 #else
1100 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1101 #endif // QT_NO_HTTP
1102
1103 return true;
1104 }
1105
1106 #ifndef QT_NO_BEARERMANAGEMENT
QDisabledNetworkReply(QObject * parent,const QNetworkRequest & req,QNetworkAccessManager::Operation op)1107 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1108 const QNetworkRequest &req,
1109 QNetworkAccessManager::Operation op)
1110 : QNetworkReply(parent)
1111 {
1112 setRequest(req);
1113 setUrl(req.url());
1114 setOperation(op);
1115
1116 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1117
1118 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1119 "Network access is disabled.");
1120 setError(UnknownNetworkError, msg);
1121
1122 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1123 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1124 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1125 }
1126
~QDisabledNetworkReply()1127 QDisabledNetworkReply::~QDisabledNetworkReply()
1128 {
1129 }
1130 #endif
1131
1132 QT_END_NAMESPACE
1133
1134 #include "moc_qnetworkreplyimpl_p.cpp"
1135
1136