summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/access.pri5
-rw-r--r--src/network/access/qabstractnetworkcache.cpp4
-rw-r--r--src/network/access/qabstractnetworkcache.h2
-rw-r--r--src/network/access/qftp.cpp9
-rw-r--r--src/network/access/qftp.h2
-rw-r--r--src/network/access/qhttp.cpp7
-rw-r--r--src/network/access/qhttp.h8
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp381
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h81
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp278
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h154
-rw-r--r--src/network/access/qhttpnetworkreply.cpp117
-rw-r--r--src/network/access/qhttpnetworkreply_p.h22
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp10
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h3
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp14
-rw-r--r--src/network/access/qnetworkaccessdatabackend.cpp6
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp8
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp6
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp6
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp37
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h4
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp72
-rw-r--r--src/network/access/qnetworkaccessmanager.h2
-rw-r--r--src/network/access/qnetworkcookie.cpp255
-rw-r--r--src/network/access/qnetworkcookie.h21
-rw-r--r--src/network/access/qnetworkcookiejar.cpp296
-rw-r--r--src/network/access/qnetworkcookiejar.h81
-rw-r--r--src/network/access/qnetworkcookiejar_p.h71
-rw-r--r--src/network/access/qnetworkdiskcache.h2
-rw-r--r--src/network/access/qnetworkreply.cpp68
-rw-r--r--src/network/access/qnetworkreply.h5
-rw-r--r--src/network/access/qnetworkreply_p.h2
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp68
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h10
-rw-r--r--src/network/access/qnetworkrequest.cpp4
36 files changed, 1328 insertions, 793 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 3269b2e..ab7b3a7 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -6,6 +6,7 @@ HEADERS += access/qftp.h \
access/qhttpnetworkrequest_p.h \
access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
+ access/qhttpnetworkconnectionchannel_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
access/qnetworkaccesscache_p.h \
@@ -18,6 +19,8 @@ HEADERS += access/qftp.h \
access/qnetworkaccessftpbackend_p.h \
access/qnetworkcookie.h \
access/qnetworkcookie_p.h \
+ access/qnetworkcookiejar.h \
+ access/qnetworkcookiejar_p.h \
access/qnetworkrequest.h \
access/qnetworkrequest_p.h \
access/qnetworkreply.h \
@@ -34,6 +37,7 @@ SOURCES += access/qftp.cpp \
access/qhttpnetworkrequest.cpp \
access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
+ access/qhttpnetworkconnectionchannel.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
access/qnetworkaccessbackend.cpp \
@@ -44,6 +48,7 @@ SOURCES += access/qftp.cpp \
access/qnetworkaccessftpbackend.cpp \
access/qnetworkaccesshttpbackend.cpp \
access/qnetworkcookie.cpp \
+ access/qnetworkcookiejar.cpp \
access/qnetworkrequest.cpp \
access/qnetworkreply.cpp \
access/qnetworkreplyimpl.cpp \
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
index cfc9fe7..3f44059 100644
--- a/src/network/access/qabstractnetworkcache.cpp
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -283,6 +283,8 @@ void QNetworkCacheMetaData::setExpirationDate(const QDateTime &dateTime)
}
/*!
+ \since 4.6
+
Returns all the attributes stored with this cache item.
\sa setAttributes(), QNetworkRequest::Attribute
@@ -293,6 +295,8 @@ QNetworkCacheMetaData::AttributesMap QNetworkCacheMetaData::attributes() const
}
/*!
+ \since 4.6
+
Sets all attributes of this cache item to be the map \a attributes.
\sa attributes(), QNetworkRequest::setAttribute()
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index 38ebb69..40f2313 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -130,7 +130,7 @@ protected:
QAbstractNetworkCache(QAbstractNetworkCachePrivate &dd, QObject *parent);
private:
- Q_DECLARE_SCOPED_PRIVATE(QAbstractNetworkCache)
+ Q_DECLARE_PRIVATE(QAbstractNetworkCache)
Q_DISABLE_COPY(QAbstractNetworkCache)
};
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
index 6fc0626..8e72323 100644
--- a/src/network/access/qftp.cpp
+++ b/src/network/access/qftp.cpp
@@ -871,6 +871,9 @@ void QFtpPI::connected()
#if defined(QFTPPI_DEBUG)
// qDebug("QFtpPI state: %d [connected()]", state);
#endif
+ // try to improve performance by setting TCP_NODELAY
+ commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
+
emit connectState(QFtp::Connected);
}
@@ -1387,7 +1390,7 @@ int QFtpPrivate::addCommand(QFtpCommand *cmd)
\warning The current version of QFtp doesn't fully support
non-Unix FTP servers.
- \sa QHttp, QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+ \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
{FTP Example}
*/
@@ -1734,8 +1737,8 @@ int QFtp::setTransferMode(TransferMode mode)
Enables use of the FTP proxy on host \a host and port \a
port. Calling this function with \a host empty disables proxying.
- QFtp does not support FTP-over-HTTP proxy servers. Use QHttp for
- this.
+ QFtp does not support FTP-over-HTTP proxy servers. Use
+ QNetworkAccessManager for this.
*/
int QFtp::setProxy(const QString &host, quint16 port)
{
diff --git a/src/network/access/qftp.h b/src/network/access/qftp.h
index 8e4af9f..c6aaea4 100644
--- a/src/network/access/qftp.h
+++ b/src/network/access/qftp.h
@@ -162,7 +162,7 @@ public:
private:
Q_DISABLE_COPY(QFtp)
- Q_DECLARE_SCOPED_PRIVATE(QFtp)
+ Q_DECLARE_PRIVATE(QFtp)
Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand())
Q_PRIVATE_SLOT(d_func(), void _q_piFinished(const QString&))
diff --git a/src/network/access/qhttp.cpp b/src/network/access/qhttp.cpp
index 91f9f4e..bb72f89 100644
--- a/src/network/access/qhttp.cpp
+++ b/src/network/access/qhttp.cpp
@@ -513,6 +513,7 @@ public:
/*!
\class QHttpHeader
+ \obsolete
\brief The QHttpHeader class contains header information for HTTP.
\ingroup io
@@ -1006,6 +1007,7 @@ public:
/*!
\class QHttpResponseHeader
+ \obsolete
\brief The QHttpResponseHeader class contains response header information for HTTP.
\ingroup io
@@ -1151,7 +1153,7 @@ int QHttpResponseHeader::minorVersion() const
return d->minVer;
}
-/*! \reimp
+/*! \internal
*/
bool QHttpResponseHeader::parseLine(const QString &line, int number)
{
@@ -1210,6 +1212,7 @@ public:
/*!
\class QHttpRequestHeader
+ \obsolete
\brief The QHttpRequestHeader class contains request header information for HTTP.
\ingroup io
@@ -1365,7 +1368,7 @@ int QHttpRequestHeader::minorVersion() const
return d->minVer;
}
-/*! \reimp
+/*! \internal
*/
bool QHttpRequestHeader::parseLine(const QString &line, int number)
{
diff --git a/src/network/access/qhttp.h b/src/network/access/qhttp.h
index c1fda0e..e524350 100644
--- a/src/network/access/qhttp.h
+++ b/src/network/access/qhttp.h
@@ -112,7 +112,7 @@ protected:
QScopedPointer<QHttpHeaderPrivate> d_ptr;
private:
- Q_DECLARE_SCOPED_PRIVATE(QHttpHeader)
+ Q_DECLARE_PRIVATE(QHttpHeader)
};
class QHttpResponseHeaderPrivate;
@@ -139,7 +139,7 @@ protected:
bool parseLine(const QString &line, int number);
private:
- Q_DECLARE_SCOPED_PRIVATE(QHttpResponseHeader)
+ Q_DECLARE_PRIVATE(QHttpResponseHeader)
friend class QHttpPrivate;
};
@@ -167,7 +167,7 @@ protected:
bool parseLine(const QString &line, int number);
private:
- Q_DECLARE_SCOPED_PRIVATE(QHttpRequestHeader)
+ Q_DECLARE_PRIVATE(QHttpRequestHeader)
};
class Q_NETWORK_EXPORT QHttp : public QObject
@@ -282,7 +282,7 @@ Q_SIGNALS:
private:
Q_DISABLE_COPY(QHttp)
- Q_DECLARE_SCOPED_PRIVATE(QHttp)
+ Q_DECLARE_PRIVATE(QHttp)
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
Q_PRIVATE_SLOT(d_func(), void _q_slotReadyRead())
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 827681f..93d738c 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qhttpnetworkconnection_p.h"
+#include "qhttpnetworkconnectionchannel_p.h"
#include "private/qnoncontiguousbytedevice_p.h"
#include <private/qnetworkrequest_p.h>
#include <private/qobject_p.h>
@@ -74,7 +75,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
#endif
{
- channels = new Channel[channelCount];
+ channels = new QHttpNetworkConnectionChannel[channelCount];
}
QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
@@ -88,58 +89,11 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
delete []channels;
}
-void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket)
-{
- Q_Q(QHttpNetworkConnection);
-
- QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
- q, SLOT(_q_bytesWritten(qint64)),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(connected()),
- q, SLOT(_q_connected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(readyRead()),
- q, SLOT(_q_readyRead()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(disconnected()),
- q, SLOT(_q_disconnected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
- q, SLOT(_q_error(QAbstractSocket::SocketError)),
- Qt::DirectConnection);
-#ifndef QT_NO_NETWORKPROXY
- QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
- q, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
- Qt::DirectConnection);
-#endif
-
-#ifndef QT_NO_OPENSSL
- QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
- if (sslSocket) {
- // won't be a sslSocket if encrypt is false
- QObject::connect(sslSocket, SIGNAL(encrypted()),
- q, SLOT(_q_encrypted()),
- Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)),
- q, SLOT(_q_sslErrors(const QList<QSslError>&)),
- Qt::DirectConnection);
- }
-#endif
-}
-
void QHttpNetworkConnectionPrivate::init()
{
- for (int i = 0; i < channelCount; ++i) {
-#ifndef QT_NO_OPENSSL
- if (encrypt)
- channels[i].socket = new QSslSocket;
- else
- channels[i].socket = new QTcpSocket;
-#else
- channels[i].socket = new QTcpSocket;
-#endif
-
- connectSignals(channels[i].socket);
+ for (int i = 0; i < channelCount; i++) {
+ channels[i].setConnection(this->q_func());
+ channels[i].init();
}
}
@@ -156,47 +110,35 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
bool QHttpNetworkConnectionPrivate::isSocketBusy(QAbstractSocket *socket) const
{
int i = indexOf(socket);
- return (channels[i].state & BusyState);
+ return (channels[i].state & QHttpNetworkConnectionChannel::BusyState);
}
bool QHttpNetworkConnectionPrivate::isSocketWriting(QAbstractSocket *socket) const
{
int i = indexOf(socket);
- return (i != -1 && (channels[i].state & WritingState));
+ return (i != -1 && (channels[i].state & QHttpNetworkConnectionChannel::WritingState));
}
bool QHttpNetworkConnectionPrivate::isSocketWaiting(QAbstractSocket *socket) const
{
int i = indexOf(socket);
- return (i != -1 && (channels[i].state & WaitingState));
+ return (i != -1 && (channels[i].state & QHttpNetworkConnectionChannel::WaitingState));
}
bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) const
{
int i = indexOf(socket);
- return (i != -1 && (channels[i].state & ReadingState));
-}
-
-
-void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, const QByteArray &fragment)
-{
- char *dst = reply.d_func()->responseData.reserve(fragment.size());
- qMemCopy(dst, fragment.constData(), fragment.size());
-}
-
-void QHttpNetworkConnectionPrivate::appendCompressedData(QHttpNetworkReply &reply, const QByteArray &fragment)
-{
- reply.d_func()->compressedData.append(fragment);
+ return (i != -1 && (channels[i].state & QHttpNetworkConnectionChannel::ReadingState));
}
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
{
- return reply.d_func()->responseData.size();
+ return reply.d_func()->responseData.byteAmount();
}
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
{
- return reply.d_func()->responseData.nextDataBlockSize();
+ return reply.d_func()->responseData.sizeNextBlock();
}
qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetworkReply &reply) const
@@ -204,21 +146,6 @@ qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetwor
return reply.d_func()->compressedData.size();
}
-qint64 QHttpNetworkConnectionPrivate::read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize)
-{
- QRingBuffer *rb = &reply.d_func()->responseData;
- if (maxSize == -1 || maxSize >= rb->size()) {
- // read the whole data
- data = rb->readAll();
- rb->clear();
- } else {
- // read only the requested length
- data.resize(maxSize);
- rb->read(data.data(), maxSize);
- }
- return data.size();
-}
-
void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply)
{
reply->d_func()->compressedData.clear();
@@ -261,7 +188,12 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
#ifndef QT_NO_NETWORKPROXY
}
#endif
- // set the gzip header
+
+ // If the request had a accept-encoding set, we better not mess
+ // with it. If it was not set, we announce that we understand gzip
+ // and remember this fact in request.d->autoDecompress so that
+ // we can later decompress the HTTP reply if it has such an
+ // encoding.
value = request.headerField("accept-encoding");
if (value.isEmpty()) {
#ifndef QT_NO_COMPRESS
@@ -305,7 +237,7 @@ bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket)
channels[index].resendCurrent = true;
return false;
}
- channels[index].state = ConnectingState;
+ channels[index].state = QHttpNetworkConnectionChannel::ConnectingState;
channels[index].pendingEncrypt = encrypt;
// This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
@@ -335,8 +267,9 @@ bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket)
#ifndef QT_NO_OPENSSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
sslSocket->connectToHostEncrypted(connectHost, connectPort);
- if (channels[index].ignoreSSLErrors)
+ if (channels[index].ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
+ sslSocket->ignoreSslErrors(channels[index].ignoreSslErrorsList);
#else
emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError);
#endif
@@ -355,7 +288,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
int i = indexOf(socket);
switch (channels[i].state) {
- case IdleState: { // write the header
+ case QHttpNetworkConnectionChannel::IdleState: { // write the header
if (!ensureConnection(socket)) {
// wait for the connection (and encryption) to be done
// sendRequest will be called again from either
@@ -369,7 +302,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
channels[i].reply->d_func()->connection = q;
channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress;
}
- channels[i].state = WritingState;
+ channels[i].state = QHttpNetworkConnectionChannel::WritingState;
channels[i].pendingEncrypt = false;
// if the url contains authentication parameters, use the new ones
// both channels will use the new authentication parameters
@@ -399,24 +332,25 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
if (uploadByteDevice) {
// connect the signals so this function gets called again
- QObject::connect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead()));
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead()));
channels[i].bytesTotal = channels[i].request.contentLength();
} else {
- channels[i].state = WaitingState;
+ socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets!
+ channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
break;
}
// write the initial chunk together with the headers
// fall through
}
- case WritingState:
+ case QHttpNetworkConnectionChannel::WritingState:
{
// write the data
QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
if (!uploadByteDevice || channels[i].bytesTotal == channels[i].written) {
if (uploadByteDevice)
emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);
- channels[i].state = WaitingState; // now wait for response
+ channels[i].state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
sendRequest(socket);
break;
}
@@ -463,7 +397,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
if (channels[i].written == channels[i].bytesTotal) {
// make sure this function is called once again
- channels[i].state = WaitingState;
+ channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
sendRequest(socket);
break;
}
@@ -473,11 +407,11 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
break;
}
- case WaitingState:
+ case QHttpNetworkConnectionChannel::WaitingState:
{
QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
if (uploadByteDevice) {
- QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead()));
+ QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead()));
}
// ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
// this is needed if the sends an reply before we have finished sending the request. In that
@@ -485,8 +419,8 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
receiveReply(socket, channels[i].reply);
break;
}
- case ReadingState:
- case Wait4AuthState:
+ case QHttpNetworkConnectionChannel::ReadingState:
+ case QHttpNetworkConnectionChannel::Wait4AuthState:
// ignore _q_bytesWritten in these states
// fall through
default:
@@ -556,15 +490,14 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork
if (ret >= retCheck) {
if (inflated.size()) {
reply->d_func()->totalProgress += inflated.size();
- appendUncompressedData(*reply, inflated);
+ reply->d_func()->appendUncompressedReplyData(inflated);
if (shouldEmitSignals(reply)) {
+ // important: At the point of this readyRead(), inflated must be cleared,
+ // else implicit sharing will trigger memcpy when the user is reading data!
emit reply->readyRead();
// make sure that the reply is valid
if (channels[i].reply != reply)
return true;
- // emit dataReadProgress signal (signal is currently not connected
- // to the rest of QNAM) since readProgress of the
- // QNonContiguousByteDevice is used
emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
// make sure that the reply is valid
if (channels[i].reply != reply)
@@ -595,7 +528,7 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
if (!socket->bytesAvailable()) {
if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
- channels[i].state = IdleState;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
allDone(socket, reply);
} else {
// try to reconnect/resend before sending an error.
@@ -646,7 +579,7 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
emit reply->headerChanged();
if (!expectContent(reply)) {
reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
- channels[i].state = IdleState;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
allDone(socket, reply);
return;
}
@@ -674,25 +607,23 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
{
// use the traditional slower reading (for compressed encoding, chunked encoding,
// no content-length etc)
- QBuffer fragment;
- fragment.open(QIODevice::WriteOnly);
- bytes = reply->d_func()->readBody(socket, &fragment);
+ QByteDataBuffer byteDatas;
+ bytes = reply->d_func()->readBody(socket, &byteDatas);
if (bytes) {
if (reply->d_func()->autoDecompress)
- appendCompressedData(*reply, fragment.data());
+ reply->d_func()->appendCompressedReplyData(byteDatas);
else
- appendUncompressedData(*reply, fragment.data());
+ reply->d_func()->appendUncompressedReplyData(byteDatas);
if (!reply->d_func()->autoDecompress) {
- reply->d_func()->totalProgress += fragment.size();
+ reply->d_func()->totalProgress += bytes;
if (shouldEmitSignals(reply)) {
+ // important: At the point of this readyRead(), the byteDatas list must be empty,
+ // else implicit sharing will trigger memcpy when the user is reading data!
emit reply->readyRead();
// make sure that the reply is valid
if (channels[i].reply != reply)
return;
- // emit dataReadProgress signal (signal is currently not connected
- // to the rest of QNAM) since readProgress of the
- // QNonContiguousByteDevice is used
emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength);
// make sure that the reply is valid
if (channels[i].reply != reply)
@@ -711,7 +642,7 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
// everything done, fall through
}
case QHttpNetworkReplyPrivate::AllDoneState:
- channels[i].state = IdleState;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
allDone(socket, reply);
break;
default:
@@ -860,11 +791,11 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
eraseData(channels[i].reply);
closeChannel(i);
channels[i].lastStatus = 0;
- channels[i].state = Wait4AuthState;
+ channels[i].state = QHttpNetworkConnectionChannel::Wait4AuthState;
return false;
}
// cannot use this socket until the slot returns
- channels[i].state = WaitingState;
+ channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
socket->blockSignals(true);
if (!isProxy) {
pendingAuthSignal = true;
@@ -879,7 +810,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
}
socket->blockSignals(false);
// socket free to use
- channels[i].state = IdleState;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
if (priv->phase != QAuthenticatorPrivate::Done) {
// send any pending requests
copyCredentials(i, auth, isProxy);
@@ -902,8 +833,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
socket->close();
// remove pending request on the other channels
for (int j = 0; j < channelCount; ++j) {
- if (j != i && channels[j].state == Wait4AuthState)
- channels[j].state = IdleState;
+ if (j != i && channels[j].state == QHttpNetworkConnectionChannel::Wait4AuthState)
+ channels[j].state = QHttpNetworkConnectionChannel::IdleState;
}
return true;
}
@@ -999,11 +930,7 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke
void QHttpNetworkConnectionPrivate::closeChannel(int channel)
{
- QAbstractSocket *socket = channels[channel].socket;
- socket->blockSignals(true);
- socket->close();
- socket->blockSignals(false);
- channels[channel].state = IdleState;
+ channels[channel].close();
}
void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket)
@@ -1097,53 +1024,6 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
-//private slots
-void QHttpNetworkConnectionPrivate::_q_readyRead()
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return; // ### error
- if (isSocketWaiting(socket) || isSocketReading(socket)) {
- int i = indexOf(socket);
- channels[i].state = ReadingState;
- if (channels[i].reply)
- receiveReply(socket, channels[i].reply);
- }
- // ### error
-}
-
-void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes)
-{
- Q_UNUSED(bytes);
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return; // ### error
- // bytes have been written to the socket. write even more of them :)
- if (isSocketWriting(socket))
- sendRequest(socket);
- // otherwise we do nothing
-}
-
-void QHttpNetworkConnectionPrivate::_q_disconnected()
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return; // ### error
- // read the available data before closing
- int i = indexOf(socket);
- if (isSocketWaiting(socket) || isSocketReading(socket)) {
- channels[i].state = ReadingState;
- if (channels[i].reply)
- receiveReply(socket, channels[i].reply);
- } else if (channels[i].state == IdleState && channels[i].resendCurrent) {
- // re-sending request because the socket was in ClosingState
- QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
- }
- channels[i].state = IdleState;
-}
void QHttpNetworkConnectionPrivate::_q_startNextRequest()
{
@@ -1151,7 +1031,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
for (int i = 0; i < channelCount; ++i) {
if (channels[i].resendCurrent) {
channels[i].resendCurrent = false;
- channels[i].state = IdleState;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
if (channels[i].reply)
sendRequest(channels[i].socket);
}
@@ -1175,125 +1055,14 @@ void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests()
// send the request using the idle socket
for (int i = 0 ; i < channelCount; ++i) {
QAbstractSocket *socket = channels[i].socket;
- if (channels[i].state == Wait4AuthState) {
- channels[i].state = IdleState;
+ if (channels[i].state == QHttpNetworkConnectionChannel::Wait4AuthState) {
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
if (channels[i].reply)
sendRequest(socket);
}
}
}
-void QHttpNetworkConnectionPrivate::_q_connected()
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return; // ### error
- int i = indexOf(socket);
- // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
- //channels[i].reconnectAttempts = 2;
- if (!channels[i].pendingEncrypt) {
- channels[i].state = IdleState;
- if (channels[i].reply)
- sendRequest(socket);
- else
- closeChannel(i);
- }
-}
-
-
-void QHttpNetworkConnectionPrivate::_q_error(QAbstractSocket::SocketError socketError)
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return;
- bool send2Reply = false;
- int i = indexOf(socket);
- QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
-
- switch (socketError) {
- case QAbstractSocket::HostNotFoundError:
- errorCode = QNetworkReply::HostNotFoundError;
- break;
- case QAbstractSocket::ConnectionRefusedError:
- errorCode = QNetworkReply::ConnectionRefusedError;
- break;
- case QAbstractSocket::RemoteHostClosedError:
- // try to reconnect/resend before sending an error.
- // while "Reading" the _q_disconnected() will handle this.
- if (channels[i].state != IdleState && channels[i].state != ReadingState) {
- if (channels[i].reconnectAttempts-- > 0) {
- resendCurrentRequest(socket);
- return;
- } else {
- send2Reply = true;
- errorCode = QNetworkReply::RemoteHostClosedError;
- }
- } else {
- return;
- }
- break;
- case QAbstractSocket::SocketTimeoutError:
- // try to reconnect/resend before sending an error.
- if (channels[i].state == WritingState && (channels[i].reconnectAttempts-- > 0)) {
- resendCurrentRequest(socket);
- return;
- }
- send2Reply = true;
- errorCode = QNetworkReply::TimeoutError;
- break;
- case QAbstractSocket::ProxyAuthenticationRequiredError:
- errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
- break;
- case QAbstractSocket::SslHandshakeFailedError:
- errorCode = QNetworkReply::SslHandshakeFailedError;
- break;
- default:
- // all other errors are treated as NetworkError
- errorCode = QNetworkReply::UnknownNetworkError;
- break;
- }
- QPointer<QObject> that = q;
- QString errorString = errorDetail(errorCode, socket);
- if (send2Reply) {
- if (channels[i].reply) {
- channels[i].reply->d_func()->errorString = errorString;
- // this error matters only to this reply
- emit channels[i].reply->finishedWithError(errorCode, errorString);
- }
- // send the next request
- QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
- } else {
- // the failure affects all requests.
- emit q->error(errorCode, errorString);
- }
- if (that) //signals make enter the event loop
- closeChannel(i);
-}
-
-#ifndef QT_NO_NETWORKPROXY
-void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
-{
- Q_Q(QHttpNetworkConnection);
- emit q->proxyAuthenticationRequired(proxy, auth, q);
-}
-#endif
-
-void QHttpNetworkConnectionPrivate::_q_uploadDataReadyRead()
-{
- Q_Q(QHttpNetworkConnection);
- // upload data emitted readyRead()
- // find out which channel it is for
- QObject *sender = q->sender();
-
- for (int i = 0; i < channelCount; ++i) {
- if (sender == channels[i].request.uploadByteDevice()) {
- sendRequest(channels[i].socket);
- break;
- }
- }
-}
QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
@@ -1388,27 +1157,6 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
// SSL support below
#ifndef QT_NO_OPENSSL
-void QHttpNetworkConnectionPrivate::_q_encrypted()
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return; // ### error
- int i = indexOf(socket);
- channels[i].state = IdleState;
- sendRequest(socket);
-}
-
-void QHttpNetworkConnectionPrivate::_q_sslErrors(const QList<QSslError> &errors)
-{
- Q_Q(QHttpNetworkConnection);
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
- if (!socket)
- return;
- //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
- emit q->sslErrors(errors);
-}
-
QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const
{
if (!encrypt)
@@ -1440,15 +1188,32 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel)
if (channel == -1) { // ignore for all channels
for (int i = 0; i < d->channelCount; ++i) {
static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
- d->channels[i].ignoreSSLErrors = true;
+ d->channels[i].ignoreAllSslErrors = true;
}
} else {
static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
- d->channels[channel].ignoreSSLErrors = true;
+ d->channels[channel].ignoreAllSslErrors = true;
}
}
+void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
+{
+ Q_D(QHttpNetworkConnection);
+ if (!d->encrypt)
+ return;
+
+ if (channel == -1) { // ignore for all channels
+ for (int i = 0; i < d->channelCount; ++i) {
+ static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
+ d->channels[i].ignoreSslErrorsList = errors;
+ }
+
+ } else {
+ static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
+ d->channels[channel].ignoreSslErrorsList = errors;
+ }
+}
#endif //QT_NO_OPENSSL
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 3b23870..d6b0325 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -65,6 +65,7 @@
#include <private/qhttpnetworkrequest_p.h>
#include <private/qhttpnetworkreply_p.h>
+#include "qhttpnetworkconnectionchannel_p.h"
#ifndef QT_NO_HTTP
@@ -79,6 +80,7 @@ QT_BEGIN_NAMESPACE
class QHttpNetworkRequest;
class QHttpNetworkReply;
+class QByteArray;
class QHttpNetworkConnectionPrivate;
class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
@@ -116,6 +118,7 @@ public:
#ifndef QT_NO_OPENSSL
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
+ void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
Q_SIGNALS:
void sslErrors(const QList<QSslError> &errors);
@@ -132,31 +135,16 @@ Q_SIGNALS:
void error(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
private:
- Q_DECLARE_SCOPED_PRIVATE(QHttpNetworkConnection)
+ Q_DECLARE_PRIVATE(QHttpNetworkConnection)
Q_DISABLE_COPY(QHttpNetworkConnection)
friend class QHttpNetworkReply;
+ friend class QHttpNetworkConnectionChannel;
- Q_PRIVATE_SLOT(d_func(), void _q_bytesWritten(qint64))
- Q_PRIVATE_SLOT(d_func(), void _q_readyRead())
- Q_PRIVATE_SLOT(d_func(), void _q_disconnected())
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
Q_PRIVATE_SLOT(d_func(), void _q_restartAuthPendingRequests())
- Q_PRIVATE_SLOT(d_func(), void _q_connected())
- Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
-#ifndef QT_NO_NETWORKPROXY
- Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))
-#endif
- Q_PRIVATE_SLOT(d_func(), void _q_uploadDataReadyRead())
-
-#ifndef QT_NO_OPENSSL
- Q_PRIVATE_SLOT(d_func(), void _q_encrypted())
- Q_PRIVATE_SLOT(d_func(), void _q_sslErrors(const QList<QSslError>&))
-#endif
};
-
-
// private classes
typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
@@ -168,17 +156,6 @@ public:
QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
~QHttpNetworkConnectionPrivate();
void init();
- void connectSignals(QAbstractSocket *socket);
-
- enum SocketState {
- IdleState = 0, // ready to send request
- ConnectingState = 1, // connecting to host
- WritingState = 2, // writing the data
- WaitingState = 4, // waiting for reply
- ReadingState = 8, // reading the reply
- Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done
- BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
- };
enum { ChunkSize = 4096 };
@@ -198,18 +175,8 @@ public:
void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
// private slots
- void _q_bytesWritten(qint64 bytes); // proceed sending
- void _q_readyRead(); // pending data to read
- void _q_disconnected(); // disconnected from host
void _q_startNextRequest(); // send the next request from the queue
void _q_restartAuthPendingRequests(); // send the currently blocked request
- void _q_connected(); // start sending request
- void _q_error(QAbstractSocket::SocketError); // error from socket
-#ifndef QT_NO_NETWORKPROXY
- void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
-#endif
-
- void _q_uploadDataReadyRead();
void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
bool ensureConnection(QAbstractSocket *socket);
@@ -224,46 +191,16 @@ public:
quint16 port;
bool encrypt;
- struct Channel {
- QAbstractSocket *socket;
- SocketState state;
- QHttpNetworkRequest request; // current request
- QHttpNetworkReply *reply; // current reply for this request
- qint64 written;
- qint64 bytesTotal;
- bool resendCurrent;
- int lastStatus; // last status received on this channel
- bool pendingEncrypt; // for https (send after encrypted)
- int reconnectAttempts; // maximum 2 reconnection attempts
- QAuthenticatorPrivate::Method authMehtod;
- QAuthenticatorPrivate::Method proxyAuthMehtod;
- QAuthenticator authenticator;
- QAuthenticator proxyAuthenticator;
-#ifndef QT_NO_OPENSSL
- bool ignoreSSLErrors;
-#endif
- Channel() : socket(0), state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false),
- lastStatus(0), pendingEncrypt(false), reconnectAttempts(2),
- authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
-#ifndef QT_NO_OPENSSL
- , ignoreSSLErrors(false)
-#endif
- {}
- };
static const int channelCount;
- Channel *channels; // parallel connections to the server
+ QHttpNetworkConnectionChannel *channels; // parallel connections to the server
+
bool pendingAuthSignal; // there is an incomplete authentication signal
bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
- void appendUncompressedData(QHttpNetworkReply &reply, const QByteArray &fragment);
- void appendCompressedData(QHttpNetworkReply &reply, const QByteArray &fragment);
-
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const;
- qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize);
-
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply);
@@ -272,8 +209,6 @@ public:
inline bool expectContent(QHttpNetworkReply *reply);
#ifndef QT_NO_OPENSSL
- void _q_encrypted(); // start sending request (https)
- void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
#endif
@@ -284,6 +219,8 @@ public:
//The request queues
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
+
+ friend class QHttpNetworkConnectionChannel;
};
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
new file mode 100644
index 0000000..6cd46fd
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkconnection_p.h"
+#include "qhttpnetworkconnectionchannel_p.h"
+
+#include <qpair.h>
+#include <qdebug.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslkey.h>
+# include <QtNetwork/qsslcipher.h>
+# include <QtNetwork/qsslconfiguration.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
+
+void QHttpNetworkConnectionChannel::init()
+{
+#ifndef QT_NO_OPENSSL
+ if (connection->d_func()->encrypt)
+ socket = new QSslSocket;
+ else
+ socket = new QTcpSocket;
+#else
+ socket = new QTcpSocket;
+#endif
+
+ QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
+ this, SLOT(_q_bytesWritten(qint64)),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(connected()),
+ this, SLOT(_q_connected()),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(readyRead()),
+ this, SLOT(_q_readyRead()),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(disconnected()),
+ this, SLOT(_q_disconnected()),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(_q_error(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+#ifndef QT_NO_NETWORKPROXY
+ QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
+ this, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
+ Qt::DirectConnection);
+#endif
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ if (sslSocket) {
+ // won't be a sslSocket if encrypt is false
+ QObject::connect(sslSocket, SIGNAL(encrypted()),
+ this, SLOT(_q_encrypted()),
+ Qt::DirectConnection);
+ QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)),
+ this, SLOT(_q_sslErrors(const QList<QSslError>&)),
+ Qt::DirectConnection);
+ }
+#endif
+}
+
+
+void QHttpNetworkConnectionChannel::close()
+{
+ socket->blockSignals(true);
+ socket->close();
+ socket->blockSignals(false);
+ state = QHttpNetworkConnectionChannel::IdleState;
+}
+
+
+//private slots
+void QHttpNetworkConnectionChannel::_q_readyRead()
+{
+ if (!socket)
+ return; // ### error
+ if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) {
+ state = QHttpNetworkConnectionChannel::ReadingState;
+ if (reply)
+ connection->d_func()->receiveReply(socket, reply);
+ }
+ // ### error
+}
+
+void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
+{
+ Q_UNUSED(bytes);
+ if (!socket)
+ return; // ### error
+ // bytes have been written to the socket. write even more of them :)
+ if (connection->d_func()->isSocketWriting(socket))
+ connection->d_func()->sendRequest(socket);
+ // otherwise we do nothing
+}
+
+void QHttpNetworkConnectionChannel::_q_disconnected()
+{
+ if (!socket)
+ return; // ### error
+ // read the available data before closing
+ if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) {
+ state = QHttpNetworkConnectionChannel::ReadingState;
+ if (reply)
+ connection->d_func()->receiveReply(socket, reply);
+ } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
+ // re-sending request because the socket was in ClosingState
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+ state = QHttpNetworkConnectionChannel::IdleState;
+}
+
+
+void QHttpNetworkConnectionChannel::_q_connected()
+{
+ if (!socket)
+ return; // ### error
+
+ // improve performance since we get the request sent by the kernel ASAP
+ socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
+
+ // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
+ //channels[i].reconnectAttempts = 2;
+ if (!pendingEncrypt) {
+ state = QHttpNetworkConnectionChannel::IdleState;
+ if (reply)
+ connection->d_func()->sendRequest(socket);
+ else
+ close();
+ }
+}
+
+
+void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
+{
+ if (!socket)
+ return;
+ bool send2Reply = false;
+ QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
+
+ switch (socketError) {
+ case QAbstractSocket::HostNotFoundError:
+ errorCode = QNetworkReply::HostNotFoundError;
+ break;
+ case QAbstractSocket::ConnectionRefusedError:
+ errorCode = QNetworkReply::ConnectionRefusedError;
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+ // try to reconnect/resend before sending an error.
+ // while "Reading" the _q_disconnected() will handle this.
+ if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
+ if (reconnectAttempts-- > 0) {
+ connection->d_func()->resendCurrentRequest(socket);
+ return;
+ } else {
+ send2Reply = true;
+ errorCode = QNetworkReply::RemoteHostClosedError;
+ }
+ } else {
+ return;
+ }
+ break;
+ case QAbstractSocket::SocketTimeoutError:
+ // try to reconnect/resend before sending an error.
+ if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
+ connection->d_func()->resendCurrentRequest(socket);
+ return;
+ }
+ send2Reply = true;
+ errorCode = QNetworkReply::TimeoutError;
+ break;
+ case QAbstractSocket::ProxyAuthenticationRequiredError:
+ errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
+ break;
+ case QAbstractSocket::SslHandshakeFailedError:
+ errorCode = QNetworkReply::SslHandshakeFailedError;
+ break;
+ default:
+ // all other errors are treated as NetworkError
+ errorCode = QNetworkReply::UnknownNetworkError;
+ break;
+ }
+ QPointer<QObject> that = connection;
+ QString errorString = connection->d_func()->errorDetail(errorCode, socket);
+ if (send2Reply) {
+ if (reply) {
+ reply->d_func()->errorString = errorString;
+ // this error matters only to this reply
+ emit reply->finishedWithError(errorCode, errorString);
+ }
+ // send the next request
+ QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
+ } else {
+ // the failure affects all requests.
+ emit connection->error(errorCode, errorString);
+ }
+ if (that) //signal emission triggered event loop
+ close();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
+{
+ emit connection->proxyAuthenticationRequired(proxy, auth, connection);
+}
+#endif
+
+void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
+{
+ connection->d_func()->sendRequest(socket);
+}
+
+#ifndef QT_NO_OPENSSL
+void QHttpNetworkConnectionChannel::_q_encrypted()
+{
+ if (!socket)
+ return; // ### error
+ state = QHttpNetworkConnectionChannel::IdleState;
+ connection->d_func()->sendRequest(socket);
+}
+
+void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
+{
+ if (!socket)
+ return;
+ //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
+ emit connection->sslErrors(errors);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qhttpnetworkconnectionchannel_p.cpp"
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
new file mode 100644
index 0000000..013e7a5
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKCONNECTIONCHANNEL_H
+#define QHTTPNETWORKCONNECTIONCHANNEL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qabstractsocket.h>
+
+#include <private/qobject_p.h>
+#include <qauthenticator.h>
+#include <qnetworkproxy.h>
+#include <qbuffer.h>
+
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qhttpnetworkreply_p.h>
+
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslsocket.h>
+# include <QtNetwork/qsslerror.h>
+#else
+# include <QtNetwork/qtcpsocket.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkRequest;
+class QHttpNetworkReply;
+class QByteArray;
+class QHttpNetworkConnection;
+
+class QHttpNetworkConnectionChannel : public QObject {
+ Q_OBJECT
+public:
+ enum ChannelState {
+ IdleState = 0, // ready to send request
+ ConnectingState = 1, // connecting to host
+ WritingState = 2, // writing the data
+ WaitingState = 4, // waiting for reply
+ ReadingState = 8, // reading the reply
+ Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done
+ BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
+ };
+ QAbstractSocket *socket;
+ ChannelState state;
+ QHttpNetworkRequest request; // current request
+ QHttpNetworkReply *reply; // current reply for this request
+ qint64 written;
+ qint64 bytesTotal;
+ bool resendCurrent;
+ int lastStatus; // last status received on this channel
+ bool pendingEncrypt; // for https (send after encrypted)
+ int reconnectAttempts; // maximum 2 reconnection attempts
+ QAuthenticatorPrivate::Method authMehtod;
+ QAuthenticatorPrivate::Method proxyAuthMehtod;
+ QAuthenticator authenticator;
+ QAuthenticator proxyAuthenticator;
+#ifndef QT_NO_OPENSSL
+ bool ignoreAllSslErrors;
+ QList<QSslError> ignoreSslErrorsList;
+#endif
+ QHttpNetworkConnectionChannel() : socket(0), state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false),
+ lastStatus(0), pendingEncrypt(false), reconnectAttempts(2),
+ authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
+#ifndef QT_NO_OPENSSL
+ , ignoreAllSslErrors(false)
+#endif
+ , connection(0)
+ {}
+
+ void setConnection(QHttpNetworkConnection *c) {connection = c;}
+ QHttpNetworkConnection *connection;
+
+ void init();
+ void close();
+
+ protected slots:
+ void _q_bytesWritten(qint64 bytes); // proceed sending
+ void _q_readyRead(); // pending data to read
+ void _q_disconnected(); // disconnected from host
+ void _q_connected(); // start sending request
+ void _q_error(QAbstractSocket::SocketError); // error from socket
+#ifndef QT_NO_NETWORKPROXY
+ void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
+#endif
+
+ void _q_uploadDataReadyRead();
+
+#ifndef QT_NO_OPENSSL
+ void _q_encrypted(); // start sending request (https)
+ void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
+#endif
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 5319252..52672d5 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -176,13 +176,10 @@ qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
return -1;
}
-QByteArray QHttpNetworkReply::read(qint64 maxSize)
+QByteArray QHttpNetworkReply::readAny()
{
Q_D(QHttpNetworkReply);
- QByteArray data;
- if (d->connection)
- d->connection->d_func()->read(*this, data, maxSize);
- return data;
+ return d->responseData.read();
}
bool QHttpNetworkReply::isFinished() const
@@ -195,8 +192,9 @@ bool QHttpNetworkReply::isFinished() const
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
+ chunkedTransferEncoding(0),
currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
- autoDecompress(false), requestIsPrepared(false)
+ autoDecompress(false), responseData(), requestIsPrepared(false)
{
}
@@ -498,6 +496,9 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
state = ReadingDataState;
fragment.clear(); // next fragment
bodyLength = contentLength(); // cache the length
+
+ // cache isChunked() since it is called often
+ chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
}
return bytes;
}
@@ -538,7 +539,7 @@ void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
bool QHttpNetworkReplyPrivate::isChunked()
{
- return headerField("transfer-encoding").toLower().contains("chunked");
+ return chunkedTransferEncoding;
}
bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
@@ -549,17 +550,19 @@ bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QRingBuffer *rb)
+qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
{
- quint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
- char* dst = rb->reserve(toBeRead);
- qint64 haveRead = socket->read(dst, toBeRead);
+ qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
+ QByteArray bd;
+ bd.resize(toBeRead);
+ qint64 haveRead = socket->read(bd.data(), bd.size());
if (haveRead == -1) {
- rb->chop(toBeRead);
+ bd.clear();
return 0; // ### error checking here;
}
+ bd.resize(haveRead);
- rb->chop(toBeRead - haveRead);
+ rb->append(bd);
if (contentRead + haveRead == bodyLength) {
state = AllDoneState;
@@ -571,7 +574,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QRingBuff
}
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
+qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
if (isChunked()) {
@@ -589,33 +592,35 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *ou
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
{
qint64 bytes = 0;
Q_ASSERT(in);
Q_ASSERT(out);
int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
- QByteArray raw(toBeRead, 0);
- while (size > 0) {
- qint64 read = in->read(raw.data(), raw.size());
- if (read == 0)
- return bytes;
- // ### error checking here
- qint64 written = out->write(raw.data(), read);
- if (written == 0)
+ while (toBeRead > 0) {
+ QByteArray byteData;
+ byteData.resize(toBeRead);
+ qint64 haveRead = in->read(byteData.data(), byteData.size());
+ if (haveRead <= 0) {
+ // ### error checking here
+ byteData.clear();
return bytes;
- if (read != written)
- qDebug() << "### read" << read << "written" << written;
- bytes += read;
- size -= read;
- out->waitForBytesWritten(-1); // throttle
+ }
+
+ byteData.resize(haveRead);
+ out->append(byteData);
+ bytes += haveRead;
+ size -= haveRead;
+
+ toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
}
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QIODevice *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
{
qint64 bytes = 0;
while (in->bytesAvailable()) { // while we can read from input
@@ -636,17 +641,14 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QIODevice *
state = AllDoneState;
break;
}
- // otherwise, read data
- qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
- QByteArray buffer(readSize, 0);
- qint64 read = in->read(buffer.data(), readSize);
- bytes += read;
- currentChunkRead += read;
- qint64 written = out->write(buffer);
- Q_UNUSED(written); // Avoid compile warning when building release
- Q_ASSERT(read == written);
+
+ // otherwise, try to read what is missing for this chunk
+ qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
+ currentChunkRead += haveRead;
+ bytes += haveRead;
+
// ### error checking here
- out->waitForBytesWritten(-1);
+
}
return bytes;
}
@@ -683,6 +685,36 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
return bytes;
}
+void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
+{
+ responseData.append(qba);
+
+ // clear the original! helps with implicit sharing and
+ // avoiding memcpy when the user is reading the data
+ qba.clear();
+}
+
+void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
+{
+ responseData.append(data);
+
+ // clear the original! helps with implicit sharing and
+ // avoiding memcpy when the user is reading the data
+ data.clear();
+}
+
+void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
+{
+ // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
+ // instead of one QByteArray.
+ for(int i = 0; i < data.bufferCount(); i++) {
+ QByteArray &byteData = data[i];
+ compressedData.append(byteData.constData(), byteData.size());
+ }
+ data.clear();
+}
+
+
// SSL support below
#ifndef QT_NO_OPENSSL
@@ -708,6 +740,13 @@ void QHttpNetworkReply::ignoreSslErrors()
d->connection->ignoreSslErrors();
}
+void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->ignoreSslErrors(errors);
+}
+
#endif //QT_NO_OPENSSL
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index c729df9..fe49799 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -80,6 +80,7 @@ static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
#include <private/qhttpnetworkrequest_p.h>
#include <private/qauthenticator_p.h>
#include <private/qringbuffer_p.h>
+#include <private/qbytedata_p.h>
QT_BEGIN_NAMESPACE
@@ -122,7 +123,7 @@ public:
qint64 bytesAvailable() const;
qint64 bytesAvailableNextBlock() const;
- QByteArray read(qint64 maxSize = -1);
+ QByteArray readAny();
bool isFinished() const;
@@ -130,6 +131,7 @@ public:
QSslConfiguration sslConfiguration() const;
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors();
+ void ignoreSslErrors(const QList<QSslError> &errors);
Q_SIGNALS:
void sslErrors(const QList<QSslError> &errors);
@@ -144,9 +146,10 @@ Q_SIGNALS:
void dataSendProgress(qint64 done, qint64 total);
private:
- Q_DECLARE_SCOPED_PRIVATE(QHttpNetworkReply)
+ Q_DECLARE_PRIVATE(QHttpNetworkReply)
friend class QHttpNetworkConnection;
friend class QHttpNetworkConnectionPrivate;
+ friend class QHttpNetworkConnectionChannel;
};
@@ -159,16 +162,20 @@ public:
bool parseStatus(const QByteArray &status);
qint64 readHeader(QAbstractSocket *socket);
void parseHeader(const QByteArray &header);
- qint64 readBody(QAbstractSocket *socket, QIODevice *out);
- qint64 readBodyFast(QAbstractSocket *socket, QRingBuffer *rb);
+ qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
+ qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
bool findChallenge(bool forProxy, QByteArray &challenge) const;
QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
void clear();
- qint64 readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size);
- qint64 readReplyBodyChunked(QIODevice *in, QIODevice *out);
+ qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
+ void appendUncompressedReplyData(QByteArray &qba);
+ void appendUncompressedReplyData(QByteDataBuffer &data);
+ void appendCompressedReplyData(QByteDataBuffer &data);
+
qint64 bytesAvailable() const;
bool isChunked();
bool connectionCloseEnabled();
@@ -197,6 +204,7 @@ public:
qint64 contentRead;
qint64 totalProgress;
QByteArray fragment; // used for header, status, chunk header etc, not for reply data
+ bool chunkedTransferEncoding;
qint64 currentChunkSize;
qint64 currentChunkRead;
QPointer<QHttpNetworkConnection> connection;
@@ -207,7 +215,7 @@ public:
#endif
bool autoDecompress;
- QRingBuffer responseData; // uncompressed body
+ QByteDataBuffer responseData; // uncompressed body
QByteArray compressedData; // compressed body (temporary)
bool requestIsPrepared;
};
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 88ae894..caaa38e 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -165,6 +165,12 @@ void QNetworkAccessBackend::ignoreSslErrors()
// do nothing
}
+void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ Q_UNUSED(errors);
+ // do nothing
+}
+
void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
{
// do nothing
@@ -217,9 +223,9 @@ qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
return reply->nextDownstreamBlockSize();
}
-void QNetworkAccessBackend::writeDownstreamData(const QByteArray &data)
+void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
{
- reply->appendDownstreamData(data);
+ reply->appendDownstreamData(list);
}
void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 21cb4a6..27da5bc 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -118,6 +118,7 @@ public:
virtual void downstreamReadyWrite();
virtual void copyFinished(QIODevice *);
virtual void ignoreSslErrors();
+ virtual void ignoreSslErrors(const QList<QSslError> &errors);
virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
virtual void setSslConfiguration(const QSslConfiguration &configuration);
@@ -166,7 +167,7 @@ protected:
// these functions control the downstream mechanism
// that is, data that has come via the connection and is going out the backend
qint64 nextDownstreamBlockSize() const;
- void writeDownstreamData(const QByteArray &data);
+ void writeDownstreamData(QByteDataBuffer &list);
public slots:
// for task 251801, needs to be a slot to be called asynchronously
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index f46a50a..8571ba3 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -86,6 +86,20 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
+ // set the raw headers
+ QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
+ QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
+ end = rawHeaders.constEnd();
+ for ( ; it != end; ++it)
+ setRawHeader(it->first, it->second);
+
+ // handle a possible redirect
+ QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirectionTarget.isValid()) {
+ setAttribute(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
+ redirectionRequested(redirectionTarget.toUrl());
+ }
+
// signal we're open
metaDataChanged();
diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp
index 609f0c5..4436cf4 100644
--- a/src/network/access/qnetworkaccessdatabackend.cpp
+++ b/src/network/access/qnetworkaccessdatabackend.cpp
@@ -117,7 +117,11 @@ void QNetworkAccessDataBackend::open()
setHeader(QNetworkRequest::ContentLengthHeader, payload.size());
emit metaDataChanged();
- writeDownstreamData(payload);
+ QByteDataBuffer list;
+ list.append(payload);
+ payload.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
+
finished();
return;
}
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index ce768f5..394e196 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -155,7 +155,11 @@ void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
// have read something
buffer.resize(haveRead);
bytesDownloaded += haveRead;
- writeDownstreamData(buffer);
+
+ QByteDataBuffer list;
+ list.append(buffer);
+ buffer.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
}
}
}
@@ -276,7 +280,7 @@ void QNetworkAccessDebugPipeBackend::socketConnected()
bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms)
{
- Q_UNUSED(ms);
+ Q_UNUSED(ms);
qCritical("QNetworkAccess: Debug pipe backend does not support waitForReadyRead()");
return false;
}
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index e3fc8bf..533fc75 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -263,7 +263,11 @@ bool QNetworkAccessFileBackend::readMoreFromFile()
data.resize(actuallyRead);
totalBytes += actuallyRead;
- writeDownstreamData(data);
+
+ QByteDataBuffer list;
+ list.append(data);
+ data.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
}
return true;
}
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index d6276a3..911b31a 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -355,7 +355,11 @@ void QNetworkAccessFtpBackend::ftpDone()
void QNetworkAccessFtpBackend::ftpReadyRead()
{
- writeDownstreamData(ftp->readAll());
+ QByteArray data = ftp->readAll();
+ QByteDataBuffer list;
+ list.append(data);
+ data.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
}
void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text)
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index 4bc36db..293f857 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -294,7 +294,7 @@ public:
QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
: QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0)
#ifndef QT_NO_OPENSSL
- , pendingSslConfiguration(0), pendingIgnoreSslErrors(false)
+ , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
#endif
{
}
@@ -521,8 +521,9 @@ void QNetworkAccessHttpBackend::postRequest()
#ifndef QT_NO_OPENSSL
if (pendingSslConfiguration)
httpReply->setSslConfiguration(*pendingSslConfiguration);
- if (pendingIgnoreSslErrors)
+ if (pendingIgnoreAllSslErrors)
httpReply->ignoreSslErrors();
+ httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
#endif
connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
@@ -649,16 +650,19 @@ void QNetworkAccessHttpBackend::readFromHttp()
if (!httpReply)
return;
- // We implement the download rate control
- // Don't read from QHttpNetworkAccess more than QNetworkAccessBackend wants
- // One of the two functions above will be called when we can read again
+ // We read possibly more than nextDownstreamBlockSize(), but
+ // this is not a critical thing since it is already in the
+ // memory anyway
- qint64 bytesToRead = qBound<qint64>(0, httpReply->bytesAvailable(), nextDownstreamBlockSize());
- if (!bytesToRead)
- return;
+ QByteDataBuffer list;
+
+ while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) {
+ QByteArray data = httpReply->readAny();
+ list.append(data);
+ }
- QByteArray data = httpReply->read(bytesToRead);
- writeDownstreamData(data);
+ if (!list.isEmpty())
+ writeDownstreamData(list);
}
void QNetworkAccessHttpBackend::replyFinished()
@@ -885,7 +889,18 @@ void QNetworkAccessHttpBackend::ignoreSslErrors()
if (httpReply)
httpReply->ignoreSslErrors();
else
- pendingIgnoreSslErrors = true;
+ pendingIgnoreAllSslErrors = true;
+}
+
+void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ if (httpReply) {
+ httpReply->ignoreSslErrors(errors);
+ } else {
+ // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+ // is called before QNetworkAccessManager::get() (or post(), etc.)
+ pendingIgnoreSslErrorsList = errors;
+ }
}
void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
index dec69d0..968f4a5 100644
--- a/src/network/access/qnetworkaccesshttpbackend_p.h
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -85,6 +85,7 @@ public:
virtual void copyFinished(QIODevice *);
#ifndef QT_NO_OPENSSL
virtual void ignoreSslErrors();
+ virtual void ignoreSslErrors(const QList<QSslError> &errors);
virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
virtual void setSslConfiguration(const QSslConfiguration &configuration);
@@ -112,7 +113,8 @@ private:
#ifndef QT_NO_OPENSSL
QSslConfiguration *pendingSslConfiguration;
- bool pendingIgnoreSslErrors;
+ bool pendingIgnoreAllSslErrors;
+ QList<QSslError> pendingIgnoreSslErrorsList;
#endif
void disconnectFromHttp();
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index cd736c8..8569ecd 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -121,7 +121,9 @@ static void ensureInitialized()
as well as meta-data (headers, etc.).
\note After the request has finished, it is the responsibility of the user
- to delete the QNetworkReply object at an appropriate time.
+ to delete the QNetworkReply object at an appropriate time. Do not directly
+ delete it inside the slot connected to finished(). You can use the
+ deleteLater() function.
A more involved example, assuming the manager is already existent,
can be:
@@ -202,6 +204,9 @@ static void ensureInitialized()
See QNetworkReply::finished() for information on the status that
the object will be in.
+ \note Do not delete the \a reply object in the slot connected to this
+ signal. Use deleteLater().
+
\sa QNetworkReply::finished(), QNetworkReply::error()
*/
@@ -540,10 +545,10 @@ void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
}
/*!
- This function is used to post a request to obtain the network
- headers for \a request. It takes its name after the HTTP request
- associated (HEAD). It returns a new QNetworkReply object which
- will contain such headers.
+ Posts a request to obtain the network headers for \a request
+ and returns a new QNetworkReply object which will contain such headers
+
+ The function is named after the HTTP request associated (HEAD).
*/
QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
{
@@ -551,11 +556,12 @@ QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
}
/*!
- This function is used to post a request to obtain the contents of
- the target \a request. It will cause the contents to be
- downloaded, along with the headers associated with it. It returns
- a new QNetworkReply object opened for reading which emits its
- QIODevice::readyRead() signal whenever new data arrives.
+ Posts a request to obtain the contents of the target \a request
+ and returns a new QNetworkReply object opened for reading which emits the
+ \l{QIODevice::readyRead()}{readyRead()} signal whenever new data
+ arrives.
+
+ The contents as well as associated headers will be downloaded.
\sa post(), put(), deleteResource()
*/
@@ -565,18 +571,15 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
}
/*!
- This function is used to send an HTTP POST request to the
- destination specified by \a request. The contents of the \a data
+ Sends an HTTP POST request to the destination specified by \a request
+ and returns a new QNetworkReply object opened for reading that will
+ contain the reply sent by the server. The contents of the \a data
device will be uploaded to the server.
- \a data must be opened for reading when this function is called
- and must remain valid until the finished() signal is emitted for
- this reply.
-
- The returned QNetworkReply object will be open for reading and
- will contain the reply sent by the server to the POST request.
+ \a data must be open for reading and must remain valid until the
+ finished() signal is emitted for this reply.
- Note: sending a POST request on protocols other than HTTP and
+ \note Sending a POST request on protocols other than HTTP and
HTTPS is undefined and will probably fail.
\sa get(), put(), deleteResource()
@@ -588,8 +591,9 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODe
/*!
\overload
- This function sends the contents of the \a data byte array to the
- destination specified by \a request.
+
+ Sends the contents of the \a data byte array to the destination
+ specified by \a request.
*/
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
{
@@ -603,20 +607,19 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
}
/*!
- This function is used to upload the contents of \a data to the
- destination \a request.
+ Uploads the contents of \a data to the destination \a request and
+ returnes a new QNetworkReply object that will be open for reply.
\a data must be opened for reading when this function is called
and must remain valid until the finished() signal is emitted for
this reply.
- The returned QNetworkReply object will be open for reply, but
- whether anything will be available for reading is protocol
- dependent. For HTTP, the server may send a small HTML page
- indicating the upload was successful (or not). Other protocols
- will probably have content in their replies.
+ Whether anything will be available for reading from the returned
+ object is protocol dependent. For HTTP, the server may send a
+ small HTML page indicating the upload was successful (or not).
+ Other protocols will probably have content in their replies.
- For HTTP, this request will send a PUT request, which most servers
+ \note For HTTP, this request will send a PUT request, which most servers
do not allow. Form upload mechanisms, including that of uploading
files through HTML forms, use the POST mechanism.
@@ -629,8 +632,8 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODev
/*!
\overload
- This function sends the contents of the \a data byte array to the
- destination specified by \a request.
+ Sends the contents of the \a data byte array to the destination
+ specified by \a request.
*/
QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
{
@@ -646,9 +649,10 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const
/*!
\since 4.6
- This function is used to send a request to delete the resource
- identified by the URL of \a request.
- This feature is currently available for HTTP only, performing an HTTP DELETE request.
+ Sends a request to delete the resource identified by the URL of \a request.
+
+ \note This feature is currently available for HTTP only, performing an
+ HTTP DELETE request.
\sa get(), post(), put()
*/
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 01f09cf..f7967f6 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -119,7 +119,7 @@ protected:
private:
friend class QNetworkReplyImplPrivate;
- Q_DECLARE_SCOPED_PRIVATE(QNetworkAccessManager)
+ Q_DECLARE_PRIVATE(QNetworkAccessManager)
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>))
};
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index c039703..dd5edb0 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -1068,259 +1068,4 @@ QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
}
#endif
-
-
-class QNetworkCookieJarPrivate: public QObjectPrivate
-{
-public:
- QList<QNetworkCookie> allCookies;
-
- Q_DECLARE_PUBLIC(QNetworkCookieJar)
-};
-
-/*!
- \class QNetworkCookieJar
- \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
- \since 4.4
-
- Cookies are small bits of information that stateless protocols
- like HTTP use to maintain some persistent information across
- requests.
-
- A cookie is set by a remote server when it replies to a request
- and it expects the same cookie to be sent back when further
- requests are sent.
-
- The cookie jar is the object that holds all cookies set in
- previous requests. Web browsers save their cookie jars to disk in
- order to conserve permanent cookies across invocations of the
- application.
-
- QNetworkCookieJar does not implement permanent storage: it only
- keeps the cookies in memory. Once the QNetworkCookieJar object is
- deleted, all cookies it held will be discarded as well. If you
- want to save the cookies, you should derive from this class and
- implement the saving to disk to your own storage format.
-
- This class implements only the basic security recommended by the
- cookie specifications and does not implement any cookie acceptance
- policy (it accepts all cookies set by any requests). In order to
- override those rules, you should reimplement the
- cookiesForUrl() and setCookiesFromUrl() virtual
- functions. They are called by QNetworkReply and
- QNetworkAccessManager when they detect new cookies and when they
- require cookies.
-
- \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
- QNetworkRequest, QNetworkAccessManager::setCookieJar()
-*/
-
-/*!
- Creates a QNetworkCookieJar object and sets the parent object to
- be \a parent.
-
- The cookie jar is initialized to empty.
-*/
-QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
- : QObject(*new QNetworkCookieJarPrivate, parent)
-{
-}
-
-/*!
- Destroys this cookie jar object and discards all cookies stored in
- it. Cookies are not saved to disk in the QNetworkCookieJar default
- implementation.
-
- If you need to save the cookies to disk, you have to derive from
- QNetworkCookieJar and save the cookies to disk yourself.
-*/
-QNetworkCookieJar::~QNetworkCookieJar()
-{
-}
-
-/*!
- Returns all cookies stored in this cookie jar. This function is
- suitable for derived classes to save cookies to disk, as well as
- to implement cookie expiration and other policies.
-
- \sa setAllCookies(), cookiesForUrl()
-*/
-QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
-{
- return d_func()->allCookies;
-}
-
-/*!
- Sets the internal list of cookies held by this cookie jar to be \a
- cookieList. This function is suitable for derived classes to
- implement loading cookies from permanent storage, or their own
- cookie acceptance policies by reimplementing
- setCookiesFromUrl().
-
- \sa allCookies(), setCookiesFromUrl()
-*/
-void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
-{
- Q_D(QNetworkCookieJar);
- d->allCookies = cookieList;
-}
-
-static inline bool isParentPath(QString path, QString reference)
-{
- if (!path.endsWith(QLatin1Char('/')))
- path += QLatin1Char('/');
- if (!reference.endsWith(QLatin1Char('/')))
- reference += QLatin1Char('/');
- return path.startsWith(reference);
-}
-
-static inline bool isParentDomain(QString domain, QString reference)
-{
- if (!reference.startsWith(QLatin1Char('.')))
- return domain == reference;
-
- return domain.endsWith(reference) || domain == reference.mid(1);
-}
-
-/*!
- Adds the cookies in the list \a cookieList to this cookie
- jar. Default values for path and domain are taken from the \a
- url object.
-
- Returns true if one or more cookes are set for url otherwise false.
-
- If a cookie already exists in the cookie jar, it will be
- overridden by those in \a cookieList.
-
- The default QNetworkCookieJar class implements only a very basic
- security policy (it makes sure that the cookies' domain and path
- match the reply's). To enhance the security policy with your own
- algorithms, override setCookiesFromUrl().
-
- Also, QNetworkCookieJar does not have a maximum cookie jar
- size. Reimplement this function to discard older cookies to create
- room for new ones.
-
- \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
-*/
-bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
- const QUrl &url)
-{
- Q_D(QNetworkCookieJar);
- QString defaultDomain = url.host();
- QString pathAndFileName = url.path();
- QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
- if (defaultPath.isEmpty())
- defaultPath = QLatin1Char('/');
-
- int added = 0;
- QDateTime now = QDateTime::currentDateTime();
- foreach (QNetworkCookie cookie, cookieList) {
- bool isDeletion = !cookie.isSessionCookie() &&
- cookie.expirationDate() < now;
-
- // validate the cookie & set the defaults if unset
- if (cookie.path().isEmpty())
- cookie.setPath(defaultPath);
- else if (!isParentPath(pathAndFileName, cookie.path()))
- continue; // not accepted
-
- if (cookie.domain().isEmpty()) {
- cookie.setDomain(defaultDomain);
- } else {
- QString domain = cookie.domain();
- if (!(isParentDomain(domain, defaultDomain)
- || isParentDomain(defaultDomain, domain))) {
- continue; // not accepted
- }
-
- // reject if domain is like ".com"
- // (i.e., reject if domain does not contain embedded dots, see RFC 2109 section 4.3.2)
- // this is just a rudimentary check and does not cover all cases
- if (domain.lastIndexOf(QLatin1Char('.')) == 0)
- continue; // not accepted
-
- }
-
- QList<QNetworkCookie>::Iterator it = d->allCookies.begin(),
- end = d->allCookies.end();
- for ( ; it != end; ++it)
- // does this cookie already exist?
- if (cookie.name() == it->name() &&
- cookie.domain() == it->domain() &&
- cookie.path() == it->path()) {
- // found a match
- d->allCookies.erase(it);
- break;
- }
-
- // did not find a match
- if (!isDeletion) {
- d->allCookies += cookie;
- ++added;
- }
- }
- return (added > 0);
-}
-
-/*!
- Returns the cookies to be added to when a request is sent to
- \a url. This function is called by the default
- QNetworkAccessManager::createRequest(), which adds the
- cookies returned by this function to the request being sent.
-
- If more than one cookie with the same name is found, but with
- differing paths, the one with longer path is returned before the
- one with shorter path. In other words, this function returns
- cookies sorted by path length.
-
- The default QNetworkCookieJar class implements only a very basic
- security policy (it makes sure that the cookies' domain and path
- match the reply's). To enhance the security policy with your own
- algorithms, override cookiesForUrl().
-
- \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
-*/
-QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
-{
-// \b Warning! This is only a dumb implementation!
-// It does NOT follow all of the recommendations from
-// http://wp.netscape.com/newsref/std/cookie_spec.html
-// It does not implement a very good cross-domain verification yet.
-
- Q_D(const QNetworkCookieJar);
- QDateTime now = QDateTime::currentDateTime();
- QList<QNetworkCookie> result;
-
- // scan our cookies for something that matches
- QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
- end = d->allCookies.constEnd();
- for ( ; it != end; ++it) {
- if (!isParentDomain(url.host(), it->domain()))
- continue;
- if (!isParentPath(url.path(), it->path()))
- continue;
- if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
- continue;
-
- // insert this cookie into result, sorted by path
- QList<QNetworkCookie>::Iterator insertIt = result.begin();
- while (insertIt != result.end()) {
- if (insertIt->path().length() < it->path().length()) {
- // insert here
- insertIt = result.insert(insertIt, *it);
- break;
- } else {
- ++insertIt;
- }
- }
-
- // this is the shortest path yet, just append
- if (insertIt == result.end())
- result += *it;
- }
-
- return result;
-}
-
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index 0653806..35c7095 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -108,25 +108,8 @@ private:
};
Q_DECLARE_TYPEINFO(QNetworkCookie, Q_MOVABLE_TYPE);
-class QNetworkCookieJarPrivate;
-class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject
-{
- Q_OBJECT
-public:
- QNetworkCookieJar(QObject *parent = 0);
- virtual ~QNetworkCookieJar();
-
- virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
- virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
-
-protected:
- QList<QNetworkCookie> allCookies() const;
- void setAllCookies(const QList<QNetworkCookie> &cookieList);
-
-private:
- Q_DECLARE_SCOPED_PRIVATE(QNetworkCookieJar)
- Q_DISABLE_COPY(QNetworkCookieJar)
-};
+// ### Qt5 remove this include
+#include "qnetworkcookiejar.h"
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
new file mode 100644
index 0000000..03f7427
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkcookiejar.h"
+#include "qnetworkcookiejar_p.h"
+
+#include "QtNetwork/qnetworkcookie.h"
+#include "QtCore/qurl.h"
+#include "QtCore/qdatetime.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkCookieJar
+ \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
+ \since 4.4
+
+ Cookies are small bits of information that stateless protocols
+ like HTTP use to maintain some persistent information across
+ requests.
+
+ A cookie is set by a remote server when it replies to a request
+ and it expects the same cookie to be sent back when further
+ requests are sent.
+
+ The cookie jar is the object that holds all cookies set in
+ previous requests. Web browsers save their cookie jars to disk in
+ order to conserve permanent cookies across invocations of the
+ application.
+
+ QNetworkCookieJar does not implement permanent storage: it only
+ keeps the cookies in memory. Once the QNetworkCookieJar object is
+ deleted, all cookies it held will be discarded as well. If you
+ want to save the cookies, you should derive from this class and
+ implement the saving to disk to your own storage format.
+
+ This class implements only the basic security recommended by the
+ cookie specifications and does not implement any cookie acceptance
+ policy (it accepts all cookies set by any requests). In order to
+ override those rules, you should reimplement the
+ cookiesForUrl() and setCookiesFromUrl() virtual
+ functions. They are called by QNetworkReply and
+ QNetworkAccessManager when they detect new cookies and when they
+ require cookies.
+
+ \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
+ QNetworkRequest, QNetworkAccessManager::setCookieJar()
+*/
+
+/*!
+ Creates a QNetworkCookieJar object and sets the parent object to
+ be \a parent.
+
+ The cookie jar is initialized to empty.
+*/
+QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
+ : QObject(*new QNetworkCookieJarPrivate, parent)
+{
+}
+
+/*!
+ Destroys this cookie jar object and discards all cookies stored in
+ it. Cookies are not saved to disk in the QNetworkCookieJar default
+ implementation.
+
+ If you need to save the cookies to disk, you have to derive from
+ QNetworkCookieJar and save the cookies to disk yourself.
+*/
+QNetworkCookieJar::~QNetworkCookieJar()
+{
+}
+
+/*!
+ Returns all cookies stored in this cookie jar. This function is
+ suitable for derived classes to save cookies to disk, as well as
+ to implement cookie expiration and other policies.
+
+ \sa setAllCookies(), cookiesForUrl()
+*/
+QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
+{
+ return d_func()->allCookies;
+}
+
+/*!
+ Sets the internal list of cookies held by this cookie jar to be \a
+ cookieList. This function is suitable for derived classes to
+ implement loading cookies from permanent storage, or their own
+ cookie acceptance policies by reimplementing
+ setCookiesFromUrl().
+
+ \sa allCookies(), setCookiesFromUrl()
+*/
+void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
+{
+ Q_D(QNetworkCookieJar);
+ d->allCookies = cookieList;
+}
+
+static inline bool isParentPath(QString path, QString reference)
+{
+ if (!path.endsWith(QLatin1Char('/')))
+ path += QLatin1Char('/');
+ if (!reference.endsWith(QLatin1Char('/')))
+ reference += QLatin1Char('/');
+ return path.startsWith(reference);
+}
+
+static inline bool isParentDomain(QString domain, QString reference)
+{
+ if (!reference.startsWith(QLatin1Char('.')))
+ return domain == reference;
+
+ return domain.endsWith(reference) || domain == reference.mid(1);
+}
+
+/*!
+ Adds the cookies in the list \a cookieList to this cookie
+ jar. Default values for path and domain are taken from the \a
+ url object.
+
+ Returns true if one or more cookes are set for url otherwise false.
+
+ If a cookie already exists in the cookie jar, it will be
+ overridden by those in \a cookieList.
+
+ The default QNetworkCookieJar class implements only a very basic
+ security policy (it makes sure that the cookies' domain and path
+ match the reply's). To enhance the security policy with your own
+ algorithms, override setCookiesFromUrl().
+
+ Also, QNetworkCookieJar does not have a maximum cookie jar
+ size. Reimplement this function to discard older cookies to create
+ room for new ones.
+
+ \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
+*/
+bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
+ const QUrl &url)
+{
+ Q_D(QNetworkCookieJar);
+ QString defaultDomain = url.host();
+ QString pathAndFileName = url.path();
+ QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
+ if (defaultPath.isEmpty())
+ defaultPath = QLatin1Char('/');
+
+ int added = 0;
+ QDateTime now = QDateTime::currentDateTime();
+ foreach (QNetworkCookie cookie, cookieList) {
+ bool isDeletion = !cookie.isSessionCookie() &&
+ cookie.expirationDate() < now;
+
+ // validate the cookie & set the defaults if unset
+ if (cookie.path().isEmpty())
+ cookie.setPath(defaultPath);
+ else if (!isParentPath(pathAndFileName, cookie.path()))
+ continue; // not accepted
+
+ if (cookie.domain().isEmpty()) {
+ cookie.setDomain(defaultDomain);
+ } else {
+ QString domain = cookie.domain();
+ if (!(isParentDomain(domain, defaultDomain)
+ || isParentDomain(defaultDomain, domain))) {
+ continue; // not accepted
+ }
+
+ // reject if domain is like ".com"
+ // (i.e., reject if domain does not contain embedded dots, see RFC 2109 section 4.3.2)
+ // this is just a rudimentary check and does not cover all cases
+ if (domain.lastIndexOf(QLatin1Char('.')) == 0)
+ continue; // not accepted
+
+ }
+
+ QList<QNetworkCookie>::Iterator it = d->allCookies.begin(),
+ end = d->allCookies.end();
+ for ( ; it != end; ++it)
+ // does this cookie already exist?
+ if (cookie.name() == it->name() &&
+ cookie.domain() == it->domain() &&
+ cookie.path() == it->path()) {
+ // found a match
+ d->allCookies.erase(it);
+ break;
+ }
+
+ // did not find a match
+ if (!isDeletion) {
+ d->allCookies += cookie;
+ ++added;
+ }
+ }
+ return (added > 0);
+}
+
+/*!
+ Returns the cookies to be added to when a request is sent to
+ \a url. This function is called by the default
+ QNetworkAccessManager::createRequest(), which adds the
+ cookies returned by this function to the request being sent.
+
+ If more than one cookie with the same name is found, but with
+ differing paths, the one with longer path is returned before the
+ one with shorter path. In other words, this function returns
+ cookies sorted by path length.
+
+ The default QNetworkCookieJar class implements only a very basic
+ security policy (it makes sure that the cookies' domain and path
+ match the reply's). To enhance the security policy with your own
+ algorithms, override cookiesForUrl().
+
+ \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
+*/
+QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
+{
+// \b Warning! This is only a dumb implementation!
+// It does NOT follow all of the recommendations from
+// http://wp.netscape.com/newsref/std/cookie_spec.html
+// It does not implement a very good cross-domain verification yet.
+
+ Q_D(const QNetworkCookieJar);
+ QDateTime now = QDateTime::currentDateTime();
+ QList<QNetworkCookie> result;
+
+ // scan our cookies for something that matches
+ QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
+ end = d->allCookies.constEnd();
+ for ( ; it != end; ++it) {
+ if (!isParentDomain(url.host(), it->domain()))
+ continue;
+ if (!isParentPath(url.path(), it->path()))
+ continue;
+ if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
+ continue;
+
+ // insert this cookie into result, sorted by path
+ QList<QNetworkCookie>::Iterator insertIt = result.begin();
+ while (insertIt != result.end()) {
+ if (insertIt->path().length() < it->path().length()) {
+ // insert here
+ insertIt = result.insert(insertIt, *it);
+ break;
+ } else {
+ ++insertIt;
+ }
+ }
+
+ // this is the shortest path yet, just append
+ if (insertIt == result.end())
+ result += *it;
+ }
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar.h b/src/network/access/qnetworkcookiejar.h
new file mode 100644
index 0000000..fae0857
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIEJAR_H
+#define QNETWORKCOOKIEJAR_H
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+
+// ### Qt5 remove this include
+#include "qnetworkcookie.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QNetworkCookieJarPrivate;
+class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject
+{
+ Q_OBJECT
+public:
+ QNetworkCookieJar(QObject *parent = 0);
+ virtual ~QNetworkCookieJar();
+
+ virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
+ virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
+
+protected:
+ QList<QNetworkCookie> allCookies() const;
+ void setAllCookies(const QList<QNetworkCookie> &cookieList);
+
+private:
+ Q_DECLARE_PRIVATE(QNetworkCookieJar)
+ Q_DISABLE_COPY(QNetworkCookieJar)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
new file mode 100644
index 0000000..eea7eee
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIEJAR_P_H
+#define QNETWORKCOOKIEJAR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access framework. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qobject_p.h"
+#include "qnetworkcookie.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkCookieJarPrivate: public QObjectPrivate
+{
+public:
+ QList<QNetworkCookie> allCookies;
+
+ Q_DECLARE_PUBLIC(QNetworkCookieJar)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h
index 8827bfb..2d04564 100644
--- a/src/network/access/qnetworkdiskcache.h
+++ b/src/network/access/qnetworkdiskcache.h
@@ -84,7 +84,7 @@ protected:
virtual qint64 expire();
private:
- Q_DECLARE_SCOPED_PRIVATE(QNetworkDiskCache)
+ Q_DECLARE_PRIVATE(QNetworkDiskCache)
Q_DISABLE_COPY(QNetworkDiskCache)
};
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 1b0d9f5..f6649b6 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -87,6 +87,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
indicates the progress of the upload for operations that have such
content.
+ \note Do not delete the object in the slot connected to the
+ error() or finished() signal. Use deleteLater().
+
\sa QNetworkRequest, QNetworkAccessManager
*/
@@ -232,6 +235,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
QNetworkAccessManager::finished() where that signal's reply
parameter is this object.
+ \note Do not delete the object in the slot connected to this
+ signal. Use deleteLater().
+
\sa QNetworkAccessManager::finished()
*/
@@ -246,6 +252,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
detected. Call errorString() to obtain a textual representation of
the error condition.
+ \note Do not delete the object in the slot connected to this
+ signal. Use deleteLater().
+
\sa error(), errorString()
*/
@@ -442,6 +451,31 @@ QNetworkReply::NetworkError QNetworkReply::error() const
}
/*!
+ \since 4.6
+
+ Returns true when the reply has finished or was aborted.
+
+ \sa isRunning()
+*/
+bool QNetworkReply::isFinished() const
+{
+ return d_func()->isFinished();
+}
+
+/*!
+ \since 4.6
+
+ Returns true when the request is still processing and the
+ reply has not finished or was aborted yet.
+
+ \sa isFinished()
+*/
+bool QNetworkReply::isRunning() const
+{
+ return !isFinished();
+}
+
+/*!
Returns the URL of the content downloaded or uploaded. Note that
the URL may be different from that of the original request.
@@ -559,6 +593,38 @@ void QNetworkReply::setSslConfiguration(const QSslConfiguration &config)
qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
}
}
+
+/*!
+ \overload
+ \since 4.6
+
+ If this function is called, the SSL errors given in \a errors
+ will be ignored.
+
+ Note that you can set the expected certificate in the SSL error:
+ If, for instance, you want to issue a request to a server that uses
+ a self-signed certificate, consider the following snippet:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkreply.cpp 0
+
+ Multiple calls to this function will replace the list of errors that
+ were passed in previous calls.
+ You can clear the list of errors you want to ignore by calling this
+ function with an empty list.
+
+ \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
+*/
+void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ // do this cryptic trick, because we could not add a virtual method to this class later on
+ // since that breaks binary compatibility
+ int id = metaObject()->indexOfMethod("ignoreSslErrorsImplementation(QList<QSslError>)");
+ if (id != -1) {
+ QList<QSslError> copy(errors);
+ void *arr[] = { 0, &copy };
+ qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
+ }
+}
#endif
/*!
@@ -573,7 +639,7 @@ void QNetworkReply::setSslConfiguration(const QSslConfiguration &config)
sslErrors() signal, which indicates which errors were
found.
- \sa sslConfiguration(), sslErrors()
+ \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
*/
void QNetworkReply::ignoreSslErrors()
{
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 3fc2dbc..679ab71 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -116,6 +116,8 @@ public:
QNetworkAccessManager::Operation operation() const;
QNetworkRequest request() const;
NetworkError error() const;
+ bool isFinished() const;
+ bool isRunning() const;
QUrl url() const;
// "cooked" headers
@@ -132,6 +134,7 @@ public:
#ifndef QT_NO_OPENSSL
QSslConfiguration sslConfiguration() const;
void setSslConfiguration(const QSslConfiguration &configuration);
+ void ignoreSslErrors(const QList<QSslError> &errors);
#endif
public Q_SLOTS:
@@ -162,7 +165,7 @@ protected:
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
private:
- Q_DECLARE_SCOPED_PRIVATE(QNetworkReply)
+ Q_DECLARE_PRIVATE(QNetworkReply)
};
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index c8543f0..b51e3fb 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -75,6 +75,8 @@ public:
static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager)
{ reply->d_func()->manager = manager; }
+ virtual bool isFinished() const { return false; }
+
Q_DECLARE_PUBLIC(QNetworkReply)
};
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 6eacdf1..de7f8b4 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -91,6 +91,8 @@ void QNetworkReplyImplPrivate::_q_startOperation()
void QNetworkReplyImplPrivate::_q_copyReadyRead()
{
Q_Q(QNetworkReplyImpl);
+ if (state != Working)
+ return;
if (!copyDevice || !q->isOpen())
return;
@@ -101,16 +103,17 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
break;
bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
- char *ptr = readBuffer.reserve(bytesToRead);
- qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead);
+ QByteArray byteData;
+ byteData.resize(bytesToRead);
+ qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
if (bytesActuallyRead == -1) {
- readBuffer.chop(bytesToRead);
+ byteData.clear();
backendNotify(NotifyCopyFinished);
break;
}
- if (bytesActuallyRead != bytesToRead)
- readBuffer.chop(bytesToRead - bytesActuallyRead);
+ byteData.resize(bytesActuallyRead);
+ readBuffer.append(byteData);
if (!copyDevice->isSequential() && copyDevice->atEnd()) {
backendNotify(NotifyCopyFinished);
@@ -382,26 +385,33 @@ qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
if (readBufferMaxSize == 0)
return DesiredBufferSize;
- return qMax<qint64>(0, readBufferMaxSize - readBuffer.size());
+ return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
}
// we received downstream data and send this to the cache
// and to our readBuffer (which in turn gets read by the user of QNetworkReply)
-void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
+void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
{
Q_Q(QNetworkReplyImpl);
if (!q->isOpen())
return;
- char *ptr = readBuffer.reserve(data.size());
- memcpy(ptr, data.constData(), data.size());
-
if (cacheEnabled && !cacheSaveDevice) {
// save the meta data
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
metaData = backend->fetchCacheMetaData(metaData);
+
+ // save the redirect request also in the cache
+ QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirectionTarget.isValid()) {
+ QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
+ attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
+ metaData.setAttributes(attributes);
+ }
+
cacheSaveDevice = networkCache->prepare(metaData);
+
if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
if (cacheSaveDevice && !cacheSaveDevice->isOpen())
qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
@@ -414,10 +424,19 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
}
}
- if (cacheSaveDevice)
- cacheSaveDevice->write(data);
+ qint64 bytesWritten = 0;
+ for (int i = 0; i < data.bufferCount(); i++) {
+ QByteArray item = data[i];
+
+ if (cacheSaveDevice)
+ cacheSaveDevice->write(item.constData(), item.size());
+ readBuffer.append(item);
+
+ bytesWritten += item.size();
+ }
+ data.clear();
- bytesDownloaded += data.size();
+ bytesDownloaded += bytesWritten;
lastBytesDownloaded = bytesDownloaded;
QPointer<QNetworkReplyImpl> qq = q;
@@ -426,6 +445,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
pauseNotificationHandling();
emit q->downloadProgress(bytesDownloaded,
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ // important: At the point of this readyRead(), the data parameter list must be empty,
+ // else implicit sharing will trigger memcpy when the user is reading data!
emit q->readyRead();
// hopefully we haven't been deleted here
@@ -462,8 +483,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
void QNetworkReplyImplPrivate::finished()
{
Q_Q(QNetworkReplyImpl);
- Q_ASSERT_X(state != Finished, "QNetworkReplyImpl",
- "Backend called finished/finishedWithError more than once");
+ if (state == Finished || state == Aborted)
+ return;
state = Finished;
pendingNotifications.clear();
@@ -531,6 +552,11 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
#endif
}
+bool QNetworkReplyImplPrivate::isFinished() const
+{
+ return (state == Finished || state == Aborted);
+}
+
QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
: QNetworkReply(*new QNetworkReplyImplPrivate, parent)
{
@@ -596,14 +622,14 @@ void QNetworkReplyImpl::close()
*/
qint64 QNetworkReplyImpl::bytesAvailable() const
{
- return QNetworkReply::bytesAvailable() + d_func()->readBuffer.size();
+ return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
}
void QNetworkReplyImpl::setReadBufferSize(qint64 size)
{
Q_D(QNetworkReplyImpl);
if (size > d->readBufferMaxSize &&
- size == d->readBuffer.size())
+ size > d->readBuffer.byteAmount())
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
QNetworkReply::setReadBufferSize(size);
@@ -633,6 +659,12 @@ void QNetworkReplyImpl::ignoreSslErrors()
d->backend->ignoreSslErrors();
}
+void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->backend)
+ d->backend->ignoreSslErrors(errors);
+}
#endif // QT_NO_OPENSSL
/*!
@@ -651,7 +683,7 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
return 1;
}
- maxlen = qMin<qint64>(maxlen, d->readBuffer.size());
+ maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
return d->readBuffer.read(data, maxlen);
}
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index ae5038e..fba8d34 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -61,6 +61,7 @@
#include "QtCore/qqueue.h"
#include "QtCore/qbuffer.h"
#include "private/qringbuffer_p.h"
+#include "private/qbytedata_p.h"
QT_BEGIN_NAMESPACE
@@ -88,9 +89,10 @@ public:
Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const;
Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
virtual void ignoreSslErrors();
+ Q_INVOKABLE virtual void ignoreSslErrorsImplementation(const QList<QSslError> &errors);
#endif
- Q_DECLARE_SCOPED_PRIVATE(QNetworkReplyImpl)
+ Q_DECLARE_PRIVATE(QNetworkReplyImpl)
Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead())
Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished())
@@ -144,7 +146,7 @@ public:
void consume(qint64 count);
void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal);
qint64 nextDownstreamBlockSize() const;
- void appendDownstreamData(const QByteArray &data);
+ void appendDownstreamData(QByteDataBuffer &data);
void appendDownstreamData(QIODevice *data);
void finished();
void error(QNetworkReply::NetworkError code, const QString &errorString);
@@ -152,6 +154,8 @@ public:
void redirectionRequested(const QUrl &target);
void sslErrors(const QList<QSslError> &errors);
+ bool isFinished() const;
+
QNetworkAccessBackend *backend;
QIODevice *outgoingData;
QRingBuffer *outgoingDataBuffer;
@@ -170,7 +174,7 @@ public:
QList<QNetworkProxy> proxyList;
#endif
- QRingBuffer readBuffer;
+ QByteDataBuffer readBuffer;
qint64 bytesDownloaded;
qint64 lastBytesDownloaded;
qint64 bytesUploaded;
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 557143f..caa8669 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -153,9 +153,7 @@ QT_BEGIN_NAMESPACE
future uses. If the value is false, the data obtained will not
be automatically cached. If true, data may be cached, provided
it is cacheable (what is cacheable depends on the protocol
- being used). Note that the default QNetworkAccessManager
- implementation does not support caching, so it will ignore
- this attribute.
+ being used).
\value SourceIsFromCacheAttribute
Replies only, type: QVariant::Bool (default: false)