summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorShane Kearns <shane.kearns@accenture.com>2011-03-25 14:08:28 (GMT)
committerShane Kearns <shane.kearns@accenture.com>2011-03-25 14:08:28 (GMT)
commit12fd077f71a8bc6f865e4e8a4ffe61d5d1b8a504 (patch)
treeba98e1ff32f288b47b0a05eb0fc293e3c3420889 /src/network/access
parentbe5e7924ec7bd6cccd626f931f55e2687910ad13 (diff)
parent116fed06f9ed561befb505d94b6b44dc71bf9e45 (diff)
downloadQt-12fd077f71a8bc6f865e4e8a4ffe61d5d1b8a504.zip
Qt-12fd077f71a8bc6f865e4e8a4ffe61d5d1b8a504.tar.gz
Qt-12fd077f71a8bc6f865e4e8a4ffe61d5d1b8a504.tar.bz2
Merge remote branch 'earth/master' into symbian-socket-engine
Conflicts: src/network/access/qhttpnetworkconnectionchannel.cpp src/network/socket/qlocalsocket.cpp src/s60installs/bwins/QtCoreu.def src/s60installs/bwins/QtGuiu.def src/s60installs/bwins/QtTestu.def src/s60installs/eabi/QtCoreu.def src/s60installs/eabi/QtGuiu.def
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/access.pri7
-rw-r--r--src/network/access/qhttpmultipart.cpp548
-rw-r--r--src/network/access/qhttpmultipart.h119
-rw-r--r--src/network/access/qhttpmultipart_p.h182
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp37
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp68
-rw-r--r--src/network/access/qhttpnetworkreply.cpp8
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp2
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h2
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp12
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp93
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h2
-rw-r--r--src/network/access/qnetworkcookie.cpp21
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp2
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h2
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h2
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp21
-rw-r--r--src/network/access/qnetworkrequest.cpp9
-rw-r--r--src/network/access/qnetworkrequest.h3
-rw-r--r--src/network/access/qnetworkrequest_p.h3
23 files changed, 1097 insertions, 53 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 57a79b3..5ead3ad 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -34,7 +34,9 @@ HEADERS += \
access/qabstractnetworkcache.h \
access/qnetworkdiskcache_p.h \
access/qnetworkdiskcache.h \
- access/qhttpthreaddelegate_p.h
+ access/qhttpthreaddelegate_p.h \
+ access/qhttpmultipart.h \
+ access/qhttpmultipart_p.h
SOURCES += \
access/qftp.cpp \
@@ -62,6 +64,7 @@ SOURCES += \
access/qnetworkreplyfileimpl.cpp \
access/qabstractnetworkcache.cpp \
access/qnetworkdiskcache.cpp \
- access/qhttpthreaddelegate.cpp
+ access/qhttpthreaddelegate.cpp \
+ access/qhttpmultipart.cpp
include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
new file mode 100644
index 0000000..640f9ea
--- /dev/null
+++ b/src/network/access/qhttpmultipart.cpp
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
+#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
+#include "QtCore/qmutex.h"
+#include "QtCore/qthreadstorage.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHttpPart
+ \brief The QHttpPart class holds a body part to be used inside a
+ HTTP multipart MIME message.
+ \since 4.8
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QHttpPart class holds a body part to be used inside a HTTP
+ multipart MIME message (which is represented by the QHttpMultiPart class).
+ A QHttpPart consists of a header block
+ and a data block, which are separated by each other by two
+ consecutive new lines. An example for one part would be:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 0
+
+ For setting headers, use setHeader() and setRawHeader(), which behave
+ exactly like QNetworkRequest::setHeader() and QNetworkRequest::setRawHeader().
+
+ For reading small pieces of data, use setBody(); for larger data blocks
+ like e.g. images, use setBodyDevice(). The latter method saves memory by
+ not copying the data internally, but reading directly from the device.
+ This means that the device must be opened and readable at the moment when
+ the multipart message containing the body part is sent on the network via
+ QNetworkAccessManager::post().
+
+ To construct a QHttpPart with a small body, consider the following snippet
+ (this produces the data shown in the example above):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 1
+
+ To construct a QHttpPart reading from a device (e.g. a file), the following
+ can be applied:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 2
+
+ Be aware that QHttpPart does not take ownership of the device when set, so
+ it is the developer's responsibility to destroy it when it is not needed anymore.
+ A good idea might be to set the multipart message as parent object for the device,
+ as documented at the documentation for QHttpMultiPart.
+
+ \sa QHttpMultiPart, QNetworkAccessManager
+*/
+
+
+/*!
+ Constructs an empty QHttpPart object.
+*/
+QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
+{
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
+{
+}
+
+/*!
+ Destroys this QHttpPart.
+*/
+QHttpPart::~QHttpPart()
+{
+ d = 0;
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpPart &QHttpPart::operator=(const QHttpPart &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this object is the same as \a other (i.e., if they
+ have the same headers and body).
+
+ \sa operator!=()
+*/
+bool QHttpPart::operator==(const QHttpPart &other) const
+{
+ return d == other.d || *d == *other.d;
+}
+
+/*!
+ \fn bool QHttpPart::operator!=(const QHttpPart &other) const
+
+ Returns true if this object is not the same as \a other.
+
+ \sa operator==()
+*/
+
+/*!
+ Sets the value of the known header \a header to be \a value,
+ overriding any previously set headers.
+
+ \sa QNetworkRequest::KnownHeaders, setRawHeader(), QNetworkRequest::setHeader()
+*/
+void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ d->setCookedHeader(header, value);
+}
+
+/*!
+ Sets the header \a headerName to be of value \a headerValue. If \a
+ headerName corresponds to a known header (see
+ QNetworkRequest::KnownHeaders), the raw format will be parsed and
+ the corresponding "cooked" header will be set as well.
+
+ Note: setting the same header twice overrides the previous
+ setting. To accomplish the behaviour of multiple HTTP headers of
+ the same name, you should concatenate the two values, separating
+ them with a comma (",") and set one single raw header.
+
+ \sa QNetworkRequest::KnownHeaders, setHeader(), QNetworkRequest::setRawHeader()
+*/
+void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+{
+ d->setRawHeader(headerName, headerValue);
+}
+
+/*!
+ Sets the body of this MIME part to \a body. The body set with this method
+ will be used unless the device is set via setBodyDevice(). For a large
+ amount of data (e.g. an image), use setBodyDevice(), which will not copy
+ the data internally.
+
+ \sa setBodyDevice()
+*/
+void QHttpPart::setBody(const QByteArray &body)
+{
+ d->setBody(body);
+}
+
+/*!
+ Sets the device to read the content from to \a device. For large amounts of data
+ this method should be preferred over setBody(),
+ because the content is not copied when using this method, but read
+ directly from the device.
+ \a device must be open and readable. QHttpPart does not take ownership
+ of \a device, i.e. the device must be closed and destroyed if necessary.
+ if \a device is sequential (e.g. sockets, but not files),
+ QNetworkAccessManager::post() should be called after \a device has
+ emitted finished().
+ For unsetting the device and using data set via setBody(), use
+ "setBodyDevice(0)".
+
+ \sa setBody(), QNetworkAccessManager::post()
+ */
+void QHttpPart::setBodyDevice(QIODevice *device)
+{
+ d->setBodyDevice(device);
+}
+
+
+
+/*!
+ \class QHttpMultiPart
+ \brief The QHttpMultiPart class resembles a MIME multipart message to be sent over HTTP.
+ \since 4.8
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QHttpMultiPart resembles a MIME multipart message, as described in RFC 2046,
+ which is to be sent over HTTP.
+ A multipart message consists of an arbitrary number of body parts (see QHttpPart),
+ which are separated by a unique boundary. The boundary of the QHttpMultiPart is
+ constructed with the string "boundary_.oOo._" followed by random characters,
+ and provides enough uniqueness to make sure it does not occur inside the parts itself.
+ If desired, the boundary can still be set via setBoundary().
+
+ As an example, consider the following code snippet, which constructs a multipart
+ message containing a text part followed by an image part:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttpmultipart.cpp 0
+
+ \sa QHttpPart, QNetworkAccessManager::post()
+*/
+
+/*!
+ \enum QHttpMultiPart::ContentType
+
+ List of known content types for a multipart subtype as described
+ in RFC 2046 and others.
+
+ \value MixedType corresponds to the "multipart/mixed" subtype,
+ meaning the body parts are independent of each other, as described
+ in RFC 2046.
+
+ \value RelatedType corresponds to the "multipart/related" subtype,
+ meaning the body parts are related to each other, as described in RFC 2387.
+
+ \value FormDataType corresponds to the "multipart/form-data"
+ subtype, meaning the body parts contain form elements, as described in RFC 2388.
+
+ \value AlternativeType corresponds to the "multipart/alternative"
+ subtype, meaning the body parts are alternative representations of
+ the same information, as described in RFC 2046.
+
+ \sa setContentType()
+*/
+
+/*!
+ Constructs a QHttpMultiPart with content type MixedType and sets
+ parent as the parent object.
+
+ \sa QHttpMultiPart::ContentType
+*/
+QHttpMultiPart::QHttpMultiPart(QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
+{
+ Q_D(QHttpMultiPart);
+ d->contentType = MixedType;
+}
+
+/*!
+ Constructs a QHttpMultiPart with content type \a contentType and
+ sets parent as the parent object.
+
+ \sa QHttpMultiPart::ContentType
+*/
+QHttpMultiPart::QHttpMultiPart(QHttpMultiPart::ContentType contentType, QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
+{
+ Q_D(QHttpMultiPart);
+ d->contentType = contentType;
+}
+
+/*!
+ Destroys the multipart.
+*/
+QHttpMultiPart::~QHttpMultiPart()
+{
+}
+
+/*!
+ Appends \a httpPart to this multipart.
+*/
+void QHttpMultiPart::append(const QHttpPart &httpPart)
+{
+ d_func()->parts.append(httpPart);
+}
+
+/*!
+ Sets the content type to \a contentType. The content type will be used
+ in the HTTP header section when sending the multipart message via
+ QNetworkAccessManager::post().
+ In case you want to use a multipart subtype not contained in
+ QHttpMultiPart::ContentType,
+ you can add the "Content-Type" header field to the QNetworkRequest
+ by hand, and then use this request together with the multipart
+ message for posting.
+
+ \sa QHttpMultiPart::ContentType, QNetworkAccessManager::post()
+*/
+void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
+{
+ d_func()->contentType = contentType;
+}
+
+/*!
+ returns the boundary.
+
+ \sa setBoundary()
+*/
+QByteArray QHttpMultiPart::boundary() const
+{
+ return d_func()->boundary;
+}
+
+/*!
+ Sets the boundary to \a boundary.
+
+ Usually, you do not need to generate a boundary yourself; upon construction
+ the boundary is initiated with the string "boundary_.oOo._" followed by random
+ characters, and provides enough uniqueness to make sure it does not occur
+ inside the parts itself.
+
+ \sa boundary()
+*/
+void QHttpMultiPart::setBoundary(const QByteArray &boundary)
+{
+ d_func()->boundary = boundary;
+}
+
+
+
+// ------------------------------------------------------------------
+// ----------- implementations of private classes: ------------------
+// ------------------------------------------------------------------
+
+
+
+qint64 QHttpPartPrivate::bytesAvailable() const
+{
+ checkHeaderCreated();
+ qint64 bytesAvailable = header.count();
+ if (bodyDevice) {
+ bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
+ } else {
+ bytesAvailable += body.count() - readPointer;
+ }
+ // the device might have closed etc., so make sure we do not return a negative value
+ return qMax(bytesAvailable, (qint64) 0);
+}
+
+qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
+{
+ checkHeaderCreated();
+ qint64 bytesRead = 0;
+ qint64 headerDataCount = header.count();
+
+ // read header if it has not been read yet
+ if (readPointer < headerDataCount) {
+ bytesRead = qMin(headerDataCount - readPointer, maxSize);
+ const char *headerData = header.constData();
+ memcpy(data, headerData + readPointer, bytesRead);
+ readPointer += bytesRead;
+ }
+ // read content if there is still space
+ if (bytesRead < maxSize) {
+ if (bodyDevice) {
+ qint64 dataBytesRead = bodyDevice->read(data + bytesRead, maxSize - bytesRead);
+ if (dataBytesRead == -1)
+ return -1;
+ bytesRead += dataBytesRead;
+ readPointer += dataBytesRead;
+ } else {
+ qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, maxSize - bytesRead);
+ const char *contentData = body.constData();
+ // if this method is called several times, we need to find the
+ // right offset in the content ourselves:
+ memcpy(data + bytesRead, contentData + readPointer - headerDataCount, contentBytesRead);
+ bytesRead += contentBytesRead;
+ readPointer += contentBytesRead;
+ }
+ }
+ return bytesRead;
+}
+
+qint64 QHttpPartPrivate::size() const
+{
+ checkHeaderCreated();
+ qint64 size = header.count();
+ if (bodyDevice) {
+ size += bodyDevice->size();
+ } else {
+ size += body.count();
+ }
+ return size;
+}
+
+bool QHttpPartPrivate::reset()
+{
+ bool ret = true;
+ if (bodyDevice)
+ if (!bodyDevice->reset())
+ ret = false;
+ readPointer = 0;
+ return ret;
+}
+void QHttpPartPrivate::checkHeaderCreated() const
+{
+ if (!headerCreated) {
+ // copied from QHttpNetworkRequestPrivate::header() and adapted
+ QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
+ QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
+ for (; it != fields.constEnd(); ++it)
+ header += it->first + ": " + it->second + "\r\n";
+ header += "\r\n";
+ headerCreated = true;
+ }
+}
+
+Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage);
+
+QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
+{
+ if (!seedCreatedStorage()->hasLocalData()) {
+ qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
+ seedCreatedStorage()->setLocalData(new bool(true));
+ }
+
+ boundary = QByteArray("boundary_.oOo._")
+ + QByteArray::number(qrand()).toBase64()
+ + QByteArray::number(qrand()).toBase64()
+ + QByteArray::number(qrand()).toBase64();
+
+ // boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
+ if (boundary.count() > 70)
+ boundary = boundary.left(70);
+}
+
+qint64 QHttpMultiPartIODevice::size() const
+{
+ // if not done yet, we calculate the size and the offsets of each part,
+ // including boundary (needed later in readData)
+ if (deviceSize == -1) {
+ qint64 currentSize = 0;
+ qint64 boundaryCount = multiPart->boundary.count();
+ for (int a = 0; a < multiPart->parts.count(); a++) {
+ partOffsets.append(currentSize);
+ // 4 additional bytes for the "--" before and the "\r\n" after the boundary,
+ // and 2 bytes for the "\r\n" after the content
+ currentSize += boundaryCount + 4 + multiPart->parts.at(a).d->size() + 2;
+ }
+ currentSize += boundaryCount + 4; // size for ending boundary and 2 beginning and ending dashes
+ deviceSize = currentSize;
+ }
+ return deviceSize;
+}
+
+bool QHttpMultiPartIODevice::isSequential() const
+{
+ for (int a = 0; a < multiPart->parts.count(); a++) {
+ QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
+ // we are sequential if any of the bodyDevices of our parts are sequential;
+ // when reading from a byte array, we are not sequential
+ if (device && device->isSequential())
+ return true;
+ }
+ return false;
+}
+
+bool QHttpMultiPartIODevice::reset()
+{
+ for (int a = 0; a < multiPart->parts.count(); a++)
+ if (!multiPart->parts[a].d->reset())
+ return false;
+ return true;
+}
+qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
+{
+ qint64 bytesRead = 0, index = 0;
+
+ // skip the parts we have already read
+ while (index < multiPart->parts.count() &&
+ readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size())
+ index++;
+
+ // read the data
+ while (bytesRead < maxSize && index < multiPart->parts.count()) {
+
+ // check whether we need to read the boundary of the current part
+ QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
+ qint64 boundaryCount = boundaryData.count();
+ qint64 partIndex = readPointer - partOffsets.at(index);
+ if (partIndex < boundaryCount) {
+ qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
+ memcpy(data + bytesRead, boundaryData.constData() + partIndex, boundaryBytesRead);
+ bytesRead += boundaryBytesRead;
+ readPointer += boundaryBytesRead;
+ partIndex += boundaryBytesRead;
+ }
+
+ // check whether we need to read the data of the current part
+ if (bytesRead < maxSize && partIndex >= boundaryCount && partIndex < boundaryCount + multiPart->parts.at(index).d->size()) {
+ qint64 dataBytesRead = multiPart->parts[index].d->readData(data + bytesRead, maxSize - bytesRead);
+ if (dataBytesRead == -1)
+ return -1;
+ bytesRead += dataBytesRead;
+ readPointer += dataBytesRead;
+ partIndex += dataBytesRead;
+ }
+
+ // check whether we need to read the ending CRLF of the current part
+ if (bytesRead < maxSize && partIndex >= boundaryCount + multiPart->parts.at(index).d->size()) {
+ if (bytesRead == maxSize - 1)
+ return bytesRead;
+ memcpy(data + bytesRead, "\r\n", 2);
+ bytesRead += 2;
+ readPointer += 2;
+ index++;
+ }
+ }
+ // check whether we need to return the final boundary
+ if (bytesRead < maxSize && index == multiPart->parts.count()) {
+ QByteArray finalBoundary = "--" + multiPart->boundary + "--";
+ qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
+ qint64 lastBoundaryBytesRead = qMin(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
+ memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
+ bytesRead += lastBoundaryBytesRead;
+ readPointer += lastBoundaryBytesRead;
+ }
+ return bytesRead;
+}
+
+qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ return -1;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
new file mode 100644
index 0000000..0a3342c
--- /dev/null
+++ b/src/network/access/qhttpmultipart.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPMULTIPART_H
+#define QHTTPMULTIPART_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QByteArray>
+#include <QtNetwork/QNetworkRequest>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHttpPartPrivate;
+class QHttpMultiPart;
+
+class Q_NETWORK_EXPORT QHttpPart
+{
+public:
+ QHttpPart();
+ QHttpPart(const QHttpPart &other);
+ ~QHttpPart();
+ QHttpPart &operator=(const QHttpPart &other);
+ bool operator==(const QHttpPart &other) const;
+ inline bool operator!=(const QHttpPart &other) const
+ { return !operator==(other); }
+
+ void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue);
+
+ void setBody(const QByteArray &body);
+ void setBodyDevice(QIODevice *device);
+
+private:
+ QSharedDataPointer<QHttpPartPrivate> d;
+
+ friend class QHttpMultiPartIODevice;
+};
+
+class QHttpMultiPartPrivate;
+
+class Q_NETWORK_EXPORT QHttpMultiPart : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum ContentType {
+ MixedType,
+ RelatedType,
+ FormDataType,
+ AlternativeType
+ };
+
+ QHttpMultiPart(QObject *parent = 0);
+ QHttpMultiPart(ContentType contentType, QObject *parent = 0);
+ ~QHttpMultiPart();
+
+ void append(const QHttpPart &httpPart);
+
+ void setContentType(ContentType contentType);
+
+ QByteArray boundary() const;
+ void setBoundary(const QByteArray &boundary);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpMultiPart)
+ Q_DISABLE_COPY(QHttpMultiPart)
+
+ friend class QNetworkAccessManager;
+ friend class QNetworkAccessManagerPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHTTPMULTIPART_H
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
new file mode 100644
index 0000000..7dc13e9
--- /dev/null
+++ b/src/network/access/qhttpmultipart_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPMULTIPART_P_H
+#define QHTTPMULTIPART_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 API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qshareddata.h"
+#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate
+{
+public:
+ inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0)
+ {
+ }
+ ~QHttpPartPrivate()
+ {
+ }
+
+
+ QHttpPartPrivate(const QHttpPartPrivate &other)
+ : QSharedData(other), QNetworkHeadersPrivate(other), body(other.body),
+ header(other.header), headerCreated(other.headerCreated), readPointer(other.readPointer)
+ {
+ bodyDevice = other.bodyDevice;
+ }
+
+ inline bool operator==(const QHttpPartPrivate &other) const
+ {
+ return rawHeaders == other.rawHeaders && body == other.body &&
+ bodyDevice == other.bodyDevice && readPointer == other.readPointer;
+ }
+
+ void setBodyDevice(QIODevice *device) {
+ bodyDevice = device;
+ readPointer = 0;
+ }
+ void setBody(const QByteArray &newBody) {
+ body = newBody;
+ readPointer = 0;
+ }
+
+ // QIODevice-style methods called by QHttpMultiPartIODevice (but this class is
+ // not a QIODevice):
+ qint64 bytesAvailable() const;
+ qint64 readData(char *data, qint64 maxSize);
+ qint64 size() const;
+ bool reset();
+
+ QByteArray body;
+ QIODevice *bodyDevice;
+
+private:
+ void checkHeaderCreated() const;
+
+ mutable QByteArray header;
+ mutable bool headerCreated;
+ qint64 readPointer;
+};
+
+
+
+class QHttpMultiPartPrivate;
+
+class Q_AUTOTEST_EXPORT QHttpMultiPartIODevice : public QIODevice
+{
+public:
+ QHttpMultiPartIODevice(QHttpMultiPartPrivate *parentMultiPart) :
+ QIODevice(), multiPart(parentMultiPart), readPointer(0), deviceSize(-1) {
+ }
+
+ ~QHttpMultiPartIODevice() {
+ }
+
+ virtual bool atEnd() const {
+ return readPointer == size();
+ }
+
+ virtual qint64 bytesAvailable() const {
+ return size() - readPointer;
+ }
+
+ virtual void close() {
+ readPointer = 0;
+ partOffsets.clear();
+ deviceSize = -1;
+ QIODevice::close();
+ }
+
+ virtual qint64 bytesToWrite() const {
+ return 0;
+ }
+
+ virtual qint64 size() const;
+ virtual bool isSequential() const;
+ virtual bool reset();
+ virtual qint64 readData(char *data, qint64 maxSize);
+ virtual qint64 writeData(const char *data, qint64 maxSize);
+
+ QHttpMultiPartPrivate *multiPart;
+ qint64 readPointer;
+ mutable QList<qint64> partOffsets;
+ mutable qint64 deviceSize;
+};
+
+
+
+class QHttpMultiPartPrivate: public QObjectPrivate
+{
+public:
+
+ QHttpMultiPartPrivate();
+
+ ~QHttpMultiPartPrivate()
+ {
+ delete device;
+ }
+
+ QList<QHttpPart> parts;
+ QByteArray boundary;
+ QHttpMultiPart::ContentType contentType;
+ QHttpMultiPartIODevice *device;
+
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QHTTPMULTIPART_P_H
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 727b014..83156c6 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -492,7 +492,7 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
{
Q_ASSERT(socket);
@@ -505,8 +505,7 @@ void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socke
prepareRequest(messagePair);
channels[i].request = messagePair.first;
channels[i].reply = messagePair.second;
- channels[i].sendRequest();
- return;
+ return true;
}
if (!lowPriorityQueue.isEmpty()) {
@@ -516,9 +515,9 @@ void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socke
prepareRequest(messagePair);
channels[i].request = messagePair.first;
channels[i].reply = messagePair.second;
- channels[i].sendRequest();
- return;
+ return true;
}
+ return false;
}
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
@@ -789,17 +788,8 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// try to get a free AND connected socket
for (int i = 0; i < channelCount; ++i) {
if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
- dequeueAndSendRequest(channels[i].socket);
- }
- }
-
- // return fast if there is nothing to do
- if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
- return;
- // try to get a free unconnected socket
- for (int i = 0; i < channelCount; ++i) {
- if (!channels[i].reply && !channels[i].isSocketBusy()) {
- dequeueAndSendRequest(channels[i].socket);
+ if (dequeueRequest(channels[i].socket))
+ channels[i].sendRequest();
}
}
@@ -816,6 +806,21 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
for (int i = 0; i < channelCount; i++)
if (channels[i].socket->state() == QAbstractSocket::ConnectedState)
fillPipeline(channels[i].socket);
+
+ // If there is not already any connected channels we need to connect a new one.
+ // We do not pair the channel with the request until we know if it is
+ // connected or not. This is to reuse connected channels before we connect new once.
+ int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].socket->state() == QAbstractSocket::ConnectingState)
+ queuedRequest--;
+ if ( queuedRequest <=0 )
+ break;
+ if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
+ channels[i].ensureConnection();
+ queuedRequest--;
+ }
+ }
}
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index ad53dec..adb779f4 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -167,7 +167,7 @@ public:
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
- void dequeueAndSendRequest(QAbstractSocket *socket);
+ bool dequeueRequest(QAbstractSocket *socket);
void prepareRequest(HttpMessagePair &request);
void fillPipeline(QAbstractSocket *socket);
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 2292a7f..a6d6172 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -100,8 +100,10 @@ void QHttpNetworkConnectionChannel::init()
if (networkSession)
socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
+#ifndef QT_NO_NETWORKPROXY
// Set by QNAM anyway, but let's be safe here
socket->setProxy(QNetworkProxy::NoProxy);
+#endif
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
this, SLOT(_q_bytesWritten(qint64)),
@@ -511,6 +513,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// called when unexpectedly reading a -1 or when data is expected but socket is closed
void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
{
+ Q_ASSERT(reply);
if (reconnectAttempts <= 0) {
// too many errors reading/receiving/parsing the status, close the socket and emit error
requeueCurrentlyPipelinedRequests();
@@ -533,7 +536,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// resend this request after we receive the disconnected signal
if (socketState == QAbstractSocket::ClosingState) {
- resendCurrent = true;
+ if (reply)
+ resendCurrent = true;
return false;
}
@@ -593,13 +597,15 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
#endif
} else {
// In case of no proxy we can use the Unbuffered QTcpSocket
+#ifndef QT_NO_NETWORKPROXY
if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
&& connection->cacheProxy().type() == QNetworkProxy::NoProxy
&& connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
+#endif
socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered);
// For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
socket->setReadBufferSize(1*1024);
-
+#ifndef QT_NO_NETWORKPROXY
} else {
socket->connectToHost(connectHost, connectPort);
@@ -608,6 +614,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// here and there.
socket->setReadBufferSize(64*1024);
}
+#endif
}
return false;
}
@@ -655,6 +662,7 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
void QHttpNetworkConnectionChannel::allDone()
{
+ Q_ASSERT(reply);
#ifndef QT_NO_COMPRESS
// expand the whole data.
if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) {
@@ -750,6 +758,7 @@ void QHttpNetworkConnectionChannel::allDone()
void QHttpNetworkConnectionChannel::detectPipeliningSupport()
{
+ Q_ASSERT(reply);
// detect HTTP Pipelining support
QByteArray serverHeaderField;
if (
@@ -833,6 +842,7 @@ void QHttpNetworkConnectionChannel::handleStatus()
bool QHttpNetworkConnectionChannel::resetUploadData()
{
+ Q_ASSERT(reply);
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
if (!uploadByteDevice)
return true;
@@ -890,7 +900,8 @@ void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
{
requeueCurrentlyPipelinedRequests();
close();
- resendCurrent = true;
+ if (reply)
+ resendCurrent = true;
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -918,15 +929,18 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
//private slots
void QHttpNetworkConnectionChannel::_q_readyRead()
{
- // We got a readyRead but no bytes are available..
- // This happens for the Unbuffered QTcpSocket
- // Also check if socket is in ConnectedState since
- // this function may also be invoked via the event loop.
if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
+ // We got a readyRead but no bytes are available..
+ // This happens for the Unbuffered QTcpSocket
+ // Also check if socket is in ConnectedState since
+ // this function may also be invoked via the event loop.
char c;
qint64 ret = socket->peek(&c, 1);
if (ret < 0) {
- socket->disconnectFromHost();
+ _q_error(socket->error());
+ // We still need to handle the reply so it emits its signals etc.
+ if (reply)
+ _q_receiveReply();
return;
}
}
@@ -992,10 +1006,10 @@ void QHttpNetworkConnectionChannel::_q_connected()
//channels[i].reconnectAttempts = 2;
if (!pendingEncrypt) {
state = QHttpNetworkConnectionChannel::IdleState;
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
if (reply)
sendRequest();
- else
- close();
}
}
@@ -1023,8 +1037,20 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
} else {
errorCode = QNetworkReply::RemoteHostClosedError;
}
+ } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
+ if (!reply->d_func()->expectContent()) {
+ // No content expected, this is a valid way to have the connection closed by the server
+ return;
+ }
+ if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
+ // There was no content-length header and it's not chunked encoding,
+ // so this is a valid way to have the connection closed by the server
+ return;
+ }
+ // ok, we got a disconnect even though we did not expect it
+ errorCode = QNetworkReply::RemoteHostClosedError;
} else {
- return;
+ errorCode = QNetworkReply::RemoteHostClosedError;
}
break;
case QAbstractSocket::SocketTimeoutError:
@@ -1049,9 +1075,13 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
QPointer<QHttpNetworkConnection> that = connection;
QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
+ // Need to dequeu the request so that we can emit the error.
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
if (reply) {
reply->d_func()->errorString = errorString;
emit reply->finishedWithError(errorCode, errorString);
+ reply = 0;
}
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
@@ -1063,7 +1093,11 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
- connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
+ // Need to dequeue the request before we can emit the error.
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
}
#endif
@@ -1079,7 +1113,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
return; // ### error
state = QHttpNetworkConnectionChannel::IdleState;
pendingEncrypt = false;
- sendRequest();
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1090,7 +1127,10 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
// Also pause the connection because socket notifiers may fire while an user
// dialog is displaying
connection->d_func()->pauseConnection();
- emit reply->sslErrors(errors);
+ if (pendingEncrypt && !reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ emit reply->sslErrors(errors);
connection->d_func()->resumeConnection();
}
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 704cf3a..cb6c09f 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -886,8 +886,14 @@ bool QHttpNetworkReplyPrivate::expectContent()
return false;
if (request.operation() == QHttpNetworkRequest::Head)
return !shouldEmitSignals();
- if (contentLength() == 0)
+ qint64 expectedContentLength = contentLength();
+ if (expectedContentLength == 0)
return false;
+ if (expectedContentLength == -1 && bodyLength == 0) {
+ // The content-length header was stripped, but its value was 0.
+ // This would be the case for an explicitly zero-length compressed response.
+ return false;
+ }
return true;
}
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 6d6fadd..73277e3 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 2e86df3..752bc09 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index 8a29a31..8016ef4 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -234,6 +234,11 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
return;
}
+ // The disk cache API does not currently support partial content retrieval.
+ // That is why we don't use the disk cache for any such requests.
+ if (request().hasRawHeader("Range"))
+ return;
+
QAbstractNetworkCache *nc = networkCache();
if (!nc)
return; // no local cache
@@ -717,8 +722,13 @@ void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d)
pendingDownloadData.append(d);
d.clear();
- writeDownstreamData(pendingDownloadData);
+ // We need to usa a copy for calling writeDownstreamData as we could
+ // possibly recurse into this this function when we call
+ // appendDownstreamDataSignalEmissions because the user might call
+ // processEvents() or spin an event loop when this occur.
+ QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData;
pendingDownloadData.clear();
+ writeDownstreamData(pendingDownloadDataCopy);
}
void QNetworkAccessHttpBackend::replyFinished()
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 7a4a894..5a7521e 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -64,6 +64,8 @@
#include "QtNetwork/qauthenticator.h"
#include "QtNetwork/qsslconfiguration.h"
#include "QtNetwork/qnetworkconfigmanager.h"
+#include "QtNetwork/qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
#include "qthread.h"
@@ -629,6 +631,46 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
}
/*!
+ \since 4.8
+
+ \overload
+
+ Sends the contents of the \a multiPart message to the destination
+ specified by \a request.
+
+ This can be used for sending MIME multipart messages over HTTP.
+
+ \sa QHttpMultiPart, QHttpPart, put()
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = post(newRequest, device);
+ return reply;
+}
+
+/*!
+ \since 4.8
+
+ \overload
+
+ Sends the contents of the \a multiPart message to the destination
+ specified by \a request.
+
+ This can be used for sending MIME multipart messages over HTTP.
+
+ \sa QHttpMultiPart, QHttpPart, post()
+*/
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = put(newRequest, device);
+ return reply;
+}
+
+/*!
Uploads the contents of \a data to the destination \a request and
returnes a new QNetworkReply object that will be open for reply.
@@ -654,7 +696,8 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODev
/*!
\overload
- Sends the contents of the \a data byte array to the destination
+
+ 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)
@@ -1203,6 +1246,54 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
}
#endif // QT_NO_BEARERMANAGEMENT
+QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ // copy the request, we probably need to add some headers
+ QNetworkRequest newRequest(request);
+
+ // add Content-Type header if not there already
+ if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ QByteArray contentType;
+ contentType.reserve(34 + multiPart->d_func()->boundary.count());
+ contentType += "multipart/";
+ switch (multiPart->d_func()->contentType) {
+ case QHttpMultiPart::RelatedType:
+ contentType += "related";
+ break;
+ case QHttpMultiPart::FormDataType:
+ contentType += "form-data";
+ break;
+ case QHttpMultiPart::AlternativeType:
+ contentType += "alternative";
+ break;
+ default:
+ contentType += "mixed";
+ break;
+ }
+ // putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
+ contentType += "; boundary=\"" + multiPart->d_func()->boundary + "\"";
+ newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
+ }
+
+ // add MIME-Version header if not there already (we must include the header
+ // if the message conforms to RFC 2045, see section 4 of that RFC)
+ QByteArray mimeHeader("MIME-Version");
+ if (!request.hasRawHeader(mimeHeader))
+ newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
+
+ QIODevice *device = multiPart->d_func()->device;
+ if (!device->isReadable()) {
+ if (!device->isOpen()) {
+ if (!device->open(QIODevice::ReadOnly))
+ qWarning("could not open device for reading");
+ } else {
+ qWarning("device is not readable");
+ }
+ }
+
+ return newRequest;
+}
+
QT_END_NAMESPACE
#include "moc_qnetworkaccessmanager.cpp"
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index d67b8ac..47760b2 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -65,6 +65,7 @@ class QSslError;
#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
class QNetworkConfiguration;
#endif
+class QHttpMultiPart;
class QNetworkReplyImplPrivate;
class QNetworkAccessManagerPrivate;
@@ -116,8 +117,10 @@ public:
QNetworkReply *get(const QNetworkRequest &request);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = 0);
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index e0817ca..f64cc4d 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -120,6 +120,8 @@ public:
void _q_networkSessionStateChanged(QNetworkSession::State state);
#endif
+ QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
+
// this is the cache for storing downloaded files
QAbstractNetworkCache *networkCache;
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 70ea5c2..c2a6925 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -359,7 +359,7 @@ void QNetworkCookie::setValue(const QByteArray &value)
}
// ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
-static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position)
+static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
{
// format is one of:
// (1) token
@@ -394,13 +394,22 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
// qdtext = <any TEXT except <">>
// quoted-pair = "\" CHAR
+
+ // If its NAME=VALUE, retain the value as is
+ // refer to ttp://bugreports.qt.nokia.com/browse/QTBUG-17746
+ if (isNameValue)
+ second += '"';
++i;
while (i < length) {
register char c = text.at(i);
if (c == '"') {
// end of quoted text
+ if (isNameValue)
+ second += '"';
break;
} else if (c == '\\') {
+ if (isNameValue)
+ second += '\\';
++i;
if (i >= length)
// broken line
@@ -476,10 +485,12 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
result = d->name;
result += '=';
- if (d->value.contains(';') ||
+ if ((d->value.contains(';') ||
d->value.contains(',') ||
d->value.contains(' ') ||
- d->value.contains('"')) {
+ d->value.contains('"')) &&
+ (!d->value.startsWith('"') &&
+ !d->value.endsWith('"'))) {
result += '"';
QByteArray value = d->value;
@@ -947,7 +958,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
QNetworkCookie cookie;
// The first part is always the "NAME=VALUE" part
- QPair<QByteArray,QByteArray> field = nextField(cookieString, position);
+ QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
if (field.first.isEmpty() || field.second.isNull())
// parsing error
break;
@@ -965,7 +976,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
case ';':
// new field in the cookie
- field = nextField(cookieString, position);
+ field = nextField(cookieString, position, false);
field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
if (field.first == "expires") {
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index d35f0ce..2040b01 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -404,7 +404,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
// ### verify that QFile uses the fd size and not the file name
qint64 size = file->size() - file->pos();
const uchar *p = 0;
-#ifndef Q_OS_WINCE
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_INTEGRITY)
p = file->map(file->pos(), size);
#endif
if (p) {
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
index 52cfe95..a09ff5c 100644
--- a/src/network/access/qnetworkreplydataimpl.cpp
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
diff --git a/src/network/access/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 6c62d28..2048376 100644
--- a/src/network/access/qnetworkreplydataimpl_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index 393e3cd..c5126de 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index f5d2772..9eb505d 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -178,9 +178,11 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (preMigrationDownloaded != Q_INT64_C(-1))
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
pauseNotificationHandling();
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
+ emit q->readyRead();
emit q->downloadProgress(bytesDownloaded,
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
- emit q->readyRead();
resumeNotificationHandling();
}
@@ -514,6 +516,13 @@ void QNetworkReplyImplPrivate::initCacheSaveDevice()
{
Q_Q(QNetworkReplyImpl);
+ // The disk cache does not support partial content, so don't even try to
+ // save any such content into the cache.
+ if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
+ cacheEnabled = false;
+ return;
+ }
+
// save the meta data
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
@@ -579,11 +588,13 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
if (preMigrationDownloaded != Q_INT64_C(-1))
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
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();
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
+ emit q->downloadProgress(bytesDownloaded,
+ totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
resumeNotificationHandling();
// do we still have room in the buffer?
@@ -684,10 +695,12 @@ void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesRe
downloadBufferCurrentSize = bytesReceived;
- emit q->downloadProgress(bytesDownloaded, bytesTotal);
// Only emit readyRead when actual data is there
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
if (bytesDownloaded > 0)
emit q->readyRead();
+ emit q->downloadProgress(bytesDownloaded, bytesTotal);
}
void QNetworkReplyImplPrivate::finished()
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index a48a26f..665ee28 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -639,6 +639,9 @@ static QByteArray headerName(QNetworkRequest::KnownHeaders header)
case QNetworkRequest::SetCookieHeader:
return "Set-Cookie";
+ case QNetworkRequest::ContentDispositionHeader:
+ return "Content-Disposition";
+
// no default:
// if new values are added, this will generate a compiler warning
}
@@ -651,6 +654,7 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
switch (header) {
case QNetworkRequest::ContentTypeHeader:
case QNetworkRequest::ContentLengthHeader:
+ case QNetworkRequest::ContentDispositionHeader:
return value.toByteArray();
case QNetworkRequest::LocationHeader:
@@ -812,6 +816,11 @@ QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
return end; // not found
}
+QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
+{
+ return rawHeaders;
+}
+
QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
QList<QByteArray> result;
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index b5ef109..d3bbba7 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -65,7 +65,8 @@ public:
LocationHeader,
LastModifiedHeader,
CookieHeader,
- SetCookieHeader
+ SetCookieHeader,
+ ContentDispositionHeader // added for QMultipartMessage
};
enum Attribute {
HttpStatusCodeAttribute,
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 23705f5..ea8c56f 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
-// this is the common part between QNetworkRequestPrivate and QNetworkReplyPrivate
+// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
class QNetworkHeadersPrivate
{
public:
@@ -77,6 +77,7 @@ public:
QWeakPointer<QObject> originatingObject;
RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
+ RawHeadersList allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
void setAllRawHeaders(const RawHeadersList &list);