44 #include "qnetworkcookie.h" |
44 #include "qnetworkcookie.h" |
45 #include "qabstractnetworkcache.h" |
45 #include "qabstractnetworkcache.h" |
46 #include "QtCore/qcoreapplication.h" |
46 #include "QtCore/qcoreapplication.h" |
47 #include "QtCore/qdatetime.h" |
47 #include "QtCore/qdatetime.h" |
48 #include "QtNetwork/qsslconfiguration.h" |
48 #include "QtNetwork/qsslconfiguration.h" |
|
49 #include "QtNetwork/qnetworksession.h" |
49 #include "qnetworkaccesshttpbackend_p.h" |
50 #include "qnetworkaccesshttpbackend_p.h" |
|
51 #include "qnetworkaccessmanager_p.h" |
50 |
52 |
51 #include <QtCore/QCoreApplication> |
53 #include <QtCore/QCoreApplication> |
52 |
54 |
53 QT_BEGIN_NAMESPACE |
55 QT_BEGIN_NAMESPACE |
54 |
56 |
55 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() |
57 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() |
56 : backend(0), outgoingData(0), outgoingDataBuffer(0), |
58 : backend(0), outgoingData(0), outgoingDataBuffer(0), |
57 copyDevice(0), |
59 copyDevice(0), |
58 cacheEnabled(false), cacheSaveDevice(0), |
60 cacheEnabled(false), cacheSaveDevice(0), |
59 notificationHandlingPaused(false), |
61 notificationHandlingPaused(false), |
60 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), |
62 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1), |
61 httpStatusCode(0), |
63 httpStatusCode(0), |
62 state(Idle) |
64 state(Idle) |
63 { |
65 { |
64 } |
66 } |
65 |
67 |
80 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!; |
82 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!; |
81 finished(); |
83 finished(); |
82 return; |
84 return; |
83 } |
85 } |
84 |
86 |
85 backend->open(); |
87 #ifndef QT_NO_BEARERMANAGEMENT |
|
88 if (!backend->start()) { |
|
89 // backend failed to start because the session state is not Connected. |
|
90 // QNetworkAccessManager will call reply->backend->start() again for us when the session |
|
91 // state changes. |
|
92 state = WaitingForSession; |
|
93 |
|
94 QNetworkSession *session = manager->d_func()->networkSession; |
|
95 |
|
96 if (session) { |
|
97 Q_Q(QNetworkReplyImpl); |
|
98 |
|
99 QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)), |
|
100 q, SLOT(_q_networkSessionFailed())); |
|
101 |
|
102 if (!session->isOpen()) |
|
103 session->open(); |
|
104 } else { |
|
105 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!"); |
|
106 } |
|
107 |
|
108 return; |
|
109 } |
|
110 #endif |
|
111 |
86 if (state != Finished) { |
112 if (state != Finished) { |
87 if (operation == QNetworkAccessManager::GetOperation) |
113 if (operation == QNetworkAccessManager::GetOperation) |
88 pendingNotifications.append(NotifyDownstreamReadyWrite); |
114 pendingNotifications.append(NotifyDownstreamReadyWrite); |
89 |
115 |
90 handleNotifications(); |
116 handleNotifications(); |
132 return; |
158 return; |
133 } |
159 } |
134 |
160 |
135 lastBytesDownloaded = bytesDownloaded; |
161 lastBytesDownloaded = bytesDownloaded; |
136 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
162 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
|
163 if (preMigrationDownloaded != Q_INT64_C(-1)) |
|
164 totalSize = totalSize.toLongLong() + preMigrationDownloaded; |
137 pauseNotificationHandling(); |
165 pauseNotificationHandling(); |
138 emit q->downloadProgress(bytesDownloaded, |
166 emit q->downloadProgress(bytesDownloaded, |
139 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); |
167 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); |
140 emit q->readyRead(); |
168 emit q->readyRead(); |
141 resumeNotificationHandling(); |
169 resumeNotificationHandling(); |
203 // don't break, try to read() again |
231 // don't break, try to read() again |
204 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered); |
232 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered); |
205 } |
233 } |
206 } |
234 } |
207 } |
235 } |
|
236 |
|
237 #ifndef QT_NO_BEARERMANAGEMENT |
|
238 void QNetworkReplyImplPrivate::_q_networkSessionConnected() |
|
239 { |
|
240 Q_Q(QNetworkReplyImpl); |
|
241 |
|
242 if (manager.isNull()) |
|
243 return; |
|
244 |
|
245 QNetworkSession *session = manager->d_func()->networkSession; |
|
246 if (!session) |
|
247 return; |
|
248 |
|
249 if (session->state() != QNetworkSession::Connected) |
|
250 return; |
|
251 |
|
252 switch (state) { |
|
253 case QNetworkReplyImplPrivate::Buffering: |
|
254 case QNetworkReplyImplPrivate::Working: |
|
255 case QNetworkReplyImplPrivate::Reconnecting: |
|
256 // Migrate existing downloads to new network connection. |
|
257 migrateBackend(); |
|
258 break; |
|
259 case QNetworkReplyImplPrivate::WaitingForSession: |
|
260 // Start waiting requests. |
|
261 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
|
262 break; |
|
263 default: |
|
264 ; |
|
265 } |
|
266 } |
|
267 |
|
268 void QNetworkReplyImplPrivate::_q_networkSessionFailed() |
|
269 { |
|
270 // Abort waiting replies. |
|
271 if (state == WaitingForSession) { |
|
272 state = Working; |
|
273 error(QNetworkReplyImpl::UnknownNetworkError, |
|
274 QCoreApplication::translate("QNetworkReply", "Network session error.")); |
|
275 finished(); |
|
276 } |
|
277 } |
|
278 #endif |
208 |
279 |
209 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, |
280 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, |
210 QIODevice *data) |
281 QIODevice *data) |
211 { |
282 { |
212 Q_Q(QNetworkReplyImpl); |
283 Q_Q(QNetworkReplyImpl); |
249 // or no backend |
320 // or no backend |
250 // if no backend, _q_startOperation will handle the error of this |
321 // if no backend, _q_startOperation will handle the error of this |
251 |
322 |
252 // for HTTP, we want to send out the request as fast as possible to the network, without |
323 // for HTTP, we want to send out the request as fast as possible to the network, without |
253 // invoking methods in a QueuedConnection |
324 // invoking methods in a QueuedConnection |
|
325 #ifndef QT_NO_HTTP |
254 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { |
326 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { |
255 _q_startOperation(); |
327 _q_startOperation(); |
256 } else { |
328 } else { |
257 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
329 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
258 } |
330 } |
|
331 #else |
|
332 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
|
333 #endif // QT_NO_HTTP |
259 } |
334 } |
260 |
335 |
261 q->QIODevice::open(QIODevice::ReadOnly); |
336 q->QIODevice::open(QIODevice::ReadOnly); |
262 } |
337 } |
263 |
338 |
469 Q_Q(QNetworkReplyImpl); |
544 Q_Q(QNetworkReplyImpl); |
470 |
545 |
471 QPointer<QNetworkReplyImpl> qq = q; |
546 QPointer<QNetworkReplyImpl> qq = q; |
472 |
547 |
473 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
548 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
|
549 if (preMigrationDownloaded != Q_INT64_C(-1)) |
|
550 totalSize = totalSize.toLongLong() + preMigrationDownloaded; |
474 pauseNotificationHandling(); |
551 pauseNotificationHandling(); |
475 emit q->downloadProgress(bytesDownloaded, |
552 emit q->downloadProgress(bytesDownloaded, |
476 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); |
553 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); |
477 // important: At the point of this readyRead(), the data parameter list must be empty, |
554 // important: At the point of this readyRead(), the data parameter list must be empty, |
478 // else implicit sharing will trigger memcpy when the user is reading data! |
555 // else implicit sharing will trigger memcpy when the user is reading data! |
519 } |
596 } |
520 |
597 |
521 void QNetworkReplyImplPrivate::finished() |
598 void QNetworkReplyImplPrivate::finished() |
522 { |
599 { |
523 Q_Q(QNetworkReplyImpl); |
600 Q_Q(QNetworkReplyImpl); |
524 if (state == Finished || state == Aborted) |
601 |
525 return; |
602 if (state == Finished || state == Aborted || state == WaitingForSession) |
|
603 return; |
|
604 |
|
605 pauseNotificationHandling(); |
|
606 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
|
607 if (preMigrationDownloaded != Q_INT64_C(-1)) |
|
608 totalSize = totalSize.toLongLong() + preMigrationDownloaded; |
|
609 |
|
610 if (!manager.isNull()) { |
|
611 #ifndef QT_NO_BEARERMANAGEMENT |
|
612 QNetworkSession *session = manager->d_func()->networkSession; |
|
613 if (session && session->state() == QNetworkSession::Roaming && |
|
614 state == Working && errorCode != QNetworkReply::OperationCanceledError) { |
|
615 // only content with a known size will fail with a temporary network failure error |
|
616 if (!totalSize.isNull()) { |
|
617 if (bytesDownloaded != totalSize) { |
|
618 if (migrateBackend()) { |
|
619 // either we are migrating or the request is finished/aborted |
|
620 if (state == Reconnecting || state == WaitingForSession) { |
|
621 resumeNotificationHandling(); |
|
622 return; // exit early if we are migrating. |
|
623 } |
|
624 } else { |
|
625 error(QNetworkReply::TemporaryNetworkFailureError, |
|
626 QNetworkReply::tr("Temporary network failure.")); |
|
627 } |
|
628 } |
|
629 } |
|
630 } |
|
631 #endif |
|
632 } |
|
633 resumeNotificationHandling(); |
526 |
634 |
527 state = Finished; |
635 state = Finished; |
528 pendingNotifications.clear(); |
636 pendingNotifications.clear(); |
529 |
637 |
530 pauseNotificationHandling(); |
638 pauseNotificationHandling(); |
531 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); |
|
532 if (totalSize.isNull() || totalSize == -1) { |
639 if (totalSize.isNull() || totalSize == -1) { |
533 emit q->downloadProgress(bytesDownloaded, bytesDownloaded); |
640 emit q->downloadProgress(bytesDownloaded, bytesDownloaded); |
534 } |
641 } |
535 |
642 |
536 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer)) |
643 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer)) |
537 emit q->uploadProgress(0, 0); |
644 emit q->uploadProgress(0, 0); |
538 resumeNotificationHandling(); |
645 resumeNotificationHandling(); |
539 |
646 |
540 completeCacheSave(); |
647 // if we don't know the total size of or we received everything save the cache |
|
648 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize) |
|
649 completeCacheSave(); |
541 |
650 |
542 // note: might not be a good idea, since users could decide to delete us |
651 // note: might not be a good idea, since users could decide to delete us |
543 // which would delete the backend too... |
652 // which would delete the backend too... |
544 // maybe we should protect the backend |
653 // maybe we should protect the backend |
545 pauseNotificationHandling(); |
654 pauseNotificationHandling(); |
562 } |
671 } |
563 |
672 |
564 void QNetworkReplyImplPrivate::metaDataChanged() |
673 void QNetworkReplyImplPrivate::metaDataChanged() |
565 { |
674 { |
566 Q_Q(QNetworkReplyImpl); |
675 Q_Q(QNetworkReplyImpl); |
567 // do we have cookies? |
676 // 1. do we have cookies? |
568 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()) { |
677 // 2. are we allowed to set them? |
|
678 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull() |
|
679 && (static_cast<QNetworkRequest::LoadControl> |
|
680 (request.attribute(QNetworkRequest::CookieSaveControlAttribute, |
|
681 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) { |
569 QList<QNetworkCookie> cookies = |
682 QList<QNetworkCookie> cookies = |
570 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader)); |
683 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader)); |
571 QNetworkCookieJar *jar = manager->cookieJar(); |
684 QNetworkCookieJar *jar = manager->cookieJar(); |
572 if (jar) |
685 if (jar) |
573 jar->setCookiesFromUrl(cookies, url); |
686 jar->setCookiesFromUrl(cookies, url); |
659 |
772 |
660 // emit signals |
773 // emit signals |
661 d->error(OperationCanceledError, tr("Operation canceled")); |
774 d->error(OperationCanceledError, tr("Operation canceled")); |
662 d->finished(); |
775 d->finished(); |
663 } |
776 } |
|
777 |
|
778 bool QNetworkReplyImpl::canReadLine () const |
|
779 { |
|
780 Q_D(const QNetworkReplyImpl); |
|
781 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine(); |
|
782 } |
|
783 |
664 |
784 |
665 /*! |
785 /*! |
666 Returns the number of bytes available for reading with |
786 Returns the number of bytes available for reading with |
667 QIODevice::read(). The number of bytes available may grow until |
787 QIODevice::read(). The number of bytes available may grow until |
668 the finished() signal is emitted. |
788 the finished() signal is emitted. |
748 } |
868 } |
749 |
869 |
750 return QObject::event(e); |
870 return QObject::event(e); |
751 } |
871 } |
752 |
872 |
|
873 /* |
|
874 Migrates the backend of the QNetworkReply to a new network connection if required. Returns |
|
875 true if the reply is migrated or it is not required; otherwise returns false. |
|
876 */ |
|
877 bool QNetworkReplyImplPrivate::migrateBackend() |
|
878 { |
|
879 Q_Q(QNetworkReplyImpl); |
|
880 |
|
881 // Network reply is already finished or aborted, don't need to migrate. |
|
882 if (state == Finished || state == Aborted) |
|
883 return true; |
|
884 |
|
885 // Backend does not support resuming download. |
|
886 if (!backend->canResume()) |
|
887 return false; |
|
888 |
|
889 // Request has outgoing data, not migrating. |
|
890 if (outgoingData) |
|
891 return false; |
|
892 |
|
893 // Request is serviced from the cache, don't need to migrate. |
|
894 if (copyDevice) |
|
895 return true; |
|
896 |
|
897 state = QNetworkReplyImplPrivate::Reconnecting; |
|
898 |
|
899 if (backend) { |
|
900 delete backend; |
|
901 backend = 0; |
|
902 } |
|
903 |
|
904 cookedHeaders.clear(); |
|
905 rawHeaders.clear(); |
|
906 |
|
907 preMigrationDownloaded = bytesDownloaded; |
|
908 |
|
909 backend = manager->d_func()->findBackend(operation, request); |
|
910 |
|
911 if (backend) { |
|
912 backend->setParent(q); |
|
913 backend->reply = this; |
|
914 backend->setResumeOffset(bytesDownloaded); |
|
915 } |
|
916 |
|
917 #ifndef QT_NO_HTTP |
|
918 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { |
|
919 _q_startOperation(); |
|
920 } else { |
|
921 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
|
922 } |
|
923 #else |
|
924 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); |
|
925 #endif // QT_NO_HTTP |
|
926 |
|
927 return true; |
|
928 } |
|
929 |
|
930 #ifndef QT_NO_BEARERMANAGEMENT |
|
931 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent, |
|
932 const QNetworkRequest &req, |
|
933 QNetworkAccessManager::Operation op) |
|
934 : QNetworkReply(parent) |
|
935 { |
|
936 setRequest(req); |
|
937 setUrl(req.url()); |
|
938 setOperation(op); |
|
939 |
|
940 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); |
|
941 |
|
942 QString msg = QCoreApplication::translate("QNetworkAccessManager", |
|
943 "Network access is disabled."); |
|
944 setError(UnknownNetworkError, msg); |
|
945 |
|
946 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, |
|
947 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError)); |
|
948 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); |
|
949 } |
|
950 |
|
951 QDisabledNetworkReply::~QDisabledNetworkReply() |
|
952 { |
|
953 } |
|
954 #endif |
|
955 |
753 QT_END_NAMESPACE |
956 QT_END_NAMESPACE |
754 |
957 |
755 #include "moc_qnetworkreplyimpl_p.cpp" |
958 #include "moc_qnetworkreplyimpl_p.cpp" |
756 |
959 |