summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorRohan McGovern <rohan.mcgovern@nokia.com>2011-09-14 07:45:44 (GMT)
committerRohan McGovern <rohan.mcgovern@nokia.com>2011-09-14 07:45:44 (GMT)
commit1b30b3a6726adf806ae221357393e562285e8346 (patch)
treec650ff836d3abdf5890726354ee6e2b9bce11304 /src/network/access
parent1c1a5fe0e2d9e28be5e0e14732d79d34c9bb2a74 (diff)
parent704dd92581783d91ccd234d58896d7078eed14a5 (diff)
downloadQt-1b30b3a6726adf806ae221357393e562285e8346.zip
Qt-1b30b3a6726adf806ae221357393e562285e8346.tar.gz
Qt-1b30b3a6726adf806ae221357393e562285e8346.tar.bz2
Merge remote branch 'origin/4.8' into 4.8-from-4.7
Conflicts: src/gui/text/qtextengine_p.h src/network/ssl/qsslsocket_openssl.cpp
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/access.pri20
-rw-r--r--src/network/access/qftp.cpp17
-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.cpp89
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h13
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp305
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h19
-rw-r--r--src/network/access/qhttpnetworkreply.cpp119
-rw-r--r--src/network/access/qhttpnetworkreply_p.h16
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp17
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp582
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h292
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp297
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h107
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp111
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h19
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp9
-rw-r--r--src/network/access/qnetworkaccessdatabackend.cpp135
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp14
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp6
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp856
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h44
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp449
-rw-r--r--src/network/access/qnetworkaccessmanager.h5
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h14
-rw-r--r--src/network/access/qnetworkcookie.cpp11
-rw-r--r--src/network/access/qnetworkcookiejar.cpp43
-rw-r--r--src/network/access/qnetworkcookiejar_p.h3
-rw-r--r--src/network/access/qnetworkcookiejartlds_p.h6481
-rw-r--r--src/network/access/qnetworkcookiejartlds_p.h.INFO17
-rw-r--r--src/network/access/qnetworkdiskcache.cpp84
-rw-r--r--src/network/access/qnetworkdiskcache_p.h5
-rw-r--r--src/network/access/qnetworkreply.cpp18
-rw-r--r--src/network/access/qnetworkreply.h1
-rw-r--r--src/network/access/qnetworkreply_p.h3
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp148
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h (renamed from src/network/access/qnetworkaccessdatabackend_p.h)46
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp (renamed from src/network/access/qfilenetworkreply.cpp)93
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h (renamed from src/network/access/qfilenetworkreply_p.h)30
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp198
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h18
-rw-r--r--src/network/access/qnetworkrequest.cpp17
-rw-r--r--src/network/access/qnetworkrequest.h7
-rw-r--r--src/network/access/qnetworkrequest_p.h3
47 files changed, 3815 insertions, 7819 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index ce79b06..99e861e 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -8,12 +8,11 @@ HEADERS += \
access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
access/qhttpnetworkconnectionchannel_p.h \
- access/qfilenetworkreply_p.h \
+ access/qnetworkaccessauthenticationmanager_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
access/qnetworkaccesscache_p.h \
access/qnetworkaccessbackend_p.h \
- access/qnetworkaccessdatabackend_p.h \
access/qnetworkaccessdebugpipebackend_p.h \
access/qnetworkaccesshttpbackend_p.h \
access/qnetworkaccessfilebackend_p.h \
@@ -23,16 +22,20 @@ HEADERS += \
access/qnetworkcookie_p.h \
access/qnetworkcookiejar.h \
access/qnetworkcookiejar_p.h \
- access/qnetworkcookiejartlds_p.h \
access/qnetworkrequest.h \
access/qnetworkrequest_p.h \
access/qnetworkreply.h \
access/qnetworkreply_p.h \
access/qnetworkreplyimpl_p.h \
+ access/qnetworkreplydataimpl_p.h \
+ access/qnetworkreplyfileimpl_p.h \
access/qabstractnetworkcache_p.h \
access/qabstractnetworkcache.h \
access/qnetworkdiskcache_p.h \
- access/qnetworkdiskcache.h
+ access/qnetworkdiskcache.h \
+ access/qhttpthreaddelegate_p.h \
+ access/qhttpmultipart.h \
+ access/qhttpmultipart_p.h
SOURCES += \
access/qftp.cpp \
@@ -42,11 +45,10 @@ SOURCES += \
access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
access/qhttpnetworkconnectionchannel.cpp \
- access/qfilenetworkreply.cpp \
+ access/qnetworkaccessauthenticationmanager.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
access/qnetworkaccessbackend.cpp \
- access/qnetworkaccessdatabackend.cpp \
access/qnetworkaccessdebugpipebackend.cpp \
access/qnetworkaccessfilebackend.cpp \
access/qnetworkaccesscachebackend.cpp \
@@ -57,7 +59,11 @@ SOURCES += \
access/qnetworkrequest.cpp \
access/qnetworkreply.cpp \
access/qnetworkreplyimpl.cpp \
+ access/qnetworkreplydataimpl.cpp \
+ access/qnetworkreplyfileimpl.cpp \
access/qabstractnetworkcache.cpp \
- access/qnetworkdiskcache.cpp
+ access/qnetworkdiskcache.cpp \
+ access/qhttpthreaddelegate.cpp \
+ access/qhttpmultipart.cpp
include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
index b832593..50a3b1e 100644
--- a/src/network/access/qftp.cpp
+++ b/src/network/access/qftp.cpp
@@ -319,6 +319,10 @@ void QFtpDTP::connectToHost(const QString & host, quint16 port)
socket = 0;
}
socket = new QTcpSocket(this);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket
+ socket->setProperty("_q_networksession", property("_q_networksession"));
+#endif
socket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));
connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
@@ -331,6 +335,10 @@ void QFtpDTP::connectToHost(const QString & host, quint16 port)
int QFtpDTP::setupListener(const QHostAddress &address)
{
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket
+ listener.setProperty("_q_networksession", property("_q_networksession"));
+#endif
if (!listener.isListening() && !listener.listen(address, 0))
return -1;
return listener.serverPort();
@@ -808,6 +816,11 @@ QFtpPI::QFtpPI(QObject *parent) :
void QFtpPI::connectToHost(const QString &host, quint16 port)
{
emit connectState(QFtp::HostLookup);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket & DTP
+ commandSocket.setProperty("_q_networksession", property("_q_networksession"));
+ dtp.setProperty("_q_networksession", property("_q_networksession"));
+#endif
commandSocket.connectToHost(host, port);
}
@@ -2240,6 +2253,10 @@ void QFtpPrivate::_q_startNextCommand()
c->rawCmds.clear();
_q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));
} else if (c->command == QFtp::ConnectToHost) {
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the PI
+ pi.setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
if (!proxyHost.isEmpty()) {
host = c->rawCmds[0];
port = c->rawCmds[1].toUInt();
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
new file mode 100644
index 0000000..7545658
--- /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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $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
+ \a 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..76e8e14
--- /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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $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..f57b0eb
--- /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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $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 d76a5fd..6101eea 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -119,6 +119,11 @@ void QHttpNetworkConnectionPrivate::init()
{
for (int i = 0; i < channelCount; i++) {
channels[i].setConnection(this->q_func());
+ channels[i].ssl = encrypt;
+#ifndef QT_NO_BEARERMANAGEMENT
+ //push session down to channels
+ channels[i].networkSession = networkSession;
+#endif
channels[i].init();
}
}
@@ -295,7 +300,13 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
int i = indexOf(socket);
// remove the corrupt data if any
reply->d_func()->eraseData();
+
+ // Clean the channel
channels[i].close();
+ channels[i].reply = 0;
+ channels[i].request = QHttpNetworkRequest();
+ channels[i].requeueCurrentlyPipelinedRequests();
+
// send the next request
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -490,7 +501,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);
@@ -503,8 +514,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()) {
@@ -514,9 +524,18 @@ 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;
+}
+
+QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest()
+{
+ if (!highPriorityQueue.isEmpty())
+ return highPriorityQueue.last().first;
+ if (!lowPriorityQueue.isEmpty())
+ return lowPriorityQueue.last().first;
+ return QHttpNetworkRequest();
}
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
@@ -567,33 +586,35 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
|| channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
return;
-
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing highPriorityQueue, size=" << highPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything, now do the low prio queue
}
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing lowPriorityQueue, size=" << lowPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
while (!lowPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything
}
+ channels[i].pipelineFlush();
}
// returns true when the processing of a queue has been done
@@ -766,7 +787,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
//resend the necessary ones.
for (int i = 0; i < channelCount; ++i) {
- if (channels[i].resendCurrent) {
+ if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
channels[i].resendCurrent = false;
channels[i].state = QHttpNetworkConnectionChannel::IdleState;
@@ -785,17 +806,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();
}
}
@@ -812,6 +824,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--;
+ }
+ }
}
@@ -826,6 +853,23 @@ void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
}
}
+#ifndef QT_NO_BEARERMANAGEMENT
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+ : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
+{
+ Q_D(QHttpNetworkConnection);
+ d->networkSession = networkSession;
+ d->init();
+}
+
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+{
+ Q_D(QHttpNetworkConnection);
+ d->networkSession = networkSession;
+ d->init();
+}
+#else
QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
{
@@ -839,6 +883,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS
Q_D(QHttpNetworkConnection);
d->init();
}
+#endif
QHttpNetworkConnection::~QHttpNetworkConnection()
{
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 420fac1..6c953be 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -55,6 +55,7 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qnetworksession.h>
#include <private/qobject_p.h>
#include <qauthenticator.h>
@@ -88,8 +89,13 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
Q_OBJECT
public:
+#ifndef QT_NO_BEARERMANAGEMENT
+ QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+ QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+#else
QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
+#endif
~QHttpNetworkConnection();
//The hostname to which this is connected to.
@@ -161,8 +167,9 @@ 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);
+ QHttpNetworkRequest predictNextRequest();
void fillPipeline(QAbstractSocket *socket);
bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
@@ -208,6 +215,10 @@ public:
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+
friend class QHttpNetworkConnectionChannel;
};
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 7c2e2a1..9b2a6e8 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -54,12 +54,17 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+#include "private/qnetworksession_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
: socket(0)
+ , ssl(false)
, state(IdleState)
, reply(0)
, written(0)
@@ -90,11 +95,15 @@ void QHttpNetworkConnectionChannel::init()
#else
socket = new QTcpSocket;
#endif
-
- // limit the socket read buffer size. we will read everything into
- // the QHttpNetworkReply anyway, so let's grow only that and not
- // here and there.
- socket->setReadBufferSize(64*1024);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //push session down to socket
+ 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)),
@@ -147,10 +156,12 @@ void QHttpNetworkConnectionChannel::init()
void QHttpNetworkConnectionChannel::close()
{
- socket->blockSignals(true);
+ if (socket->state() == QAbstractSocket::UnconnectedState)
+ state = QHttpNetworkConnectionChannel::IdleState;
+ else
+ state = QHttpNetworkConnectionChannel::ClosingState;
+
socket->close();
- socket->blockSignals(false);
- state = QHttpNetworkConnectionChannel::IdleState;
}
@@ -174,11 +185,12 @@ bool QHttpNetworkConnectionChannel::sendRequest()
written = 0; // excluding the header
bytesTotal = 0;
- reply->d_func()->clear();
- reply->d_func()->connection = connection;
- reply->d_func()->connectionChannel = this;
- reply->d_func()->autoDecompress = request.d->autoDecompress;
- reply->d_func()->pipeliningUsed = false;
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ replyPrivate->clear();
+ replyPrivate->connection = connection;
+ replyPrivate->connectionChannel = this;
+ replyPrivate->autoDecompress = request.d->autoDecompress;
+ replyPrivate->pipeliningUsed = false;
// if the url contains authentication parameters, use the new ones
// both channels will use the new authentication parameters
@@ -254,7 +266,7 @@ bool QHttpNetworkConnectionChannel::sendRequest()
#endif
{
// get pointer to upload data
- qint64 currentReadSize;
+ qint64 currentReadSize = 0;
qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
@@ -337,7 +349,6 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
- qint64 bytes = 0;
QAbstractSocket::SocketState socketState = socket->state();
// connection might be closed to signal the end of data
@@ -358,12 +369,14 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
// read loop for the response
- while (socket->bytesAvailable()) {
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
switch (state) {
case QHttpNetworkReplyPrivate::NothingDoneState: {
- // only eat whitespace on the first call
- eatWhitespace();
state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
// fallthrough
}
@@ -387,6 +400,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
bytes += headerBytes;
+ // If headers were parsed successfully now it is the ReadingDataState
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
// remove the Content-Length from header
@@ -401,6 +415,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
if (replyPrivate->shouldEmitSignals())
emit reply->headerChanged();
+ // After headerChanged had been emitted
+ // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
+ // this is handled in the ReadingDataState however
+
if (!replyPrivate->expectContent()) {
replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
allDone();
@@ -422,22 +440,29 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// to the read buffer maximum size, but we don't care since they should be small.
return;
}
- if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
- // bulk files like images should fulfill these properties and
- // we can therefore save on memory copying
- bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
- replyPrivate->totalProgress += bytes;
+
+ if (replyPrivate->userProvidedDownloadBuffer) {
+ // the user provided a direct buffer where we should put all our data in.
+ // this only works when we can tell the user the content length and he/she can allocate
+ // the buffer in that size.
+ // note that this call will read only from the still buffered data
+ qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+
+ // the user will get notified of it via progress signal
+ if (haveRead > 0)
+ emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
+ && replyPrivate->bodyLength > 0) {
+ // bulk files like images should fulfill these properties and
+ // we can therefore save on memory copying
+ qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = reply;
emit reply->readyRead();
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
else
@@ -445,8 +470,9 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// use the traditional slower reading (for compressed encoding, chunked encoding,
// no content-length etc)
QByteDataBuffer byteDatas;
- bytes = replyPrivate->readBody(socket, &byteDatas);
- if (bytes) {
+ qint64 haveRead = replyPrivate->readBody(socket, &byteDatas);
+ if (haveRead) {
+ bytes += haveRead;
if (replyPrivate->autoDecompress)
replyPrivate->appendCompressedReplyData(byteDatas);
else
@@ -455,22 +481,16 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
if (!replyPrivate->autoDecompress) {
replyPrivate->totalProgress += bytes;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = 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 (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
#ifndef QT_NO_COMPRESS
else if (!expand(false)) { // expand a chunk if possible
- return; // ### expand failed
+ // If expand() failed we can just return, it had already called connection->emitReplyError()
+ return;
}
#endif
}
@@ -487,12 +507,13 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
default:
break;
}
- }
+ } while (bytes != lastBytes && reply);
}
// 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();
@@ -515,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;
}
@@ -530,7 +552,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (socketState != QAbstractSocket::ConnectedState) {
// connect to the host if not already connected.
state = QHttpNetworkConnectionChannel::ConnectingState;
- pendingEncrypt = connection->d_func()->encrypt;
+ pendingEncrypt = ssl;
// reset state
pipeliningSupported = PipeliningSupportUnknown;
@@ -553,29 +575,57 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
#ifndef QT_NO_NETWORKPROXY
// HTTPS always use transparent proxy.
- if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) {
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
connectHost = connection->d_func()->networkProxy.hostName();
connectPort = connection->d_func()->networkProxy.port();
}
if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
- QByteArray value = request.headerField("user-agent");
+ QByteArray value;
+ // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
+ if (request.url().isEmpty())
+ value = connection->d_func()->predictNextRequest().headerField("user-agent");
+ else
+ value = request.headerField("user-agent");
if (!value.isEmpty())
socket->setProperty("_q_user-agent", value);
}
#endif
- if (connection->d_func()->encrypt) {
+ if (ssl) {
#ifndef QT_NO_OPENSSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
sslSocket->connectToHostEncrypted(connectHost, connectPort);
if (ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
#else
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
#endif
} else {
- socket->connectToHost(connectHost, connectPort);
+ // 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);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
+ }
+#endif
}
return false;
}
@@ -608,18 +658,10 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
reply->d_func()->totalProgress += inflated.size();
reply->d_func()->appendUncompressedReplyData(inflated);
if (reply->d_func()->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = 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 (replyPointer.isNull())
- return true;
emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return true;
-
}
}
} else {
@@ -634,32 +676,44 @@ 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)
- expand(true); // ### if expand returns false, its an error
+ if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) {
+ bool expandResult = expand(true);
+ // If expand() failed we can just return, it had already called connection->emitReplyError()
+ if (!expandResult)
+ return;
+ }
#endif
+
+ if (!reply) {
+ qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.nokia.com/";
+ return;
+ }
+
// while handling 401 & 407, we might reset the status code, so save this.
bool emitFinished = reply->d_func()->shouldEmitSignals();
- handleStatus();
- // ### at this point there should be no more data on the socket
- // close if server requested
bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
- if (connectionCloseEnabled)
- close();
+ detectPipeliningSupport();
+
+ handleStatus();
+ // handleStatus() might have removed the reply because it already called connection->emitReplyError()
+
// queue the finished signal, this is required since we might send new requests from
// slot connected to it. The socket will not fire readyRead signal, if we are already
// in the slot connected to readyRead
- if (emitFinished)
+ if (reply && emitFinished)
QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
+
+
// reset the reconnection attempts after we receive a complete reply.
// in case of failures, each channel will attempt two reconnects before emitting error.
reconnectAttempts = 2;
- detectPipeliningSupport();
-
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
- this->state = QHttpNetworkConnectionChannel::IdleState;
+ if (state != QHttpNetworkConnectionChannel::ClosingState)
+ state = QHttpNetworkConnectionChannel::IdleState;
// if it does not need to be sent again we can set it to 0
// the previous code did not do that and we had problems with accidental re-sending of a
@@ -697,12 +751,20 @@ void QHttpNetworkConnectionChannel::allDone()
// this was wrong, allDone gets called from that function anyway.
}
} else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
- eatWhitespace();
// this is weird. we had nothing pipelined but still bytes available. better close it.
- if (socket->bytesAvailable() > 0)
- close();
+ //if (socket->bytesAvailable() > 0)
+ // close();
+ //
+ // FIXME
+ // We do not close it anymore now, but should introduce this again after having fixed
+ // the chunked decoder in QHttpNetworkReply to read the whitespace after the last chunk.
+ // (Currently this is worked around by readStatus in the QHttpNetworkReply ignoring
+ // leading whitespace.
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else if (alreadyPipelinedRequests.isEmpty()) {
+ if (connectionCloseEnabled)
+ if (socket->state() != QAbstractSocket::UnconnectedState)
+ close();
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -710,6 +772,7 @@ void QHttpNetworkConnectionChannel::allDone()
void QHttpNetworkConnectionChannel::detectPipeliningSupport()
{
+ Q_ASSERT(reply);
// detect HTTP Pipelining support
QByteArray serverHeaderField;
if (
@@ -747,30 +810,6 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionChannel::eatWhitespace()
-{
- char c;
- do {
- qint64 ret = socket->peek(&c, 1);
-
- // nothing read, fine.
- if (ret == 0)
- return;
-
- // EOF from socket?
- if (ret == -1)
- return; // FIXME, we need to stop processing. however the next stuff done will also do that.
-
- // read all whitespace and line endings
- if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) {
- socket->read(&c, 1);
- continue;
- } else {
- break;
- }
- } while(true);
-}
-
void QHttpNetworkConnectionChannel::handleStatus()
{
Q_ASSERT(socket);
@@ -817,6 +856,10 @@ void QHttpNetworkConnectionChannel::handleStatus()
bool QHttpNetworkConnectionChannel::resetUploadData()
{
+ if (!reply) {
+ //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
+ return false;
+ }
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
if (!uploadByteDevice)
return true;
@@ -831,7 +874,7 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
}
-void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
+void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
{
// this is only called for simple GET
@@ -844,21 +887,38 @@ void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
reply->d_func()->pipeliningUsed = true;
#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(request,
- (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+ pipeline.append(QHttpNetworkRequestPrivate::header(request,
+ (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
#else
- QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
+ pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
#endif
- socket->write(header);
alreadyPipelinedRequests.append(pair);
+
+ // pipelineFlush() needs to be called at some point afterwards
}
+void QHttpNetworkConnectionChannel::pipelineFlush()
+{
+ if (pipeline.isEmpty())
+ return;
+
+ // The goal of this is so that we have everything in one TCP packet.
+ // For the Unbuffered QTcpSocket this is manually needed, the buffered
+ // QTcpSocket does it automatically.
+ // Also, sometimes the OS does it for us (Nagle's algorithm) but that
+ // happens only sometimes.
+ socket->write(pipeline);
+ pipeline.clear();
+}
+
+
void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
{
requeueCurrentlyPipelinedRequests();
close();
- resendCurrent = true;
+ if (reply)
+ resendCurrent = true;
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -886,6 +946,22 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
//private slots
void QHttpNetworkConnectionChannel::_q_readyRead()
{
+ 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) {
+ _q_error(socket->error());
+ // We still need to handle the reply so it emits its signals etc.
+ if (reply)
+ _q_receiveReply();
+ return;
+ }
+ }
+
if (isSocketWaiting() || isSocketReading()) {
state = QHttpNetworkConnectionChannel::ReadingState;
if (reply)
@@ -904,6 +980,12 @@ void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
void QHttpNetworkConnectionChannel::_q_disconnected()
{
+ if (state == QHttpNetworkConnectionChannel::ClosingState) {
+ state = QHttpNetworkConnectionChannel::IdleState;
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+
// read the available data before closing
if (isSocketWaiting() || isSocketReading()) {
if (reply) {
@@ -941,10 +1023,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();
}
}
@@ -1010,6 +1092,9 @@ 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);
@@ -1025,7 +1110,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
@@ -1041,7 +1130,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)
@@ -1052,7 +1144,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/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index ffb2711..f635cc9 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -95,9 +95,11 @@ public:
WritingState = 2, // writing the data
WaitingState = 4, // waiting for reply
ReadingState = 8, // reading the reply
- BusyState = (ConnectingState|WritingState|WaitingState|ReadingState)
+ ClosingState = 16,
+ BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|ClosingState)
};
QAbstractSocket *socket;
+ bool ssl;
ChannelState state;
QHttpNetworkRequest request; // current request
QHttpNetworkReply *reply; // current reply for this request
@@ -115,6 +117,9 @@ public:
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
// HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining
enum PipeliningSupport {
@@ -124,7 +129,11 @@ public:
};
PipeliningSupport pipeliningSupported;
QList<HttpMessagePair> alreadyPipelinedRequests;
-
+ QByteArray pipeline; // temporary buffer that gets sent to socket in pipelineFlush
+ void pipelineInto(HttpMessagePair &pair);
+ void pipelineFlush();
+ void requeueCurrentlyPipelinedRequests();
+ void detectPipeliningSupport();
QHttpNetworkConnectionChannel();
@@ -144,15 +153,9 @@ public:
bool resetUploadData(); // return true if resetting worked or there is no upload data
- void pipelineInto(HttpMessagePair &pair);
- void requeueCurrentlyPipelinedRequests();
- void detectPipeliningSupport();
-
void handleUnexpectedEOF();
void closeAndResendCurrentRequest();
- void eatWhitespace();
-
bool isSocketBusy() const;
bool isSocketWriting() const;
bool isSocketWaiting() const;
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 0f2fcba..1a02200 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -119,6 +119,7 @@ void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
{
Q_D(QHttpNetworkReply);
d->request = request;
+ d->ssl = request.isSsl();
}
int QHttpNetworkReply::statusCode() const
@@ -176,6 +177,12 @@ qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
return -1;
}
+bool QHttpNetworkReply::readAnyAvailable() const
+{
+ Q_D(const QHttpNetworkReply);
+ return (d->responseData.bufferCount() > 0);
+}
+
QByteArray QHttpNetworkReply::readAny()
{
Q_D(QHttpNetworkReply);
@@ -201,6 +208,25 @@ void QHttpNetworkReply::setDownstreamLimited(bool dsl)
d->connection->d_func()->readMoreLater(this);
}
+bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0);
+}
+
+void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
+{
+ Q_D(QHttpNetworkReply);
+ if (supportsUserProvidedDownloadBuffer())
+ d->userProvidedDownloadBuffer = b;
+}
+
+char* QHttpNetworkReply::userProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return d->userProvidedDownloadBuffer;
+}
+
bool QHttpNetworkReply::isFinished() const
{
return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
@@ -218,7 +244,10 @@ QHttpNetworkConnection* QHttpNetworkReply::connection()
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
+ : QHttpNetworkHeaderPrivate(newUrl)
+ , state(NothingDoneState)
+ , ssl(false)
+ , statusCode(100),
majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
chunkedTransferEncoding(false),
connectionCloseEnabled(true),
@@ -226,6 +255,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
autoDecompress(false), responseData(), requestIsPrepared(false)
,pipeliningUsed(false), downstreamLimited(false)
+ ,userProvidedDownloadBuffer(0)
{
}
@@ -465,6 +495,8 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return -1; // unexpected EOF
else if (haveRead == 0)
break; // read more later
+ else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
+ continue; // Ignore all whitespace that was trailing froma previous request on that socket
bytes++;
@@ -640,12 +672,32 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+{
+ // This first read is to flush the buffer inside the socket
+ qint64 haveRead = 0;
+ haveRead = socket->read(b, bodyLength - contentRead);
+ if (haveRead == -1) {
+ return 0; // ### error checking here;
+ }
+ contentRead += haveRead;
+
+ if (contentRead == bodyLength) {
+ state = AllDoneState;
+ }
+
+ return haveRead;
+}
+
+// note this function can only be used for non-chunked, non-compressed with
+// known content length
qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
{
+
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
QByteArray bd;
bd.resize(toBeRead);
- qint64 haveRead = socket->read(bd.data(), bd.size());
+ qint64 haveRead = socket->read(bd.data(), toBeRead);
if (haveRead == -1) {
bd.clear();
return 0; // ### error checking here;
@@ -667,29 +719,34 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
{
qint64 bytes = 0;
if (isChunked()) {
- bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
- } else if (bodyLength > 0) { // we have a Content-Length
+ // chunked transfer encoding (rfc 2616, sec 3.6)
+ bytes += readReplyBodyChunked(socket, out);
+ } else if (bodyLength > 0) {
+ // we have a Content-Length
bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
if (contentRead + bytes == bodyLength)
state = AllDoneState;
} else {
+ // no content length. just read what's possible
bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
}
contentRead += bytes;
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
{
+ // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
- Q_ASSERT(in);
+ Q_ASSERT(socket);
Q_ASSERT(out);
- int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
+
while (toBeRead > 0) {
QByteArray byteData;
byteData.resize(toBeRead);
- qint64 haveRead = in->read(byteData.data(), byteData.size());
+ qint64 haveRead = socket->read(byteData.data(), byteData.size());
if (haveRead <= 0) {
// ### error checking here
byteData.clear();
@@ -701,25 +758,35 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer
bytes += haveRead;
size -= haveRead;
- toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
}
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
- while (in->bytesAvailable()) { // while we can read from input
- // if we are done with the current chunk, get the size of the new chunk
+ while (socket->bytesAvailable()) {
if (currentChunkRead >= currentChunkSize) {
+ // For the first chunk and when we're done with a chunk
currentChunkSize = 0;
currentChunkRead = 0;
if (bytes) {
+ // After a chunk
char crlf[2];
- bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
+ // read the "\r\n" after the chunk
+ qint64 haveRead = socket->read(crlf, 2);
+ // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
+ // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
+ // it right now still works, but we should definitely fix this.
+
+ if (haveRead != 2)
+ return bytes; // FIXME
+ bytes += haveRead;
}
- bytes += getChunkSize(in, &currentChunkSize);
+ // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
+ bytes += getChunkSize(socket, &currentChunkSize);
if (currentChunkSize == -1)
break;
}
@@ -729,8 +796,8 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
break;
}
- // otherwise, try to read what is missing for this chunk
- qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
+ // otherwise, try to begin reading this chunk / to read what is missing for this chunk
+ qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
currentChunkRead += haveRead;
bytes += haveRead;
@@ -740,22 +807,25 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
*chunkSize = -1;
- int bytesAvailable = in->bytesAvailable();
+
+ int bytesAvailable = socket->bytesAvailable();
+ // FIXME rewrite to permanent loop without bytesAvailable
while (bytesAvailable > bytes) {
- qint64 sniffedBytes = in->peek(crlf, 2);
+ qint64 sniffedBytes = socket->peek(crlf, 2);
int fragmentSize = fragment.size();
+
// check the next two bytes for a "\r\n", skip blank lines
if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
{
- bytes += in->read(crlf, 1); // read the \r or \n
+ bytes += socket->read(crlf, 1); // read the \r or \n
if (crlf[0] == '\r')
- bytes += in->read(crlf, 1); // read the \n
+ bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
@@ -765,10 +835,15 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
} else {
// read the fragment to the buffer
char c = 0;
- bytes += in->read(&c, 1);
+ qint64 haveRead = socket->read(&c, 1);
+ if (haveRead < 0) {
+ return -1; // FIXME
+ }
+ bytes += haveRead;
fragment.append(c);
}
}
+
return bytes;
}
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 365308f..de6c0d2 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -125,10 +125,15 @@ public:
qint64 bytesAvailable() const;
qint64 bytesAvailableNextBlock() const;
+ bool readAnyAvailable() const;
QByteArray readAny();
QByteArray readAll();
void setDownstreamLimited(bool t);
+ bool supportsUserProvidedDownloadBuffer();
+ void setUserProvidedDownloadBuffer(char*);
+ char* userProvidedDownloadBuffer();
+
bool isFinished() const;
bool isPipeliningUsed() const;
@@ -150,6 +155,7 @@ Q_SIGNALS:
void finished();
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
void headerChanged();
+ // FIXME we need to change this to qint64!
void dataReadProgress(int done, int total);
void dataSendProgress(qint64 done, qint64 total);
void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
@@ -175,15 +181,16 @@ public:
qint64 readHeader(QAbstractSocket *socket);
void parseHeader(const QByteArray &header);
qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
bool findChallenge(bool forProxy, QByteArray &challenge) const;
QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
- qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
void appendUncompressedReplyData(QByteArray &qba);
void appendUncompressedReplyData(QByteDataBuffer &data);
@@ -213,6 +220,7 @@ public:
} state;
QHttpNetworkRequest request;
+ bool ssl;
int statusCode;
int majorVersion;
int minorVersion;
@@ -242,6 +250,8 @@ public:
bool pipeliningUsed;
bool downstreamLimited;
+
+ char* userProvidedDownloadBuffer;
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 2ae119e..cbc0d97 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -63,6 +63,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
pipeliningAllowed = other.pipeliningAllowed;
customVerb = other.customVerb;
withCredentials = other.withCredentials;
+ ssl = other.ssl;
}
QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
@@ -73,6 +74,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
{
return QHttpNetworkHeaderPrivate::operator==(other)
&& (operation == other.operation)
+ && (ssl == other.ssl)
&& (uploadByteDevice == other.uploadByteDevice);
}
@@ -156,8 +158,10 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
}
if (request.d->operation == QHttpNetworkRequest::Post) {
// add content type, if not set in the request
- if (request.headerField("content-type").isEmpty())
- ba += "Content-Type: application/x-www-form-urlencoded\r\n";
+ if (request.headerField("content-type").isEmpty()) {
+ qWarning("content-type missing in HTTP POST, defaulting to application/octet-stream");
+ ba += "Content-Type: application/octet-stream\r\n";
+ }
if (!request.d->uploadByteDevice && request.d->url.hasQuery()) {
QByteArray query = request.d->url.encodedQuery();
ba += "Content-Length: ";
@@ -199,6 +203,15 @@ void QHttpNetworkRequest::setUrl(const QUrl &url)
d->url = url;
}
+bool QHttpNetworkRequest::isSsl() const
+{
+ return d->ssl;
+}
+void QHttpNetworkRequest::setSsl(bool s)
+{
+ d->ssl = s;
+}
+
qint64 QHttpNetworkRequest::contentLength() const
{
return d->contentLength();
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index ea68c8d4..4000eca 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -116,6 +116,9 @@ public:
bool withCredentials() const;
void setWithCredentials(bool b);
+ bool isSsl() const;
+ void setSsl(bool);
+
void setUploadByteDevice(QNonContiguousByteDevice *bd);
QNonContiguousByteDevice* uploadByteDevice() const;
@@ -146,6 +149,7 @@ public:
bool autoDecompress;
bool pipeliningAllowed;
bool withCredentials;
+ bool ssl;
};
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
new file mode 100644
index 0000000..092fa7d
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHTTPTHREADDELEGATE_DEBUG
+#include "qhttpthreaddelegate_p.h"
+
+#include <QThread>
+#include <QTimer>
+#include <QAuthenticator>
+#include <QEventLoop>
+
+#include "private/qhttpnetworkreply_p.h"
+#include "private/qnetworkaccesscache_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
+{
+ QNetworkReply::NetworkError code;
+ // we've got an error
+ switch (httpStatusCode) {
+ case 401: // Authorization required
+ code = QNetworkReply::AuthenticationRequiredError;
+ break;
+
+ case 403: // Access denied
+ code = QNetworkReply::ContentOperationNotPermittedError;
+ break;
+
+ case 404: // Not Found
+ code = QNetworkReply::ContentNotFoundError;
+ break;
+
+ case 405: // Method Not Allowed
+ code = QNetworkReply::ContentOperationNotPermittedError;
+ break;
+
+ case 407:
+ code = QNetworkReply::ProxyAuthenticationRequiredError;
+ break;
+
+ case 418: // I'm a teapot
+ code = QNetworkReply::ProtocolInvalidOperationError;
+ break;
+
+
+ default:
+ if (httpStatusCode > 500) {
+ // some kind of server error
+ code = QNetworkReply::ProtocolUnknownError;
+ } else if (httpStatusCode >= 400) {
+ // content error we did not handle above
+ code = QNetworkReply::UnknownContentError;
+ } else {
+ qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
+ httpStatusCode, qPrintable(url.toString()));
+ code = QNetworkReply::ProtocolFailure;
+ }
+ }
+
+ return code;
+}
+
+
+static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
+{
+ QByteArray result;
+ QUrl copy = url;
+ bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
+ copy.setPort(copy.port(isEncrypted ? 443 : 80));
+ result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath |
+ QUrl::RemoveQuery | QUrl::RemoveFragment);
+
+#ifndef QT_NO_NETWORKPROXY
+ if (proxy && proxy->type() != QNetworkProxy::NoProxy) {
+ QUrl key;
+
+ switch (proxy->type()) {
+ case QNetworkProxy::Socks5Proxy:
+ key.setScheme(QLatin1String("proxy-socks5"));
+ break;
+
+ case QNetworkProxy::HttpProxy:
+ case QNetworkProxy::HttpCachingProxy:
+ key.setScheme(QLatin1String("proxy-http"));
+ break;
+
+ default:
+ break;
+ }
+
+ if (!key.scheme().isEmpty()) {
+ key.setUserName(proxy->user());
+ key.setHost(proxy->hostName());
+ key.setPort(proxy->port());
+ key.setEncodedQuery(result);
+ result = key.toEncoded();
+ }
+ }
+#else
+ Q_UNUSED(proxy)
+#endif
+
+ return "http-connection:" + result;
+}
+
+class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
+ public QNetworkAccessCache::CacheableObject
+{
+ // Q_OBJECT
+public:
+#ifdef QT_NO_BEARERMANAGEMENT
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
+ : QHttpNetworkConnection(hostName, port, encrypt)
+#else
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
+ : QHttpNetworkConnection(hostName, port, encrypt, /*parent=*/0, networkSession)
+#endif
+ {
+ setExpires(true);
+ setShareable(true);
+ }
+
+ virtual void dispose()
+ {
+#if 0 // sample code; do this right with the API
+ Q_ASSERT(!isWorking());
+#endif
+ delete this;
+ }
+};
+
+
+QThreadStorage<QNetworkAccessCache *> QHttpThreadDelegate::connections;
+
+
+QHttpThreadDelegate::~QHttpThreadDelegate()
+{
+ // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
+ if (httpReply) {
+ delete httpReply;
+ }
+
+ // Get the object cache that stores our QHttpNetworkConnection objects
+ // and release the entry for this QHttpNetworkConnection
+ if (connections.hasLocalData() && !cacheKey.isEmpty()) {
+ connections.localData()->releaseEntry(cacheKey);
+ }
+}
+
+
+QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
+ QObject(parent)
+ , ssl(false)
+ , downloadBufferMaximumSize(0)
+ , pendingDownloadData(0)
+ , pendingDownloadProgress(0)
+ , synchronous(false)
+ , incomingStatusCode(0)
+ , isPipeliningUsed(false)
+ , incomingContentLength(-1)
+ , incomingErrorCode(QNetworkReply::NoError)
+ , downloadBuffer(0)
+ , httpConnection(0)
+ , httpReply(0)
+ , synchronousRequestLoop(0)
+{
+}
+
+// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
+void QHttpThreadDelegate::startRequestSynchronously()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
+#endif
+ synchronous = true;
+
+ QEventLoop synchronousRequestLoop;
+ this->synchronousRequestLoop = &synchronousRequestLoop;
+
+ // Worst case timeout
+ QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
+
+ QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
+ synchronousRequestLoop.exec();
+
+ connections.localData()->releaseEntry(cacheKey);
+ connections.setLocalData(0);
+
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
+#endif
+}
+
+
+// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
+void QHttpThreadDelegate::startRequest()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
+#endif
+ // Check QThreadStorage for the QNetworkAccessCache
+ // If not there, create this connection cache
+ if (!connections.hasLocalData()) {
+ connections.setLocalData(new QNetworkAccessCache());
+ }
+
+ // check if we have an open connection to this host
+ QUrl urlCopy = httpRequest.url();
+ urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
+
+#ifndef QT_NO_NETWORKPROXY
+ if (transparentProxy.type() != QNetworkProxy::NoProxy)
+ cacheKey = makeCacheKey(urlCopy, &transparentProxy);
+ else if (cacheProxy.type() != QNetworkProxy::NoProxy)
+ cacheKey = makeCacheKey(urlCopy, &cacheProxy);
+ else
+#endif
+ cacheKey = makeCacheKey(urlCopy, 0);
+
+
+ // the http object is actually a QHttpNetworkConnection
+ httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
+ if (httpConnection == 0) {
+ // no entry in cache; create an object
+ // the http object is actually a QHttpNetworkConnection
+#ifdef QT_NO_BEARERMANAGEMENT
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
+#else
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
+#endif
+#ifndef QT_NO_OPENSSL
+ // Set the QSslConfiguration from this QNetworkRequest.
+ if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
+ httpConnection->setSslConfiguration(incomingSslConfiguration);
+ }
+#endif
+
+#ifndef QT_NO_NETWORKPROXY
+ httpConnection->setTransparentProxy(transparentProxy);
+ httpConnection->setCacheProxy(cacheProxy);
+#endif
+
+ // cache the QHttpNetworkConnection corresponding to this cache key
+ connections.localData()->addEntry(cacheKey, httpConnection);
+ }
+
+
+ // Send the request to the connection
+ httpReply = httpConnection->sendRequest(httpRequest);
+ httpReply->setParent(this);
+
+ // Connect the reply signals that we need to handle and then forward
+ if (synchronous) {
+ connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
+ connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
+ connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
+ this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
+
+ connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
+ connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
+
+ // Don't care about ignored SSL errors for now in the synchronous HTTP case.
+ } else if (!synchronous) {
+ connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
+ connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
+ connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
+ this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
+ // some signals are only interesting when normal asynchronous style is used
+ connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
+ connect(httpReply,SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgressSlot(int,int)));
+ connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
+#ifndef QT_NO_OPENSSL
+ connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
+#endif
+
+ // In the asynchronous HTTP case we can just forward those signals
+ // Connect the reply signals that we can directly forward
+ connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
+ connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+ }
+}
+
+// This gets called from the user thread or by the synchronous HTTP timeout timer
+void QHttpThreadDelegate::abortRequest()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
+#endif
+ if (httpReply) {
+ delete httpReply;
+ httpReply = 0;
+ }
+
+ // Got aborted by the timeout timer
+ if (synchronous) {
+ incomingErrorCode = QNetworkReply::TimeoutError;
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ } else {
+ //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
+ this->deleteLater();
+ }
+}
+
+void QHttpThreadDelegate::readyReadSlot()
+{
+ // Don't do in zerocopy case
+ if (!downloadBuffer.isNull())
+ return;
+
+ while (httpReply->readAnyAvailable()) {
+ pendingDownloadData->fetchAndAddRelease(1);
+ emit downloadData(httpReply->readAny());
+ }
+}
+
+void QHttpThreadDelegate::finishedSlot()
+{
+ if (!httpReply) {
+ qWarning() << "QHttpThreadDelegate::finishedSlot: HTTP reply had already been deleted, internal problem. Please report.";
+ return;
+ }
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
+#endif
+
+ // If there is still some data left emit that now
+ while (httpReply->readAnyAvailable()) {
+ pendingDownloadData->fetchAndAddRelease(1);
+ emit downloadData(httpReply->readAny());
+ }
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+
+ if (httpReply->statusCode() >= 400) {
+ // it's an error reply
+ QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error downloading %1 - server replied: %2"));
+ msg = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
+ emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
+ }
+
+ emit downloadFinished();
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+void QHttpThreadDelegate::synchronousFinishedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
+#endif
+ if (httpReply->statusCode() >= 400) {
+ // it's an error reply
+ QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error downloading %1 - server replied: %2"));
+ incomingErrorDetail = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
+ incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
+ }
+
+ synchronousDownloadData = httpReply->readAll();
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
+{
+ if (!httpReply) {
+ qWarning() << "QHttpThreadDelegate::finishedWithErrorSlot: HTTP reply had already been deleted, internal problem. Please report.";
+ return;
+ }
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+ emit error(errorCode,detail);
+ emit downloadFinished();
+
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+
+void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
+#endif
+ incomingErrorCode = errorCode;
+ incomingErrorDetail = detail;
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+static void downloadBufferDeleter(char *ptr)
+{
+ delete[] ptr;
+}
+
+void QHttpThreadDelegate::headerChangedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+
+ // Is using a zerocopy buffer allowed by user and possible with this reply?
+ if (httpReply->supportsUserProvidedDownloadBuffer()
+ && downloadBufferMaximumSize > 0) {
+ char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
+ if (buf) {
+ downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
+ httpReply->setUserProvidedDownloadBuffer(buf);
+ }
+ }
+
+ // We fetch this into our own
+ incomingHeaders = httpReply->header();
+ incomingStatusCode = httpReply->statusCode();
+ incomingReasonPhrase = httpReply->reasonPhrase();
+ isPipeliningUsed = httpReply->isPipeliningUsed();
+ incomingContentLength = httpReply->contentLength();
+
+ emit downloadMetaData(incomingHeaders,
+ incomingStatusCode,
+ incomingReasonPhrase,
+ isPipeliningUsed,
+ downloadBuffer,
+ incomingContentLength);
+}
+
+void QHttpThreadDelegate::synchronousHeaderChangedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
+#endif
+ // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
+ incomingHeaders = httpReply->header();
+ incomingStatusCode = httpReply->statusCode();
+ incomingReasonPhrase = httpReply->reasonPhrase();
+ isPipeliningUsed = httpReply->isPipeliningUsed();
+ incomingContentLength = httpReply->contentLength();
+}
+
+
+void QHttpThreadDelegate::dataReadProgressSlot(int done, int total)
+{
+ // If we don't have a download buffer don't attempt to go this codepath
+ // It is not used by QNetworkAccessHttpBackend
+ if (downloadBuffer.isNull())
+ return;
+
+ pendingDownloadProgress->fetchAndAddRelease(1);
+ emit downloadProgress(done, total);
+}
+
+void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
+{
+ authenticationManager->cacheCredentials(request.url(), authenticator);
+}
+
+
+#ifndef QT_NO_OPENSSL
+void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
+{
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+
+ bool ignoreAll = false;
+ QList<QSslError> specificErrors;
+ emit sslErrors(errors, &ignoreAll, &specificErrors);
+ if (ignoreAll)
+ httpReply->ignoreSslErrors();
+ if (!specificErrors.isEmpty())
+ httpReply->ignoreSslErrors(specificErrors);
+}
+#endif
+
+void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
+{
+ Q_UNUSED(request);
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
+#endif
+
+ // Ask the credential cache
+ QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
+ if (!credential.isNull()) {
+ a->setUser(credential.user);
+ a->setPassword(credential.password);
+ }
+
+ // Disconnect this connection now since we only want to ask the authentication cache once.
+ QObject::disconnect(this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
+#endif
+ // Ask the credential cache
+ QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
+ if (!credential.isNull()) {
+ a->setUser(credential.user);
+ a->setPassword(credential.password);
+ }
+
+ // Disconnect this connection now since we only want to ask the authentication cache once.
+ QObject::disconnect(this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
+}
+
+#endif
+
+#endif // QT_NO_HTTP
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
new file mode 100644
index 0000000..5730956
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPTHREADDELEGATE_H
+#define QHTTPTHREADDELEGATE_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 <QObject>
+#include <QThreadStorage>
+#include <QNetworkProxy>
+#include <QSslConfiguration>
+#include <QSslError>
+#include <QList>
+#include <QNetworkReply>
+#include "qhttpnetworkrequest_p.h"
+#include "qhttpnetworkconnection_p.h"
+#include <QSharedPointer>
+#include "qsslconfiguration.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QHttpNetworkReply;
+class QEventLoop;
+class QNetworkAccessCache;
+class QNetworkAccessCachedHttpConnection;
+
+class QHttpThreadDelegate : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QHttpThreadDelegate(QObject *parent = 0);
+
+ ~QHttpThreadDelegate();
+
+ // incoming
+ bool ssl;
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration incomingSslConfiguration;
+#endif
+ QHttpNetworkRequest httpRequest;
+ qint64 downloadBufferMaximumSize;
+ // From backend, modified by us for signal compression
+ QSharedPointer<QAtomicInt> pendingDownloadData;
+ QSharedPointer<QAtomicInt> pendingDownloadProgress;
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy cacheProxy;
+ QNetworkProxy transparentProxy;
+#endif
+ QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+ bool synchronous;
+
+ // outgoing, Retrieved in the synchronous HTTP case
+ QByteArray synchronousDownloadData;
+ QList<QPair<QByteArray,QByteArray> > incomingHeaders;
+ int incomingStatusCode;
+ QString incomingReasonPhrase;
+ bool isPipeliningUsed;
+ qint64 incomingContentLength;
+ QNetworkReply::NetworkError incomingErrorCode;
+ QString incomingErrorDetail;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+
+protected:
+ // The zerocopy download buffer, if used:
+ QSharedPointer<char> downloadBuffer;
+ // The QHttpNetworkConnection that is used
+ QNetworkAccessCachedHttpConnection *httpConnection;
+ QByteArray cacheKey;
+ QHttpNetworkReply *httpReply;
+
+ // Used for implementing the synchronous HTTP, see startRequestSynchronously()
+ QEventLoop *synchronousRequestLoop;
+
+signals:
+ void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *);
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *);
+#endif
+#ifndef QT_NO_OPENSSL
+ void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
+ void sslConfigurationChanged(const QSslConfiguration);
+#endif
+ void downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void downloadProgress(qint64, qint64);
+ void downloadData(QByteArray);
+ void error(QNetworkReply::NetworkError, const QString);
+ void downloadFinished();
+public slots:
+ // This are called via QueuedConnection from user thread
+ void startRequest();
+ void abortRequest();
+ // This is called with a BlockingQueuedConnection from user thread
+ void startRequestSynchronously();
+protected slots:
+ // From QHttp*
+ void readyReadSlot();
+ void finishedSlot();
+ void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
+ void synchronousFinishedSlot();
+ void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
+ void headerChangedSlot();
+ void synchronousHeaderChangedSlot();
+ void dataReadProgressSlot(int done, int total);
+ void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
+#ifndef QT_NO_OPENSSL
+ void sslErrorsSlot(const QList<QSslError> &errors);
+#endif
+
+ void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *);
+#ifndef QT_NO_NETWORKPROXY
+ void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *);
+#endif
+
+protected:
+ // Cache for all the QHttpNetworkConnection objects.
+ // This is per thread.
+ static QThreadStorage<QNetworkAccessCache *> connections;
+
+};
+
+// This QNonContiguousByteDevice is connected to the QNetworkAccessHttpBackend
+// and represents the PUT/POST data.
+class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevice
+{
+ Q_OBJECT
+protected:
+ bool wantDataPending;
+ qint64 m_amount;
+ char *m_data;
+ QByteArray m_dataArray;
+ bool m_atEnd;
+ qint64 m_size;
+public:
+ QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
+ : QNonContiguousByteDevice(),
+ wantDataPending(false),
+ m_amount(0),
+ m_data(0),
+ m_atEnd(aE),
+ m_size(s)
+ {
+ }
+
+ ~QNonContiguousByteDeviceThreadForwardImpl()
+ {
+ }
+
+ const char* readPointer(qint64 maximumLength, qint64 &len)
+ {
+ if (m_amount == 0 && wantDataPending == false) {
+ len = 0;
+ wantDataPending = true;
+ emit wantData(maximumLength);
+ } else if (m_amount == 0 && wantDataPending == true) {
+ // Do nothing, we already sent a wantData signal and wait for results
+ len = 0;
+ } else if (m_amount > 0) {
+ len = m_amount;
+ return m_data;
+ }
+ // cannot happen
+ return 0;
+ }
+
+ bool advanceReadPointer(qint64 a)
+ {
+ if (m_data == 0)
+ return false;
+
+ m_amount -= a;
+ m_data += a;
+
+ // To main thread to inform about our state
+ emit processedData(a);
+
+ // FIXME possible optimization, already ask user thread for some data
+
+ return true;
+ }
+
+ bool atEnd()
+ {
+ if (m_amount > 0)
+ return false;
+ else
+ return m_atEnd;
+ }
+
+ bool reset()
+ {
+ m_amount = 0;
+ m_data = 0;
+
+ // Communicate as BlockingQueuedConnection
+ bool b = false;
+ emit resetData(&b);
+ return b;
+ }
+
+ qint64 size()
+ {
+ return m_size;
+ }
+
+public slots:
+ // From user thread:
+ void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
+ {
+ wantDataPending = false;
+
+ m_dataArray = dataArray;
+ m_data = const_cast<char*>(m_dataArray.constData());
+ m_amount = dataArray.size();
+
+ m_atEnd = dataAtEnd;
+ m_size = dataSize;
+
+ // This will tell the HTTP code (QHttpNetworkConnectionChannel) that we have data available now
+ emit readyRead();
+ }
+
+signals:
+ // void readyRead(); in parent class
+ // void readProgress(qint64 current, qint64 total); happens in the main thread with the real bytedevice
+
+ // to main thread:
+ void wantData(qint64);
+ void processedData(qint64);
+ void resetData(bool *b);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif // QHTTPTHREADDELEGATE_H
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
new file mode 100644
index 0000000..1b15cf9
--- /dev/null
+++ b/src/network/access/qnetworkaccessauthenticationmanager.cpp
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessmanager.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
+
+#include "QtCore/qbuffer.h"
+#include "QtCore/qurl.h"
+#include "QtCore/qvector.h"
+#include "QtCore/QMutexLocker"
+#include "QtNetwork/qauthenticator.h"
+
+QT_BEGIN_NAMESPACE
+
+
+
+
+class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
+ public QNetworkAccessCache::CacheableObject
+{
+public:
+ QNetworkAuthenticationCache()
+ {
+ setExpires(false);
+ setShareable(true);
+ reserve(1);
+ }
+
+ QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
+ {
+ iterator it = qLowerBound(begin(), end(), domain);
+ if (it == end() && !isEmpty())
+ --it;
+ if (it == end() || !domain.startsWith(it->domain))
+ return 0;
+ return &*it;
+ }
+
+ void insert(const QString &domain, const QString &user, const QString &password)
+ {
+ QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
+ if (closestMatch && closestMatch->domain == domain) {
+ // we're overriding the current credentials
+ closestMatch->user = user;
+ closestMatch->password = password;
+ } else {
+ QNetworkAuthenticationCredential newCredential;
+ newCredential.domain = domain;
+ newCredential.user = user;
+ newCredential.password = password;
+
+ if (closestMatch)
+ QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
+ else
+ QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
+ }
+ }
+
+ virtual void dispose() { delete this; }
+};
+
+#ifndef QT_NO_NETWORKPROXY
+static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
+{
+ QUrl key;
+
+ switch (proxy.type()) {
+ case QNetworkProxy::Socks5Proxy:
+ key.setScheme(QLatin1String("proxy-socks5"));
+ break;
+
+ case QNetworkProxy::HttpProxy:
+ case QNetworkProxy::HttpCachingProxy:
+ key.setScheme(QLatin1String("proxy-http"));
+ break;
+
+ case QNetworkProxy::FtpCachingProxy:
+ key.setScheme(QLatin1String("proxy-ftp"));
+ break;
+
+ case QNetworkProxy::DefaultProxy:
+ case QNetworkProxy::NoProxy:
+ // shouldn't happen
+ return QByteArray();
+
+ // no default:
+ // let there be errors if a new proxy type is added in the future
+ }
+
+ if (key.scheme().isEmpty())
+ // proxy type not handled
+ return QByteArray();
+
+ key.setUserName(proxy.user());
+ key.setHost(proxy.hostName());
+ key.setPort(proxy.port());
+ key.setFragment(realm);
+ return "auth:" + key.toEncoded();
+}
+#endif
+
+static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
+{
+ QUrl copy = url;
+ copy.setFragment(realm);
+ return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
+}
+
+
+#ifndef QT_NO_NETWORKPROXY
+void QNetworkAccessAuthenticationManager::cacheProxyCredentials(const QNetworkProxy &p,
+ const QAuthenticator *authenticator)
+{
+ Q_ASSERT(authenticator);
+ Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
+ Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
+
+ QMutexLocker mutexLocker(&mutex);
+
+ QString realm = authenticator->realm();
+ QNetworkProxy proxy = p;
+ proxy.setUser(authenticator->user());
+ // Set two credentials: one with the username and one without
+ do {
+ // Set two credentials actually: one with and one without the realm
+ do {
+ QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
+ if (cacheKey.isEmpty())
+ return; // should not happen
+
+ QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
+ auth->insert(QString(), authenticator->user(), authenticator->password());
+ authenticationCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
+
+ if (realm.isEmpty()) {
+ break;
+ } else {
+ realm.clear();
+ }
+ } while (true);
+
+ if (proxy.user().isEmpty())
+ break;
+ else
+ proxy.setUser(QString());
+ } while (true);
+}
+
+QNetworkAuthenticationCredential
+QNetworkAccessAuthenticationManager::fetchCachedProxyCredentials(const QNetworkProxy &p,
+ const QAuthenticator *authenticator)
+{
+ QNetworkProxy proxy = p;
+ if (proxy.type() == QNetworkProxy::DefaultProxy) {
+ proxy = QNetworkProxy::applicationProxy();
+ }
+ if (!proxy.password().isEmpty())
+ return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
+
+ QString realm;
+ if (authenticator)
+ realm = authenticator->realm();
+
+ QMutexLocker mutexLocker(&mutex);
+ QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
+ if (cacheKey.isEmpty())
+ return QNetworkAuthenticationCredential();
+ if (!authenticationCache.hasEntry(cacheKey))
+ return QNetworkAuthenticationCredential();
+
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ QNetworkAuthenticationCredential cred = *auth->findClosestMatch(QString());
+ authenticationCache.releaseEntry(cacheKey);
+
+ // proxy cache credentials always have exactly one item
+ Q_ASSERT_X(!cred.isNull(), "QNetworkAccessManager",
+ "Internal inconsistency: found a cache key for a proxy, but it's empty");
+ return cred;
+}
+
+#endif
+
+void QNetworkAccessAuthenticationManager::cacheCredentials(const QUrl &url,
+ const QAuthenticator *authenticator)
+{
+ Q_ASSERT(authenticator);
+ QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
+ QString realm = authenticator->realm();
+
+ QMutexLocker mutexLocker(&mutex);
+
+ // Set two credentials actually: one with and one without the username in the URL
+ QUrl copy = url;
+ copy.setUserName(authenticator->user());
+ do {
+ QByteArray cacheKey = authenticationKey(copy, realm);
+ if (authenticationCache.hasEntry(cacheKey)) {
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ auth->insert(domain, authenticator->user(), authenticator->password());
+ authenticationCache.releaseEntry(cacheKey);
+ } else {
+ QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
+ auth->insert(domain, authenticator->user(), authenticator->password());
+ authenticationCache.addEntry(cacheKey, auth);
+ }
+
+ if (copy.userName().isEmpty()) {
+ break;
+ } else {
+ copy.setUserName(QString());
+ }
+ } while (true);
+}
+
+/*!
+ Fetch the credential data from the credential cache.
+
+ If auth is 0 (as it is when called from createRequest()), this will try to
+ look up with an empty realm. That fails in most cases for HTTP (because the
+ realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
+ never sends the credentials on the first attempt: it needs to find out what
+ authentication methods the server supports.
+
+ For FTP, realm is always empty.
+*/
+QNetworkAuthenticationCredential
+QNetworkAccessAuthenticationManager::fetchCachedCredentials(const QUrl &url,
+ const QAuthenticator *authentication)
+{
+ if (!url.password().isEmpty())
+ return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
+
+ QString realm;
+ if (authentication)
+ realm = authentication->realm();
+
+ QByteArray cacheKey = authenticationKey(url, realm);
+
+ QMutexLocker mutexLocker(&mutex);
+ if (!authenticationCache.hasEntry(cacheKey))
+ return QNetworkAuthenticationCredential();
+
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ QNetworkAuthenticationCredential cred = *auth->findClosestMatch(url.path());
+ authenticationCache.releaseEntry(cacheKey);
+ return cred;
+}
+
+void QNetworkAccessAuthenticationManager::clearCache()
+{
+ authenticationCache.clear();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
new file mode 100644
index 0000000..ddfc116
--- /dev/null
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
+#define QNETWORKACCESSAUTHENTICATIONMANAGER_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 "qnetworkaccessmanager.h"
+#include "qnetworkaccesscache_p.h"
+#include "qnetworkaccessbackend_p.h"
+#include "QtNetwork/qnetworkproxy.h"
+#include "QtCore/QMutex"
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QAbstractNetworkCache;
+class QNetworkAuthenticationCredential;
+class QNetworkCookieJar;
+
+class QNetworkAuthenticationCredential
+{
+public:
+ QString domain;
+ QString user;
+ QString password;
+ bool isNull() {
+ return domain.isNull();
+ }
+};
+Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
+inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
+{ return t1.domain < t2; }
+
+class QNetworkAccessAuthenticationManager
+{
+public:
+ QNetworkAccessAuthenticationManager() { };
+
+ void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url,
+ const QAuthenticator *auth = 0);
+
+#ifndef QT_NO_NETWORKPROXY
+ void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy,
+ const QAuthenticator *auth = 0);
+#endif
+
+ void clearCache();
+
+protected:
+ QNetworkAccessCache authenticationCache;
+ QMutex mutex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index d2d96bc..2fae7d6 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -41,12 +41,13 @@
#include "qnetworkaccessbackend_p.h"
#include "qnetworkaccessmanager_p.h"
+#include "qnetworkconfigmanager.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "QtCore/qhash.h"
#include "QtCore/qmutex.h"
-#include "QtNetwork/qnetworksession.h"
+#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkaccesscachebackend_p.h"
#include "qabstractnetworkcache.h"
@@ -106,12 +107,10 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
{
- QNonContiguousByteDevice* device = 0;
-
if (reply->outgoingDataBuffer)
- device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer);
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer));
else if (reply->outgoingData) {
- device = QNonContiguousByteDeviceFactory::create(reply->outgoingData);
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingData));
} else {
return 0;
}
@@ -120,21 +119,20 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
QVariant(false)) == QVariant(true);
if (bufferDisallowed)
- device->disableReset();
-
- // make sure we delete this later
- device->setParent(this);
+ uploadByteDevice->disableReset();
- connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
+ // We want signal emissions only for normal asynchronous uploads
+ if (!isSynchronous())
+ connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
- return device;
+ return uploadByteDevice.data();
}
// need to have this function since the reply is a private member variable
// and the special backends need to access this.
void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
- if (reply->isFinished())
+ if (reply->isFinished)
return;
reply->emitUploadProgress(bytesSent, bytesTotal);
}
@@ -241,6 +239,17 @@ void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
reply->appendDownstreamData(data);
}
+// not actually appending data, it was already written to the user buffer
+void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
+}
+
+char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
+{
+ return reply->getDownloadBuffer(size);
+}
+
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
{
return reply->q_func()->header(header);
@@ -316,11 +325,6 @@ void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator
manager->authenticationRequired(this, authenticator);
}
-void QNetworkAccessBackend::cacheCredentials(QAuthenticator *authenticator)
-{
- manager->cacheCredentials(this->reply->url, authenticator);
-}
-
void QNetworkAccessBackend::metaDataChanged()
{
reply->metaDataChanged();
@@ -340,8 +344,6 @@ void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
#endif
}
-#ifndef QT_NO_BEARERMANAGEMENT
-
/*!
Starts the backend. Returns true if the backend is started. Returns false if the backend
could not be started due to an unopened or roaming session. The caller should recall this
@@ -349,29 +351,62 @@ void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
*/
bool QNetworkAccessBackend::start()
{
- if (!manager->networkSession) {
- open();
- return true;
- }
-
- // This is not ideal.
- const QString host = reply->url.host();
- if (host == QLatin1String("localhost") ||
- QHostAddress(host) == QHostAddress::LocalHost ||
- QHostAddress(host) == QHostAddress::LocalHostIPv6) {
- // Don't need an open session for localhost access.
- open();
- return true;
+#ifndef QT_NO_BEARERMANAGEMENT
+ // For bearer, check if session start is required
+ if (manager->networkSession) {
+ // session required
+ if (manager->networkSession->isOpen() &&
+ manager->networkSession->state() == QNetworkSession::Connected) {
+ // Session is already open and ready to use.
+ // copy network session down to the backend
+ setProperty("_q_networksession", QVariant::fromValue(manager->networkSession));
+ } else {
+ // Session not ready, but can skip for loopback connections
+
+ // This is not ideal.
+ const QString host = reply->url.host();
+
+ if (host == QLatin1String("localhost") ||
+ QHostAddress(host) == QHostAddress::LocalHost ||
+ QHostAddress(host) == QHostAddress::LocalHostIPv6) {
+ // Don't need an open session for localhost access.
+ } else {
+ // need to wait for session to be opened
+ return false;
+ }
+ }
}
+#endif
- if (manager->networkSession->isOpen() &&
- manager->networkSession->state() == QNetworkSession::Connected) {
- open();
- return true;
+#ifndef QT_NO_NETWORKPROXY
+#ifndef QT_NO_BEARERMANAGEMENT
+ // Get the proxy settings from the network session (in the case of service networks,
+ // the proxy settings change depending which AP was activated)
+ QNetworkSession *session = manager->networkSession.data();
+ QNetworkConfiguration config;
+ if (session) {
+ QNetworkConfigurationManager configManager;
+ // The active configuration tells us what IAP is in use
+ QVariant v = session->sessionProperty(QLatin1String("ActiveConfiguration"));
+ if (v.isValid())
+ config = configManager.configurationFromIdentifier(qvariant_cast<QString>(v));
+ // Fallback to using the configuration if no active configuration
+ if (!config.isValid())
+ config = session->configuration();
+ // or unspecified configuration if that is no good either
+ if (!config.isValid())
+ config = QNetworkConfiguration();
}
+ reply->proxyList = manager->queryProxy(QNetworkProxyQuery(config, url()));
+#else // QT_NO_BEARERMANAGEMENT
+ // Without bearer management, the proxy depends only on the url
+ reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
+#endif
+#endif
- return false;
+ // now start the request
+ open();
+ return true;
}
-#endif
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 45be26e..ff05306 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -70,7 +70,6 @@ class QNetworkAccessManagerPrivate;
class QNetworkReplyImplPrivate;
class QAbstractNetworkCache;
class QNetworkCacheMetaData;
-class QNetworkAccessBackendUploadIODevice;
class QNonContiguousByteDevice;
// Should support direct file upload from disk or download to disk.
@@ -175,12 +174,17 @@ protected:
// Create the device used for reading the upload data
QNonContiguousByteDevice* createUploadByteDevice();
-
// 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(QByteDataBuffer &list);
+ // not actually appending data, it was already written to the user buffer
+ void writeDownstreamDataDownloadBuffer(qint64, qint64);
+ char* getDownloadBuffer(qint64);
+
+ QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+
public slots:
// for task 251801, needs to be a slot to be called asynchronously
void writeDownstreamData(QIODevice *data);
@@ -192,19 +196,22 @@ protected slots:
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
#endif
void authenticationRequired(QAuthenticator *auth);
- void cacheCredentials(QAuthenticator *auth);
void metaDataChanged();
void redirectionRequested(const QUrl &destination);
void sslErrors(const QList<QSslError> &errors);
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
+protected:
+ // FIXME In the long run we should get rid of our QNAM architecture
+ // and scrap this ReplyImpl/Backend distinction.
+ QNetworkAccessManagerPrivate *manager;
+ QNetworkReplyImplPrivate *reply;
+
private:
friend class QNetworkAccessManager;
friend class QNetworkAccessManagerPrivate;
- friend class QNetworkAccessBackendUploadIODevice;
friend class QNetworkReplyImplPrivate;
- QNetworkAccessManagerPrivate *manager;
- QNetworkReplyImplPrivate *reply;
+
bool synchronous;
};
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 37dbf04..1881dac 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -66,6 +66,7 @@ void QNetworkAccessCacheBackend::open()
QString msg = QCoreApplication::translate("QNetworkAccessCacheBackend", "Error opening %1")
.arg(this->url().toString());
error(QNetworkReply::ContentNotFoundError, msg);
+ } else {
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
}
finished();
@@ -85,14 +86,18 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
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)
+ for ( ; it != end; ++it) {
+ if (it->first.toLower() == "cache-control" &&
+ it->second.toLower().contains("must-revalidate")) {
+ return false;
+ }
setRawHeader(it->first, it->second);
+ }
// handle a possible redirect
QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp
deleted file mode 100644
index dcd5e98..0000000
--- a/src/network/access/qnetworkaccessdatabackend.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/****************************************************************************
-**
-** 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$
-** GNU Lesser General Public License Usage
-** 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.
-**
-** 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.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkaccessdatabackend_p.h"
-#include "qnetworkrequest.h"
-#include "qnetworkreply.h"
-#include "qurlinfo.h"
-#include "private/qdataurl_p.h"
-#include <qcoreapplication.h>
-
-QT_BEGIN_NAMESPACE
-
-QNetworkAccessBackend *
-QNetworkAccessDataBackendFactory::create(QNetworkAccessManager::Operation,
- const QNetworkRequest &request) const
-{
- if (request.url().scheme() == QLatin1String("data"))
- return new QNetworkAccessDataBackend;
-
- return 0;
-}
-
-QNetworkAccessDataBackend::QNetworkAccessDataBackend()
-{
-}
-
-QNetworkAccessDataBackend::~QNetworkAccessDataBackend()
-{
-}
-
-void QNetworkAccessDataBackend::open()
-{
- QUrl uri = request().url();
-
- if (operation() != QNetworkAccessManager::GetOperation &&
- operation() != QNetworkAccessManager::HeadOperation) {
- // data: doesn't support anything but GET
- const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
- "Operation not supported on %1")
- .arg(uri.toString());
- error(QNetworkReply::ContentOperationNotPermittedError, msg);
- finished();
- return;
- }
-
- QPair<QString, QByteArray> decoded = qDecodeDataUrl(uri);
-
- if (! decoded.first.isNull()) {
- setHeader(QNetworkRequest::ContentTypeHeader, decoded.first);
- setHeader(QNetworkRequest::ContentLengthHeader, decoded.second.size());
- emit metaDataChanged();
-
- QByteDataBuffer list;
- list.append(decoded.second);
- decoded.second.clear(); // important because of implicit sharing!
- writeDownstreamData(list);
-
- finished();
- return;
- }
-
- // something wrong with this URI
- const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
- "Invalid URI: %1").arg(uri.toString());
- error(QNetworkReply::ProtocolFailure, msg);
- finished();
-}
-
-void QNetworkAccessDataBackend::closeDownstreamChannel()
-{
-}
-
-void QNetworkAccessDataBackend::closeUpstreamChannel()
-{
-}
-
-bool QNetworkAccessDataBackend::waitForDownstreamReadyRead(int)
-{
- return false;
-}
-
-bool QNetworkAccessDataBackend::waitForUpstreamBytesWritten(int)
-{
- return false;
-}
-
-bool QNetworkAccessDataBackend::processRequestSynchronously()
-{
-#ifndef QT_NO_BEARERMANAGEMENT
- start();
-#else
- open();
-#endif
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 2b2014b..7c4adca 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -65,11 +65,19 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("qrc") || !url.toLocalFile().isEmpty())
+ if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- else if (!url.isEmpty() && url.authority().isEmpty()) {
- // check if QFile could, in theory, open this URL
+ } else if (!url.isEmpty() && url.authority().isEmpty()) {
+ // check if QFile could, in theory, open this URL via the file engines
+ // it has to be in the format:
+ // prefix:path/to/file
+ // or prefix:/path/to/file
+ //
+ // this construct here must match the one below in open()
QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
+ // On Windows and Symbian the drive letter is detected as the scheme.
+ if (fi.exists() && (url.scheme().isEmpty() || (url.scheme().length() == 1)))
+ qWarning("QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files");
if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
return new QNetworkAccessFileBackend;
}
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index 0a3eb7c..0461da2 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -77,7 +77,7 @@ QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("ftp"))
+ if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
return new QNetworkAccessFtpBackend;
return 0;
}
@@ -153,6 +153,10 @@ void QNetworkAccessFtpBackend::open()
if (!objectCache->requestEntry(cacheKey, this,
SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
ftp = new QNetworkAccessCachedFtpConnection;
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the QFtp
+ ftp->setProperty("_q_networksession", property("_q_networksession"));
+#endif
#ifndef QT_NO_NETWORKPROXY
if (proxy.type() == QNetworkProxy::FtpCachingProxy)
ftp->setProxy(proxy.hostName(), proxy.port());
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index abef8ab..8a247ce 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -47,65 +47,25 @@
#include "qabstractnetworkcache.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
+#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkrequest_p.h"
#include "qnetworkcookie_p.h"
#include "QtCore/qdatetime.h"
#include "QtCore/qelapsedtimer.h"
#include "QtNetwork/qsslconfiguration.h"
+#include "qhttpthreaddelegate_p.h"
+#include "qthread.h"
#ifndef QT_NO_HTTP
#include <string.h> // for strchr
-QT_BEGIN_NAMESPACE
+Q_DECLARE_METATYPE(QSharedPointer<char>)
-enum {
- DefaultHttpPort = 80,
- DefaultHttpsPort = 443
-};
+QT_BEGIN_NAMESPACE
class QNetworkProxy;
-static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy *proxy)
-{
- QByteArray result;
- QUrl copy = backend->url();
- bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
- copy.setPort(copy.port(isEncrypted ? DefaultHttpsPort : DefaultHttpPort));
- result = copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath |
- QUrl::RemoveQuery | QUrl::RemoveFragment);
-
-#ifndef QT_NO_NETWORKPROXY
- if (proxy->type() != QNetworkProxy::NoProxy) {
- QUrl key;
-
- switch (proxy->type()) {
- case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
- break;
-
- case QNetworkProxy::HttpProxy:
- case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
- break;
-
- default:
- break;
- }
-
- if (!key.scheme().isEmpty()) {
- key.setUserName(proxy->user());
- key.setHost(proxy->hostName());
- key.setPort(proxy->port());
- key.setEncodedQuery(result);
- result = key.toEncoded();
- }
- }
-#endif
-
- return "http-connection:" + result;
-}
-
static inline bool isSeparator(register char c)
{
static const char separators[] = "()<>@,;:\\\"/[]?={}";
@@ -230,71 +190,13 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,
return 0;
}
-static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
-{
- QNetworkReply::NetworkError code;
- // we've got an error
- switch (httpStatusCode) {
- case 401: // Authorization required
- code = QNetworkReply::AuthenticationRequiredError;
- break;
-
- case 403: // Access denied
- code = QNetworkReply::ContentOperationNotPermittedError;
- break;
-
- case 404: // Not Found
- code = QNetworkReply::ContentNotFoundError;
- break;
-
- case 405: // Method Not Allowed
- code = QNetworkReply::ContentOperationNotPermittedError;
- break;
-
- case 407:
- code = QNetworkReply::ProxyAuthenticationRequiredError;
- break;
-
- default:
- if (httpStatusCode > 500) {
- // some kind of server error
- code = QNetworkReply::ProtocolUnknownError;
- } else if (httpStatusCode >= 400) {
- // content error we did not handle above
- code = QNetworkReply::UnknownContentError;
- } else {
- qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
- httpStatusCode, qPrintable(url.toString()));
- code = QNetworkReply::ProtocolFailure;
- }
- }
-
- return code;
-}
-
-class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
- public QNetworkAccessCache::CacheableObject
-{
- // Q_OBJECT
-public:
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
- : QHttpNetworkConnection(hostName, port, encrypt)
- {
- setExpires(true);
- setShareable(true);
- }
-
- virtual void dispose()
- {
-#if 0 // sample code; do this right with the API
- Q_ASSERT(!isWorking());
-#endif
- delete this;
- }
-};
-
QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
- : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0)
+ : QNetworkAccessBackend()
+ , statusCode(0)
+ , pendingDownloadDataEmissions(new QAtomicInt())
+ , pendingDownloadProgressEmissions(new QAtomicInt())
+ , loadingFromCache(false)
+ , usingZerocopyDownloadBuffer(false)
#ifndef QT_NO_OPENSSL
, pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
#endif
@@ -304,77 +206,49 @@ QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend()
{
- if (http)
- disconnectFromHttp();
+ // This will do nothing if the request was already finished or aborted
+ emit abortHttpRequest();
+
#ifndef QT_NO_OPENSSL
delete pendingSslConfiguration;
#endif
}
-void QNetworkAccessHttpBackend::disconnectFromHttp()
-{
- if (http) {
- // This is abut disconnecting signals, not about disconnecting TCP connections
- disconnect(http, 0, this, 0);
-
- // Get the object cache that stores our QHttpNetworkConnection objects
- QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
-
- // synchronous calls are not put into the cache, so for them the key is empty
- if (!cacheKey.isEmpty())
- cache->releaseEntry(cacheKey);
- }
-
- // This is abut disconnecting signals, not about disconnecting TCP connections
- if (httpReply)
- disconnect(httpReply, 0, this, 0);
-
- http = 0;
- httpReply = 0;
- cacheKey.clear();
-}
-
-void QNetworkAccessHttpBackend::finished()
-{
- if (http)
- disconnectFromHttp();
- // call parent
- QNetworkAccessBackend::finished();
-}
-
/*
For a given httpRequest
1) If AlwaysNetwork, return
2) If we have a cache entry for this url populate headers so the server can return 304
3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
*/
-void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache)
+bool QNetworkAccessHttpBackend::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
{
QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
(QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
- // forced reload from the network
- // tell any caching proxy servers to reload too
- httpRequest.setHeaderField("Cache-Control", "no-cache");
- httpRequest.setHeaderField("Pragma", "no-cache");
- return;
+ // If the request does not already specify preferred cache-control
+ // force reload from the network and tell any caching proxy servers to reload too
+ if (!request().rawHeaderList().contains("Cache-Control")) {
+ httpRequest.setHeaderField("Cache-Control", "no-cache");
+ httpRequest.setHeaderField("Pragma", "no-cache");
+ }
+ return false;
}
// 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;
+ return false;
QAbstractNetworkCache *nc = networkCache();
if (!nc)
- return; // no local cache
+ return false; // no local cache
QNetworkCacheMetaData metaData = nc->metaData(url());
if (!metaData.isValid())
- return; // not in cache
+ return false; // not in cache
if (!metaData.saveToDisk())
- return;
+ return false;
QNetworkHeadersPrivate cacheHeaders;
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
@@ -388,13 +262,11 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
if (lastModified.isValid())
httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
- if (CacheLoadControlAttribute == QNetworkRequest::PreferNetwork) {
- it = cacheHeaders.findRawHeader("Cache-Control");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
- return;
- }
+ it = cacheHeaders.findRawHeader("Cache-Control");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+ if (cacheControl.contains("must-revalidate"))
+ return false;
}
QDateTime currentDateTime = QDateTime::currentDateTime();
@@ -465,14 +337,12 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
#endif
if (!response_is_fresh)
- return;
+ return false;
- loadedFromCache = true;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
#endif
- if (!sendCacheContents(metaData))
- loadedFromCache = false;
+ return sendCacheContents(metaData);
}
static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio)
@@ -490,30 +360,103 @@ static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& pr
void QNetworkAccessHttpBackend::postRequest()
{
+ QThread *thread = 0;
+ if (isSynchronous()) {
+ // A synchronous HTTP request uses its own thread
+ thread = new QThread();
+ QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+ } else if (!manager->httpThread) {
+ // We use the manager-global thread.
+ // At some point we could switch to having multiple threads if it makes sense.
+ manager->httpThread = new QThread();
+ QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater()));
+ manager->httpThread->start();
+#ifndef QT_NO_NETWORKPROXY
+ qRegisterMetaType<QNetworkProxy>("QNetworkProxy");
+#endif
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
+ qRegisterMetaType<QSslConfiguration>("QSslConfiguration");
+#endif
+ qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >");
+ qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest");
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+ qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>");
+
+ thread = manager->httpThread;
+ } else {
+ // Asynchronous request, thread already exists
+ thread = manager->httpThread;
+ }
+
+ QUrl url = request().url();
+ httpRequest.setUrl(url);
+
+ bool ssl = url.scheme().toLower() == QLatin1String("https");
+ setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
+ httpRequest.setSsl(ssl);
+
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy transparentProxy, cacheProxy;
+
+ foreach (const QNetworkProxy &p, proxyList()) {
+ // use the first proxy that works
+ // for non-encrypted connections, any transparent or HTTP proxy
+ // for encrypted, only transparent proxies
+ if (!ssl
+ && (p.capabilities() & QNetworkProxy::CachingCapability)
+ && (p.type() == QNetworkProxy::HttpProxy ||
+ p.type() == QNetworkProxy::HttpCachingProxy)) {
+ cacheProxy = p;
+ transparentProxy = QNetworkProxy::NoProxy;
+ break;
+ }
+ if (p.isTransparentProxy()) {
+ transparentProxy = p;
+ cacheProxy = QNetworkProxy::NoProxy;
+ break;
+ }
+ }
+
+ // check if at least one of the proxies
+ if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
+ cacheProxy.type() == QNetworkProxy::DefaultProxy) {
+ // unsuitable proxies
+ QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
+ Q_ARG(QString, tr("No suitable proxy found")));
+ QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection);
+ return;
+ }
+#endif
+
+
bool loadedFromCache = false;
- QHttpNetworkRequest httpRequest;
httpRequest.setPriority(convert(request().priority()));
+
switch (operation()) {
case QNetworkAccessManager::GetOperation:
httpRequest.setOperation(QHttpNetworkRequest::Get);
- validateCache(httpRequest, loadedFromCache);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
break;
case QNetworkAccessManager::HeadOperation:
httpRequest.setOperation(QHttpNetworkRequest::Head);
- validateCache(httpRequest, loadedFromCache);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
break;
case QNetworkAccessManager::PostOperation:
invalidateCache();
httpRequest.setOperation(QHttpNetworkRequest::Post);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
break;
case QNetworkAccessManager::PutOperation:
invalidateCache();
httpRequest.setOperation(QHttpNetworkRequest::Put);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
break;
case QNetworkAccessManager::DeleteOperation:
@@ -524,7 +467,7 @@ void QNetworkAccessHttpBackend::postRequest()
case QNetworkAccessManager::CustomOperation:
invalidateCache(); // for safety reasons, we don't know what the operation does
httpRequest.setOperation(QHttpNetworkRequest::Custom);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
httpRequest.setCustomVerb(request().attribute(
QNetworkRequest::CustomVerbAttribute).toByteArray());
break;
@@ -533,7 +476,12 @@ void QNetworkAccessHttpBackend::postRequest()
break; // can't happen
}
- httpRequest.setUrl(url());
+ if (loadedFromCache) {
+ // commented this out since it will be called later anyway
+ // by copyFinished()
+ //QNetworkAccessBackend::finished();
+ return; // no need to send the request! :)
+ }
QList<QByteArray> headers = request().rawHeaderList();
if (resumeOffset != 0) {
@@ -558,16 +506,10 @@ void QNetworkAccessHttpBackend::postRequest()
httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
}
}
+
foreach (const QByteArray &header, headers)
httpRequest.setHeaderField(header, request().rawHeader(header));
- if (loadedFromCache) {
- // commented this out since it will be called later anyway
- // by copyFinished()
- //QNetworkAccessBackend::finished();
- return; // no need to send the request! :)
- }
-
if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
httpRequest.setPipeliningAllowed(true);
@@ -576,195 +518,223 @@ void QNetworkAccessHttpBackend::postRequest()
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
httpRequest.setWithCredentials(false);
- httpReply = http->sendRequest(httpRequest);
- httpReply->setParent(this);
-#ifndef QT_NO_OPENSSL
- if (pendingSslConfiguration)
- httpReply->setSslConfiguration(*pendingSslConfiguration);
- if (pendingIgnoreAllSslErrors)
- httpReply->ignoreSslErrors();
- httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
- connect(httpReply, SIGNAL(sslErrors(QList<QSslError>)),
- SLOT(sslErrors(QList<QSslError>)));
-#endif
- connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
- connect(httpReply, SIGNAL(finished()), SLOT(replyFinished()));
- connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
- SLOT(httpError(QNetworkReply::NetworkError,QString)));
- connect(httpReply, SIGNAL(headerChanged()), SLOT(replyHeaderChanged()));
- connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
- SLOT(httpCacheCredentials(QHttpNetworkRequest,QAuthenticator*)));
-#ifndef QT_NO_NETWORKPROXY
- connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+ // Create the HTTP thread delegate
+ QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
+#ifndef Q_NO_BEARERMANAGEMENT
+ QVariant v(property("_q_networksession"));
+ if (v.isValid())
+ delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v);
#endif
- connect(httpReply, SIGNAL(authenticationRequired(const QHttpNetworkRequest,QAuthenticator*)),
- SLOT(httpAuthenticationRequired(const QHttpNetworkRequest,QAuthenticator*)));
-}
-
-void QNetworkAccessHttpBackend::invalidateCache()
-{
- QAbstractNetworkCache *nc = networkCache();
- if (nc)
- nc->remove(url());
-}
-
-void QNetworkAccessHttpBackend::open()
-{
- QUrl url = request().url();
- bool encrypt = url.scheme().toLower() == QLatin1String("https");
- setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, encrypt);
- // set the port number in the reply if it wasn't set
- url.setPort(url.port(encrypt ? DefaultHttpsPort : DefaultHttpPort));
+ // For the synchronous HTTP, this is the normal way the delegate gets deleted
+ // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
+ connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
- QNetworkProxy *theProxy = 0;
+ // Set the properties it needs
+ delegate->httpRequest = httpRequest;
#ifndef QT_NO_NETWORKPROXY
- QNetworkProxy transparentProxy, cacheProxy;
+ delegate->cacheProxy = cacheProxy;
+ delegate->transparentProxy = transparentProxy;
+#endif
+ delegate->ssl = ssl;
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ delegate->incomingSslConfiguration = request().sslConfiguration();
+#endif
- foreach (const QNetworkProxy &p, proxyList()) {
- // use the first proxy that works
- // for non-encrypted connections, any transparent or HTTP proxy
- // for encrypted, only transparent proxies
- if (!encrypt
- && (p.capabilities() & QNetworkProxy::CachingCapability)
- && (p.type() == QNetworkProxy::HttpProxy ||
- p.type() == QNetworkProxy::HttpCachingProxy)) {
- cacheProxy = p;
- transparentProxy = QNetworkProxy::NoProxy;
- theProxy = &cacheProxy;
- break;
+ // Do we use synchronous HTTP?
+ delegate->synchronous = isSynchronous();
+
+ // The authentication manager is used to avoid the BlockingQueuedConnection communication
+ // from HTTP thread to user thread in some cases.
+ delegate->authenticationManager = manager->authenticationManager;
+
+ if (!isSynchronous()) {
+ // Tell our zerocopy policy to the delegate
+ delegate->downloadBufferMaximumSize =
+ request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
+
+ // These atomic integers are used for signal compression
+ delegate->pendingDownloadData = pendingDownloadDataEmissions;
+ delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
+
+ // Connect the signals of the delegate to us
+ connect(delegate, SIGNAL(downloadData(QByteArray)),
+ this, SLOT(replyDownloadData(QByteArray)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadFinished()),
+ this, SLOT(replyFinished()),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+ this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
+ this, SLOT(replyDownloadProgressSlot(qint64,qint64)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
+ this, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
+ Qt::QueuedConnection);
+#ifndef QT_NO_OPENSSL
+ connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
+ this, SLOT(replySslConfigurationChanged(QSslConfiguration)),
+ Qt::QueuedConnection);
+#endif
+ // Those need to report back, therefire BlockingQueuedConnection
+ connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ Qt::BlockingQueuedConnection);
+#ifndef QT_NO_NETWORKPROXY
+ connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ Qt::BlockingQueuedConnection);
+#endif
+#ifndef QT_NO_OPENSSL
+ connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
+ this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
+ Qt::BlockingQueuedConnection);
+#endif
+ // This signal we will use to start the request.
+ connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
+ connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
+
+ if (uploadByteDevice) {
+ QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
+ new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());
+ if (uploadByteDevice->isResetDisabled())
+ forwardUploadDevice->disableReset();
+ forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
+ delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
+
+ // From main thread to user thread:
+ QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)),
+ forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);
+ QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
+ forwardUploadDevice, SIGNAL(readyRead()),
+ Qt::QueuedConnection);
+
+ // From http thread to user thread:
+ QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
+ this, SLOT(wantUploadDataSlot(qint64)));
+ QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
+ this, SLOT(sentUploadDataSlot(qint64)));
+ connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
+ this, SLOT(resetUploadDataSlot(bool*)),
+ Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
}
- if (p.isTransparentProxy()) {
- transparentProxy = p;
- cacheProxy = QNetworkProxy::NoProxy;
- theProxy = &transparentProxy;
- break;
+ } else if (isSynchronous()) {
+ connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
+
+ if (uploadByteDevice) {
+ // For the synchronous HTTP use case the use thread (this one here) is blocked
+ // so we cannot use the asynchronous upload architecture.
+ // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
+ // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
+ // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread
+ // since it only wraps a QRingBuffer
+ delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
}
}
- // check if at least one of the proxies
- if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
- cacheProxy.type() == QNetworkProxy::DefaultProxy) {
- // unsuitable proxies
- if (isSynchronous()) {
- error(QNetworkReply::ProxyNotFoundError, tr("No suitable proxy found"));
- finished();
+
+ // Move the delegate to the http thread
+ delegate->moveToThread(thread);
+ // This call automatically moves the uploadDevice too for the asynchronous case.
+
+ // Send an signal to the delegate so it starts working in the other thread
+ if (isSynchronous()) {
+ emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
+
+ if (delegate->incomingErrorCode != QNetworkReply::NoError) {
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength);
+ replyDownloadData(delegate->synchronousDownloadData);
+ httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
} else {
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
- Q_ARG(QString, tr("No suitable proxy found")));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength);
+ replyDownloadData(delegate->synchronousDownloadData);
}
- return;
- }
-#endif
- if (isSynchronous()) {
- // for synchronous requests, we just create a new connection
- http = new QHttpNetworkConnection(1, url.host(), url.port(), encrypt, this);
-#ifndef QT_NO_NETWORKPROXY
- http->setTransparentProxy(transparentProxy);
- http->setCacheProxy(cacheProxy);
-#endif
- postRequest();
- processRequestSynchronously();
+ // End the thread. It will delete itself from the finished() signal
+ thread->quit();
+
+ finished();
} else {
- // check if we have an open connection to this host
- cacheKey = makeCacheKey(this, theProxy);
- QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
- // the http object is actually a QHttpNetworkConnection
- http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey));
- if (http == 0) {
- // no entry in cache; create an object
- // the http object is actually a QHttpNetworkConnection
- http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt);
+ emit startHttpRequest(); // Signal to the HTTP thread and go back to user.
+ }
+}
-#ifndef QT_NO_NETWORKPROXY
- http->setTransparentProxy(transparentProxy);
- http->setCacheProxy(cacheProxy);
-#endif
+void QNetworkAccessHttpBackend::invalidateCache()
+{
+ QAbstractNetworkCache *nc = networkCache();
+ if (nc)
+ nc->remove(url());
+}
- // cache the QHttpNetworkConnection corresponding to this cache key
- cache->addEntry(cacheKey, static_cast<QNetworkAccessCachedHttpConnection *>(http.data()));
- }
- postRequest();
- }
+void QNetworkAccessHttpBackend::open()
+{
+ postRequest();
}
void QNetworkAccessHttpBackend::closeDownstreamChannel()
{
- // this indicates that the user closed the stream while the reply isn't finished yet
+ // FIXME Maybe we can get rid of this whole architecture part
}
void QNetworkAccessHttpBackend::downstreamReadyWrite()
{
- readFromHttp();
- if (httpReply && httpReply->bytesAvailable() == 0 && httpReply->isFinished())
- replyFinished();
+ // FIXME Maybe we can get rid of this whole architecture part
}
void QNetworkAccessHttpBackend::setDownstreamLimited(bool b)
{
- if (httpReply)
- httpReply->setDownstreamLimited(b);
+ Q_UNUSED(b);
+ // We know that readBuffer maximum size limiting is broken since quite a while.
+ // The task to fix this is QTBUG-15065
}
-void QNetworkAccessHttpBackend::replyReadyRead()
+void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d)
{
- readFromHttp();
-}
-
-void QNetworkAccessHttpBackend::readFromHttp()
-{
- if (!httpReply)
+ int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1;
+
+ if (pendingSignals > 0) {
+ // Some more signal emissions to this slot are pending.
+ // Instead of writing the downstream data, we wait
+ // and do it in the next call we get
+ // (signal comppression)
+ pendingDownloadData.append(d);
return;
-
- // We read possibly more than nextDownstreamBlockSize(), but
- // this is not a critical thing since it is already in the
- // memory anyway
-
- QByteDataBuffer list;
-
- while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) {
- list.append(httpReply->readAny());
}
- if (!list.isEmpty())
- writeDownstreamData(list);
+ pendingDownloadData.append(d);
+ d.clear();
+ // 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()
{
- if (httpReply->bytesAvailable())
- // we haven't read everything yet. Wait some more.
+ // We are already loading from cache, we still however
+ // got this signal because it was posted already
+ if (loadingFromCache)
return;
- int statusCode = httpReply->statusCode();
- if (statusCode >= 400) {
- // it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error downloading %1 - server replied: %2"));
- msg = msg.arg(url().toString(), httpReply->reasonPhrase());
- error(statusCodeFromHttp(httpReply->statusCode(), httpReply->url()), msg);
- }
-
-#ifndef QT_NO_OPENSSL
- // store the SSL configuration now
- // once we call finished(), we won't have access to httpReply anymore
- QSslConfiguration sslConfig = httpReply->sslConfiguration();
- if (pendingSslConfiguration) {
- *pendingSslConfiguration = sslConfig;
- } else if (!sslConfig.isNull()) {
- QT_TRY {
- pendingSslConfiguration = new QSslConfiguration(sslConfig);
- } QT_CATCH(...) {
- qWarning("QNetworkAccess: could not allocate a QSslConfiguration object for a SSL connection.");
- }
- }
-#endif
-
finished();
}
@@ -786,12 +756,27 @@ void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode)
}
}
-void QNetworkAccessHttpBackend::replyHeaderChanged()
+void QNetworkAccessHttpBackend::replyDownloadMetaData
+ (QList<QPair<QByteArray,QByteArray> > hm,
+ int sc,QString rp,bool pu,
+ QSharedPointer<char> db,
+ qint64 contentLength)
{
- setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, httpReply->isPipeliningUsed());
+ statusCode = sc;
+ reasonPhrase = rp;
+
+ // Download buffer
+ if (!db.isNull()) {
+ reply->setDownloadBuffer(db, contentLength);
+ usingZerocopyDownloadBuffer = true;
+ } else {
+ usingZerocopyDownloadBuffer = false;
+ }
+
+ setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
// reconstruct the HTTP header
- QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header();
+ QList<QPair<QByteArray, QByteArray> > headerMap = hm;
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
end = headerMap.constEnd();
QByteArray header;
@@ -808,11 +793,10 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
setRawHeader(it->first, value);
}
- setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpReply->statusCode());
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase());
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
// is it a redirection?
- const int statusCode = httpReply->statusCode();
checkForRedirect(statusCode);
if (statusCode >= 500 && statusCode < 600) {
@@ -854,19 +838,29 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
if (!isCachingEnabled())
setCachingEnabled(true);
}
+
metaDataChanged();
}
-void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
- QAuthenticator *auth)
+void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received, qint64 total)
{
- authenticationRequired(auth);
+ // we can be sure here that there is a download buffer
+
+ int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
+ if (pendingSignals > 0) {
+ // Let's ignore this signal and look at the next one coming in
+ // (signal comppression)
+ return;
+ }
+
+ // Now do the actual notification of new bytes
+ writeDownstreamDataDownloadBuffer(received, total);
}
-void QNetworkAccessHttpBackend::httpCacheCredentials(const QHttpNetworkRequest &,
- QAuthenticator *auth)
+void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
+ QAuthenticator *auth)
{
- cacheCredentials(auth);
+ authenticationRequired(auth);
}
void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
@@ -875,8 +869,56 @@ void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "http error!" << errorCode << errorString;
#endif
+
error(errorCode, errorString);
- finished();
+}
+
+#ifndef QT_NO_OPENSSL
+void QNetworkAccessHttpBackend::replySslErrors(
+ const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
+{
+ // Go to generic backend
+ sslErrors(list);
+ // Check if the callback set any ignore and return this here to http thread
+ if (pendingIgnoreAllSslErrors)
+ *ignoreAll = true;
+ if (!pendingIgnoreSslErrorsList.isEmpty())
+ *toBeIgnored = pendingIgnoreSslErrorsList;
+}
+
+void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfiguration &c)
+{
+ // Receiving the used SSL configuration from the HTTP thread
+ if (pendingSslConfiguration)
+ *pendingSslConfiguration = c;
+ else if (!c.isNull())
+ pendingSslConfiguration = new QSslConfiguration(c);
+}
+#endif
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r)
+{
+ *r = uploadByteDevice->reset();
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount)
+{
+ uploadByteDevice->advanceReadPointer(amount);
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize)
+{
+ // call readPointer
+ qint64 currentUploadDataLength = 0;
+ char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
+ // Let's make a copy of this data
+ QByteArray dataArray(data, currentUploadDataLength);
+
+ // Communicate back to HTTP thread
+ emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
}
/*
@@ -919,16 +961,18 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
// This needs to be emitted in the event loop because it can be reached at
// the direct code path of qnam.get(...) before the user has a chance
// to connect any signals.
- QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "metaDataChanged", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection);
qRegisterMetaType<QIODevice*>("QIODevice*");
- QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
+ QMetaObject::invokeMethod(this, "writeDownstreamData", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes";
#endif
- if (httpReply)
- disconnect(httpReply, SIGNAL(finished()), this, SLOT(replyFinished()));
+
+ // Set the following flag so we can ignore some signals from HTTP thread
+ // that would still come
+ loadingFromCache = true;
return true;
}
@@ -941,39 +985,29 @@ void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev)
#ifndef QT_NO_OPENSSL
void QNetworkAccessHttpBackend::ignoreSslErrors()
{
- if (httpReply)
- httpReply->ignoreSslErrors();
- else
- pendingIgnoreAllSslErrors = 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;
- }
+ // 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
{
- if (httpReply)
- config = httpReply->sslConfiguration();
- else if (pendingSslConfiguration)
+ if (pendingSslConfiguration)
config = *pendingSslConfiguration;
+ else
+ config = request().sslConfiguration();
}
void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig)
{
- if (httpReply)
- httpReply->setSslConfiguration(newconfig);
- else if (pendingSslConfiguration)
- *pendingSslConfiguration = newconfig;
- else
- pendingSslConfiguration = new QSslConfiguration(newconfig);
+ // Setting a SSL configuration on a reply is not supported. The user needs to set
+ // her/his QSslConfiguration on the QNetworkRequest.
+ Q_UNUSED(newconfig);
}
#endif
@@ -1075,7 +1109,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
bool canDiskCache;
// only cache GET replies by default, all other replies (POST, PUT, DELETE)
// are not cacheable by default (according to RFC 2616 section 9)
- if (httpReply->request().operation() == QHttpNetworkRequest::Get) {
+ if (httpRequest.operation() == QHttpNetworkRequest::Get) {
canDiskCache = true;
// 14.32
@@ -1093,7 +1127,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
canDiskCache = false;
// responses to POST might be cacheable
- } else if (httpReply->request().operation() == QHttpNetworkRequest::Post) {
+ } else if (httpRequest.operation() == QHttpNetworkRequest::Post) {
canDiskCache = false;
// some pages contain "expires:" and "cache-control: no-cache" field,
@@ -1107,12 +1141,11 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
}
metaData.setSaveToDisk(canDiskCache);
- int statusCode = httpReply->statusCode();
QNetworkCacheMetaData::AttributesMap attributes;
if (statusCode != 304) {
// update the status code
attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
- attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase());
+ attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
} else {
// this is a redirection, keep the attributes intact
attributes = oldMetaData.attributes();
@@ -1128,7 +1161,8 @@ bool QNetworkAccessHttpBackend::canResume() const
return false;
// Can only resume if server/resource supports Range header.
- if (httpReply->headerField("Accept-Ranges", "none") == "none")
+ QByteArray acceptRangesheaderName("Accept-Ranges");
+ if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none")
return false;
// We only support resuming for byte ranges.
@@ -1138,6 +1172,11 @@ bool QNetworkAccessHttpBackend::canResume() const
return false;
}
+ // If we're using a download buffer then we don't support resuming/migration
+ // right now. Too much trouble.
+ if (usingZerocopyDownloadBuffer)
+ return false;
+
return true;
}
@@ -1146,87 +1185,6 @@ void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset)
resumeOffset = offset;
}
-bool QNetworkAccessHttpBackend::processRequestSynchronously()
-{
- QHttpNetworkConnectionChannel *channel = &http->channels()[0];
-
- // Disconnect all socket signals. They will only confuse us when using waitFor*
- QObject::disconnect(channel->socket, 0, 0, 0);
-
- qint64 timeout = 20*1000; // 20 sec
- QElapsedTimer timeoutTimer;
-
- bool waitResult = channel->socket->waitForConnected(timeout);
- timeoutTimer.start();
-
- if (!waitResult || channel->socket->state() != QAbstractSocket::ConnectedState) {
- error(QNetworkReply::UnknownNetworkError, QLatin1String("could not connect"));
- return false;
- }
- channel->_q_connected(); // this will send the request (via sendRequest())
-
-#ifndef QT_NO_OPENSSL
- if (http->isSsl()) {
- qint64 remainingTimeEncrypted = timeout - timeoutTimer.elapsed();
- if (!static_cast<QSslSocket *>(channel->socket)->waitForEncrypted(remainingTimeEncrypted)) {
- error(QNetworkReply::SslHandshakeFailedError,
- QLatin1String("could not encrypt or timeout while encrypting"));
- return false;
- }
- channel->_q_encrypted();
- }
-#endif
-
- // if we get a 401 or 407, we might need to send the request twice, see below
- bool authenticating = false;
-
- do {
- channel->sendRequest();
-
- qint64 remainingTimeBytesWritten;
- while(channel->socket->bytesToWrite() > 0 ||
- channel->state == QHttpNetworkConnectionChannel::WritingState) {
- remainingTimeBytesWritten = timeout - timeoutTimer.elapsed();
- channel->sendRequest(); // triggers channel->socket->write()
- if (!channel->socket->waitForBytesWritten(remainingTimeBytesWritten)) {
- error(QNetworkReply::TimeoutError,
- QLatin1String("could not write bytes to socket or timeout while writing"));
- return false;
- }
- }
-
- qint64 remainingTimeBytesRead = timeout - timeoutTimer.elapsed();
- // Loop for at most remainingTime until either the socket disconnects
- // or the reply is finished
- do {
- waitResult = channel->socket->waitForReadyRead(remainingTimeBytesRead);
- remainingTimeBytesRead = timeout - timeoutTimer.elapsed();
- if (!waitResult || remainingTimeBytesRead <= 0
- || channel->socket->state() != QAbstractSocket::ConnectedState) {
- error(QNetworkReply::TimeoutError,
- QLatin1String("could not read from socket or timeout while reading"));
- return false;
- }
-
- if (channel->socket->bytesAvailable())
- channel->_q_readyRead();
-
- if (!httpReply)
- return false; // we got a 401 or 407 and cannot handle it (it might happen that
- // disconnectFromHttp() was called, in that case the reply is zero)
- // ### I am quite sure this does not work for NTLM
- // ### how about uploading to an auth / proxyAuth site?
-
- authenticating = (httpReply->statusCode() == 401 || httpReply->statusCode() == 407);
-
- if (httpReply->isFinished())
- break;
- } while (remainingTimeBytesRead > 0);
- } while (authenticating);
-
- return true;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_HTTP
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
index affdd9c..7d4ea56 100644
--- a/src/network/access/qnetworkaccesshttpbackend_p.h
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -61,6 +61,8 @@
#include "QtCore/qpointer.h"
#include "QtCore/qdatetime.h"
+#include "QtCore/qsharedpointer.h"
+#include "qatomic.h"
#ifndef QT_NO_HTTP
@@ -99,23 +101,44 @@ public:
bool canResume() const;
void setResumeOffset(quint64 offset);
- virtual bool processRequestSynchronously();
+signals:
+ // To HTTP thread:
+ void startHttpRequest();
+ void abortHttpRequest();
+ void startHttpRequestSynchronously();
+
+ void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
private slots:
- void replyReadyRead();
+ // From HTTP thread:
+ void replyDownloadData(QByteArray);
void replyFinished();
- void replyHeaderChanged();
+ void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
- void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
+#ifndef QT_NO_OPENSSL
+ void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
+ void replySslConfigurationChanged(const QSslConfiguration&);
+#endif
+
+ // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
+ void resetUploadDataSlot(bool *r);
+ void wantUploadDataSlot(qint64);
+ void sentUploadDataSlot(qint64);
+
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
- void finished(); // override
private:
- QHttpNetworkReply *httpReply;
- QPointer<QHttpNetworkConnection> http;
- QByteArray cacheKey;
- QNetworkAccessBackendUploadIODevice *uploadDevice;
+ QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
+ int statusCode;
+ QString reasonPhrase;
+ // Will be increased by HTTP thread:
+ QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
+ QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
+ bool loadingFromCache;
+ QByteDataBuffer pendingDownloadData;
+ bool usingZerocopyDownloadBuffer;
#ifndef QT_NO_OPENSSL
QSslConfiguration *pendingSslConfiguration;
@@ -125,8 +148,7 @@ private:
quint64 resumeOffset;
- void disconnectFromHttp();
- void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache);
+ bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
void invalidateCache();
void postRequest();
void readFromHttp();
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 0433c34..8fc8eb7 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -53,10 +53,10 @@
#include "qnetworkaccesshttpbackend_p.h"
#include "qnetworkaccessftpbackend_p.h"
#include "qnetworkaccessfilebackend_p.h"
-#include "qnetworkaccessdatabackend_p.h"
#include "qnetworkaccessdebugpipebackend_p.h"
#include "qnetworkaccesscachebackend_p.h"
-#include "qfilenetworkreply_p.h"
+#include "qnetworkreplydataimpl_p.h"
+#include "qnetworkreplyfileimpl_p.h"
#include "QtCore/qbuffer.h"
#include "QtCore/qurl.h"
@@ -64,6 +64,10 @@
#include "QtNetwork/qauthenticator.h"
#include "QtNetwork/qsslconfiguration.h"
#include "QtNetwork/qnetworkconfigmanager.h"
+#include "QtNetwork/qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
+
+#include "qthread.h"
QT_BEGIN_NAMESPACE
@@ -71,7 +75,6 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
#endif // QT_NO_HTTP
Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
-Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend)
#ifndef QT_NO_FTP
Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
#endif // QT_NO_FTP
@@ -85,7 +88,7 @@ static void ensureInitialized()
#ifndef QT_NO_HTTP
(void) httpBackend();
#endif // QT_NO_HTTP
- (void) dataBackend();
+
#ifndef QT_NO_FTP
(void) ftpBackend();
#endif
@@ -298,6 +301,10 @@ static void ensureInitialized()
again, without emitting the authenticationRequired() signal. If it
rejects the credentials, this signal will be emitted again.
+ \note It is not possible to use a QueuedConnection to connect to
+ this signal, as the connection will fail if the authenticator has
+ not been filled in with new information when the signal returns.
+
\sa proxyAuthenticationRequired()
*/
@@ -342,107 +349,6 @@ static void ensureInitialized()
QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
*/
-class QNetworkAuthenticationCredential
-{
-public:
- QString domain;
- QString user;
- QString password;
-};
-Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
-inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
-{ return t1.domain < t2; }
-
-class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
- public QNetworkAccessCache::CacheableObject
-{
-public:
- QNetworkAuthenticationCache()
- {
- setExpires(false);
- setShareable(true);
- reserve(1);
- }
-
- QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
- {
- iterator it = qLowerBound(begin(), end(), domain);
- if (it == end() && !isEmpty())
- --it;
- if (it == end() || !domain.startsWith(it->domain))
- return 0;
- return &*it;
- }
-
- void insert(const QString &domain, const QString &user, const QString &password)
- {
- QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
- if (closestMatch && closestMatch->domain == domain) {
- // we're overriding the current credentials
- closestMatch->user = user;
- closestMatch->password = password;
- } else {
- QNetworkAuthenticationCredential newCredential;
- newCredential.domain = domain;
- newCredential.user = user;
- newCredential.password = password;
-
- if (closestMatch)
- QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
- else
- QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
- }
- }
-
- virtual void dispose() { delete this; }
-};
-
-#ifndef QT_NO_NETWORKPROXY
-static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
-{
- QUrl key;
-
- switch (proxy.type()) {
- case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
- break;
-
- case QNetworkProxy::HttpProxy:
- case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
- break;
-
- case QNetworkProxy::FtpCachingProxy:
- key.setScheme(QLatin1String("proxy-ftp"));
- break;
-
- case QNetworkProxy::DefaultProxy:
- case QNetworkProxy::NoProxy:
- // shouldn't happen
- return QByteArray();
-
- // no default:
- // let there be errors if a new proxy type is added in the future
- }
-
- if (key.scheme().isEmpty())
- // proxy type not handled
- return QByteArray();
-
- key.setUserName(proxy.user());
- key.setHost(proxy.hostName());
- key.setPort(proxy.port());
- key.setFragment(realm);
- return "auth:" + key.toEncoded();
-}
-#endif
-
-static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
-{
- QUrl copy = url;
- copy.setFragment(realm);
- return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
-}
/*!
Constructs a QNetworkAccessManager object that is the center of
@@ -452,6 +358,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
: QObject(*new QNetworkAccessManagerPrivate, parent)
{
ensureInitialized();
+
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
}
/*!
@@ -727,6 +635,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.
@@ -752,7 +700,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)
@@ -946,30 +895,19 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
{
Q_D(QNetworkAccessManager);
- // 4.7 only hotfix fast path for data:// URLs
- // In 4.8 this is solved with QNetworkReplyDataImpl and will work there
- // This hotfix is done for not needing a QNetworkSession for data://
- if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
- && (req.url().scheme() == QLatin1String("data"))) {
- QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
- QNetworkReplyImplPrivate *priv = reply->d_func();
- priv->manager = this;
- priv->backend = new QNetworkAccessDataBackend();
- priv->backend->manager = this->d_func();
- priv->backend->setParent(reply);
- priv->backend->reply = priv;
- priv->setup(op, req, outgoingData);
- return reply;
- }
+ bool isLocalFile = req.url().isLocalFile();
+ QString scheme = req.url().scheme().toLower();
// fast path for GET on file:// URLs
- // Also if the scheme is empty we consider it a file.
// The QNetworkAccessFileBackend will right now only be used for PUT
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
- && (req.url().scheme() == QLatin1String("file")
- || req.url().scheme() == QLatin1String("qrc")
- || req.url().scheme().isEmpty())) {
- return new QFileNetworkReply(this, req, op);
+ && (isLocalFile || scheme == QLatin1String("qrc"))) {
+ return new QNetworkReplyFileImpl(this, req, op);
+ }
+
+ if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
+ && scheme == QLatin1String("data")) {
+ return new QNetworkReplyDataImpl(this, req, op);
}
// A request with QNetworkRequest::AlwaysCache does not need any bearer management
@@ -995,8 +933,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
#ifndef QT_NO_BEARERMANAGEMENT
// Return a disabled network reply if network access is disabled.
// Except if the scheme is empty or file://.
- if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") ||
- req.url().scheme().isEmpty())) {
+ if (!d->networkAccessible && !isLocalFile) {
return new QDisabledNetworkReply(this, req, op);
}
@@ -1030,7 +967,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (d->cookieJar) {
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
+ request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
}
}
@@ -1038,7 +975,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QUrl url = request.url();
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
#ifndef QT_NO_BEARERMANAGEMENT
- if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) {
+ if (!isLocalFile) {
connect(this, SIGNAL(networkSessionConnected()),
reply, SLOT(_q_networkSessionConnected()));
}
@@ -1053,10 +990,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
// third step: find a backend
priv->backend = d->findBackend(op, request);
-#ifndef QT_NO_NETWORKPROXY
- QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));
- priv->proxyList = proxyList;
-#endif
if (priv->backend) {
priv->backend->setParent(reply);
priv->backend->reply = priv;
@@ -1135,10 +1068,10 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend
// also called when last URL is empty, e.g. on first call
if (backend->reply->urlForLastAuthentication.isEmpty()
|| url != backend->reply->urlForLastAuthentication) {
- QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator);
- if (cred) {
- authenticator->setUser(cred->user);
- authenticator->setPassword(cred->password);
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
backend->reply->urlForLastAuthentication = url;
return;
}
@@ -1151,7 +1084,7 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend
backend->reply->urlForLastAuthentication = url;
emit q->authenticationRequired(backend->reply->q_func(), authenticator);
- cacheCredentials(url, authenticator);
+ authenticationManager->cacheCredentials(url, authenticator);
}
#ifndef QT_NO_NETWORKPROXY
@@ -1168,10 +1101,10 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
// possible solution: some tracking inside the authenticator
// or a new function proxyAuthenticationSucceeded(true|false)
if (proxy != backend->reply->lastProxyAuthentication) {
- QNetworkAuthenticationCredential *cred = fetchCachedProxyCredentials(proxy);
- if (cred) {
- authenticator->setUser(cred->user);
- authenticator->setPassword(cred->password);
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
return;
}
}
@@ -1183,75 +1116,7 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
backend->reply->lastProxyAuthentication = proxy;
emit q->proxyAuthenticationRequired(proxy, authenticator);
- cacheProxyCredentials(proxy, authenticator);
-}
-
-void QNetworkAccessManagerPrivate::cacheProxyCredentials(const QNetworkProxy &p,
- const QAuthenticator *authenticator)
-{
- Q_ASSERT(authenticator);
- Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
- Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
-
- QString realm = authenticator->realm();
- QNetworkProxy proxy = p;
- proxy.setUser(authenticator->user());
- // Set two credentials: one with the username and one without
- do {
- // Set two credentials actually: one with and one without the realm
- do {
- QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
- if (cacheKey.isEmpty())
- return; // should not happen
-
- QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
- auth->insert(QString(), authenticator->user(), authenticator->password());
- objectCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
-
- if (realm.isEmpty()) {
- break;
- } else {
- realm.clear();
- }
- } while (true);
-
- if (proxy.user().isEmpty())
- break;
- else
- proxy.setUser(QString());
- } while (true);
-}
-
-QNetworkAuthenticationCredential *
-QNetworkAccessManagerPrivate::fetchCachedProxyCredentials(const QNetworkProxy &p,
- const QAuthenticator *authenticator)
-{
- QNetworkProxy proxy = p;
- if (proxy.type() == QNetworkProxy::DefaultProxy) {
- proxy = QNetworkProxy::applicationProxy();
- }
- if (!proxy.password().isEmpty())
- return 0; // no need to set credentials if it already has them
-
- QString realm;
- if (authenticator)
- realm = authenticator->realm();
-
- QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
- if (cacheKey.isEmpty())
- return 0;
- if (!objectCache.hasEntry(cacheKey))
- return 0;
-
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString());
- objectCache.releaseEntry(cacheKey);
-
- // proxy cache credentials always have exactly one item
- Q_ASSERT_X(cred, "QNetworkAccessManager",
- "Internal inconsistency: found a cache key for a proxy, but it's empty");
- return cred;
+ authenticationManager->cacheProxyCredentials(proxy, authenticator);
}
QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
@@ -1275,77 +1140,25 @@ QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProx
}
#endif
-void QNetworkAccessManagerPrivate::cacheCredentials(const QUrl &url,
- const QAuthenticator *authenticator)
-{
- Q_ASSERT(authenticator);
- QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
- QString realm = authenticator->realm();
-
- // Set two credentials actually: one with and one without the username in the URL
- QUrl copy = url;
- copy.setUserName(authenticator->user());
- do {
- QByteArray cacheKey = authenticationKey(copy, realm);
- if (objectCache.hasEntry(cacheKey)) {
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- auth->insert(domain, authenticator->user(), authenticator->password());
- objectCache.releaseEntry(cacheKey);
- } else {
- QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
- auth->insert(domain, authenticator->user(), authenticator->password());
- objectCache.addEntry(cacheKey, auth);
- }
-
- if (copy.userName().isEmpty()) {
- break;
- } else {
- copy.setUserName(QString());
- }
- } while (true);
-}
-
-/*!
- Fetch the credential data from the credential cache.
-
- If auth is 0 (as it is when called from createRequest()), this will try to
- look up with an empty realm. That fails in most cases for HTTP (because the
- realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
- never sends the credentials on the first attempt: it needs to find out what
- authentication methods the server supports.
-
- For FTP, realm is always empty.
-*/
-QNetworkAuthenticationCredential *
-QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url,
- const QAuthenticator *authentication)
-{
- if (!url.password().isEmpty())
- return 0; // no need to set credentials if it already has them
-
- QString realm;
- if (authentication)
- realm = authentication->realm();
-
- QByteArray cacheKey = authenticationKey(url, realm);
- if (!objectCache.hasEntry(cacheKey))
- return 0;
-
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
- objectCache.releaseEntry(cacheKey);
- return cred;
-}
-
void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
{
manager->d_func()->objectCache.clear();
+ manager->d_func()->authenticationManager->clearCache();
+
+ if (manager->d_func()->httpThread) {
+ // The thread will deleteLater() itself from its finished() signal
+ manager->d_func()->httpThread->quit();
+ manager->d_func()->httpThread = 0;
+ }
}
QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
{
+ if (httpThread) {
+ // The thread will deleteLater() itself from its finished() signal
+ httpThread->quit();
+ httpThread = 0;
+ }
}
#ifndef QT_NO_BEARERMANAGEMENT
@@ -1355,8 +1168,25 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
initializeSession = false;
- if (!config.isValid()) {
- networkSession.clear();
+ QSharedPointer<QNetworkSession> newSession;
+ if (config.isValid())
+ newSession = QSharedNetworkSessionManager::getSession(config);
+
+ if (networkSession) {
+ //do nothing if new and old session are the same
+ if (networkSession == newSession)
+ return;
+ //disconnect from old session
+ QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
+ QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
+ QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
+ q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
+ }
+
+ //switch to new session (null if config was invalid)
+ networkSession = newSession;
+
+ if (!networkSession) {
online = false;
if (networkAccessible == QNetworkAccessManager::NotAccessible)
@@ -1367,8 +1197,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
return;
}
- networkSession = QSharedNetworkSessionManager::getSession(config);
-
+ //connect to new session
QObject::connect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection);
//QueuedConnection is used to avoid deleting the networkSession inside its closed signal
QObject::connect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection);
@@ -1380,9 +1209,15 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
{
+ Q_Q(QNetworkAccessManager);
if (networkSession) {
networkConfiguration = networkSession->configuration().identifier();
+ //disconnect from old session
+ QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
+ QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
+ QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
+ q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
networkSession.clear();
}
}
@@ -1391,8 +1226,12 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
{
Q_Q(QNetworkAccessManager);
- if (state == QNetworkSession::Connected)
+ //Do not emit the networkSessionConnected signal here, except for roaming -> connected
+ //transition, otherwise it is emitted twice in a row when opening a connection.
+ if (state == QNetworkSession::Connected && lastSessionState == QNetworkSession::Roaming)
emit q->networkSessionConnected();
+ lastSessionState = state;
+
if (online) {
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
online = false;
@@ -1407,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 c065791..91b313f 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);
@@ -156,6 +159,8 @@ protected:
private:
friend class QNetworkReplyImplPrivate;
+ friend class QNetworkAccessHttpBackend;
+
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/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index ce9e0f1..a76eafd 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -59,6 +59,7 @@
#include "private/qobject_p.h"
#include "QtNetwork/qnetworkproxy.h"
#include "QtNetwork/qnetworksession.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
QT_BEGIN_NAMESPACE
@@ -72,16 +73,19 @@ class QNetworkAccessManagerPrivate: public QObjectPrivate
public:
QNetworkAccessManagerPrivate()
: networkCache(0), cookieJar(0),
+ httpThread(0),
#ifndef QT_NO_NETWORKPROXY
proxyFactory(0),
#endif
#ifndef QT_NO_BEARERMANAGEMENT
networkSession(0),
+ lastSessionState(QNetworkSession::Invalid),
networkAccessible(QNetworkAccessManager::Accessible),
online(false),
initializeSession(true),
#endif
- cookieJarCreated(false)
+ cookieJarCreated(false),
+ authenticationManager(new QNetworkAccessAuthenticationManager)
{ }
~QNetworkAccessManagerPrivate();
@@ -116,11 +120,15 @@ 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;
QNetworkCookieJar *cookieJar;
+ QThread *httpThread;
+
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy proxy;
@@ -129,6 +137,7 @@ public:
#ifndef QT_NO_BEARERMANAGEMENT
QSharedPointer<QNetworkSession> networkSession;
+ QNetworkSession::State lastSessionState;
QString networkConfiguration;
QNetworkAccessManager::NetworkAccessibility networkAccessible;
bool online;
@@ -137,6 +146,9 @@ public:
bool cookieJarCreated;
+ // The cache with authorization data:
+ QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+
// this cache can be used by individual backends to cache e.g. their TCP connections to a server
// and use the connections for multiple requests.
QNetworkAccessCache objectCache;
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 983c6ee..0670738 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -395,8 +395,8 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
// 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 it is NAME=VALUE, retain the value as is
+ // refer to http://bugreports.qt.nokia.com/browse/QTBUG-17746
if (isNameValue)
second += '"';
++i;
@@ -432,7 +432,9 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
position = i;
for ( ; i < length; ++i) {
register char c = text.at(i);
- if (c == ',' || c == ';' || isLWS(c))
+ // for name value pairs, we want to parse until reaching the next ';'
+ // and not break when reaching a space char
+ if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
break;
}
@@ -487,7 +489,6 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
result += '=';
if ((d->value.contains(';') ||
d->value.contains(',') ||
- d->value.contains(' ') ||
d->value.contains('"')) &&
(!d->value.startsWith('"') &&
!d->value.endsWith('"'))) {
@@ -737,7 +738,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
// 4 digit Year
if (isNum
&& year == -1
- && dateString.length() >= at + 3) {
+ && dateString.length() > at + 3) {
if (isNumber(dateString[at + 1])
&& isNumber(dateString[at + 2])
&& isNumber(dateString[at + 3])) {
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
index e49a8e1..1a5f73b 100644
--- a/src/network/access/qnetworkcookiejar.cpp
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -40,12 +40,12 @@
****************************************************************************/
#include "qnetworkcookiejar.h"
-#include "qnetworkcookiejartlds_p.h"
#include "qnetworkcookiejar_p.h"
#include "QtNetwork/qnetworkcookie.h"
#include "QtCore/qurl.h"
#include "QtCore/qdatetime.h"
+#include "private/qtldurl_p.h"
QT_BEGIN_NAMESPACE
@@ -216,7 +216,7 @@ bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieLis
// the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
// redundant; the "leading dot" rule has been relaxed anyway, see above
// we remove the leading dot for this check
- if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
+ if (qIsEffectiveTLD(domain.remove(0, 1)))
continue; // not accepted
}
@@ -317,43 +317,4 @@ QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
return result;
}
-bool QNetworkCookieJarPrivate::isEffectiveTLD(const QString &domain)
-{
- // for domain 'foo.bar.com':
- // 1. return if TLD table contains 'foo.bar.com'
- if (containsTLDEntry(domain))
- return true;
-
- if (domain.contains(QLatin1Char('.'))) {
- int count = domain.size() - domain.indexOf(QLatin1Char('.'));
- QString wildCardDomain;
- wildCardDomain.reserve(count + 1);
- wildCardDomain.append(QLatin1Char('*'));
- wildCardDomain.append(domain.right(count));
- // 2. if table contains '*.bar.com',
- // test if table contains '!foo.bar.com'
- if (containsTLDEntry(wildCardDomain)) {
- QString exceptionDomain;
- exceptionDomain.reserve(domain.size() + 1);
- exceptionDomain.append(QLatin1Char('!'));
- exceptionDomain.append(domain);
- return (! containsTLDEntry(exceptionDomain));
- }
- }
- return false;
-}
-
-bool QNetworkCookieJarPrivate::containsTLDEntry(const QString &entry)
-{
- int index = qHash(entry) % tldCount;
- int currentDomainIndex = tldIndices[index];
- while (currentDomainIndex < tldIndices[index+1]) {
- QString currentEntry = QString::fromUtf8(tldData + currentDomainIndex);
- if (currentEntry == entry)
- return true;
- currentDomainIndex += qstrlen(tldData + currentDomainIndex) + 1; // +1 for the ending \0
- }
- return false;
-}
-
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
index 912847b..34858d9 100644
--- a/src/network/access/qnetworkcookiejar_p.h
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -63,9 +63,6 @@ class QNetworkCookieJarPrivate: public QObjectPrivate
public:
QList<QNetworkCookie> allCookies;
- static bool Q_AUTOTEST_EXPORT isEffectiveTLD(const QString &domain);
- static bool containsTLDEntry(const QString &entry);
-
Q_DECLARE_PUBLIC(QNetworkCookieJar)
};
diff --git a/src/network/access/qnetworkcookiejartlds_p.h b/src/network/access/qnetworkcookiejartlds_p.h
deleted file mode 100644
index b06d881..0000000
--- a/src/network/access/qnetworkcookiejartlds_p.h
+++ /dev/null
@@ -1,6481 +0,0 @@
-// Version: MPL 1.1/GPL 2.0/LGPL 2.1
-//
-// The contents of this file are subject to the Mozilla Public License Version
-// 1.1 (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-// http://www.mozilla.org/MPL/
-//
-// Software distributed under the License is distributed on an "AS IS" basis,
-// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-// for the specific language governing rights and limitations under the
-// License.
-//
-// The Original Code is the Public Suffix List.
-//
-// The Initial Developer of the Original Code is
-// Jo Hermans <jo.hermans@gmail.com>.
-// Portions created by the Initial Developer are Copyright (C) 2007
-// the Initial Developer. All Rights Reserved.
-//
-// Contributor(s):
-// Ruben Arakelyan <ruben@wackomenace.co.uk>
-// Gervase Markham <gerv@gerv.net>
-// Pamela Greene <pamg.bugs@gmail.com>
-// David Triendl <david@triendl.name>
-// Jothan Frakes <jothan@gmail.com>
-// The kind representatives of many TLD registries
-//
-// Alternatively, the contents of this file may be used under the terms of
-// either the GNU General Public License Version 2 or later (the "GPL"), or
-// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-// in which case the provisions of the GPL or the LGPL are applicable instead
-// of those above. If you wish to allow use of your version of this file only
-// under the terms of either the GPL or the LGPL, and not to allow others to
-// use your version of this file under the terms of the MPL, indicate your
-// decision by deleting the provisions above and replace them with the notice
-// and other provisions required by the GPL or the LGPL. If you do not delete
-// the provisions above, a recipient may use your version of this file under
-// the terms of any one of the MPL, the GPL or the LGPL.
-//
-
-#ifndef QNETWORKCOOKIEJARTLD_P_H
-#define QNETWORKCOOKIEJARTLD_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.
-//
-
-QT_BEGIN_NAMESPACE
-
-// note to maintainer:
-// this file should be updated before each release ->
-// for instructions see the program at
-// util/network/cookiejar-generateTLDs
-
-static const quint16 tldCount = 3949;
-static const quint16 tldIndices[] = {
-0,
-7,
-14,
-14,
-20,
-51,
-61,
-93,
-100,
-100,
-116,
-159,
-167,
-180,
-180,
-193,
-234,
-234,
-234,
-255,
-255,
-255,
-280,
-280,
-287,
-287,
-295,
-303,
-313,
-326,
-326,
-380,
-393,
-413,
-419,
-419,
-419,
-424,
-438,
-438,
-469,
-515,
-515,
-515,
-534,
-534,
-557,
-557,
-557,
-557,
-572,
-572,
-572,
-579,
-587,
-597,
-612,
-612,
-624,
-636,
-648,
-662,
-687,
-709,
-714,
-740,
-766,
-789,
-789,
-805,
-805,
-810,
-815,
-815,
-824,
-824,
-831,
-857,
-869,
-891,
-891,
-916,
-916,
-916,
-927,
-934,
-964,
-971,
-987,
-987,
-987,
-1008,
-1008,
-1016,
-1016,
-1030,
-1030,
-1052,
-1075,
-1075,
-1082,
-1087,
-1115,
-1135,
-1135,
-1135,
-1172,
-1178,
-1178,
-1178,
-1202,
-1207,
-1220,
-1220,
-1266,
-1266,
-1266,
-1266,
-1272,
-1290,
-1316,
-1316,
-1332,
-1332,
-1339,
-1339,
-1352,
-1352,
-1389,
-1389,
-1408,
-1415,
-1437,
-1444,
-1489,
-1489,
-1502,
-1502,
-1512,
-1518,
-1539,
-1555,
-1562,
-1584,
-1598,
-1607,
-1607,
-1607,
-1632,
-1652,
-1652,
-1658,
-1658,
-1675,
-1682,
-1709,
-1733,
-1748,
-1776,
-1783,
-1783,
-1790,
-1797,
-1826,
-1850,
-1850,
-1856,
-1880,
-1887,
-1901,
-1921,
-1947,
-1961,
-1967,
-1967,
-1967,
-1972,
-1986,
-1986,
-1986,
-2009,
-2029,
-2029,
-2047,
-2061,
-2075,
-2075,
-2075,
-2075,
-2075,
-2075,
-2082,
-2082,
-2124,
-2124,
-2129,
-2162,
-2162,
-2162,
-2236,
-2256,
-2263,
-2276,
-2283,
-2313,
-2313,
-2347,
-2380,
-2387,
-2387,
-2387,
-2431,
-2438,
-2445,
-2452,
-2459,
-2459,
-2469,
-2490,
-2516,
-2527,
-2540,
-2540,
-2586,
-2610,
-2630,
-2630,
-2653,
-2660,
-2669,
-2693,
-2693,
-2710,
-2710,
-2719,
-2719,
-2734,
-2740,
-2740,
-2753,
-2753,
-2763,
-2770,
-2775,
-2782,
-2789,
-2802,
-2820,
-2827,
-2827,
-2841,
-2855,
-2855,
-2865,
-2872,
-2884,
-2884,
-2919,
-2937,
-2955,
-2962,
-3012,
-3042,
-3073,
-3083,
-3083,
-3100,
-3105,
-3112,
-3131,
-3131,
-3166,
-3180,
-3187,
-3194,
-3211,
-3218,
-3223,
-3233,
-3249,
-3259,
-3268,
-3314,
-3314,
-3324,
-3324,
-3336,
-3336,
-3336,
-3336,
-3350,
-3363,
-3376,
-3398,
-3416,
-3445,
-3464,
-3488,
-3488,
-3497,
-3545,
-3552,
-3552,
-3552,
-3566,
-3573,
-3573,
-3573,
-3581,
-3581,
-3603,
-3603,
-3615,
-3621,
-3621,
-3683,
-3683,
-3710,
-3710,
-3716,
-3716,
-3748,
-3770,
-3791,
-3803,
-3810,
-3817,
-3833,
-3846,
-3846,
-3852,
-3876,
-3876,
-3882,
-3903,
-3910,
-3939,
-3939,
-3939,
-3947,
-3962,
-3962,
-3981,
-3994,
-4021,
-4030,
-4042,
-4085,
-4085,
-4096,
-4096,
-4104,
-4123,
-4123,
-4123,
-4143,
-4154,
-4164,
-4194,
-4194,
-4194,
-4205,
-4205,
-4222,
-4238,
-4301,
-4309,
-4322,
-4331,
-4331,
-4331,
-4331,
-4331,
-4347,
-4365,
-4375,
-4375,
-4385,
-4398,
-4412,
-4430,
-4430,
-4437,
-4447,
-4463,
-4472,
-4472,
-4472,
-4484,
-4484,
-4484,
-4484,
-4484,
-4490,
-4490,
-4511,
-4511,
-4522,
-4522,
-4522,
-4522,
-4528,
-4528,
-4534,
-4551,
-4551,
-4551,
-4564,
-4583,
-4583,
-4611,
-4611,
-4611,
-4622,
-4622,
-4649,
-4668,
-4677,
-4692,
-4692,
-4692,
-4705,
-4723,
-4723,
-4723,
-4729,
-4729,
-4743,
-4743,
-4750,
-4750,
-4763,
-4770,
-4776,
-4776,
-4776,
-4793,
-4811,
-4811,
-4811,
-4821,
-4821,
-4841,
-4857,
-4891,
-4897,
-4903,
-4903,
-4903,
-4919,
-4935,
-4942,
-4958,
-4958,
-4975,
-4975,
-4975,
-4985,
-4985,
-5020,
-5032,
-5032,
-5040,
-5053,
-5068,
-5079,
-5079,
-5101,
-5115,
-5115,
-5135,
-5154,
-5161,
-5161,
-5168,
-5168,
-5184,
-5210,
-5210,
-5238,
-5255,
-5278,
-5285,
-5308,
-5318,
-5327,
-5327,
-5333,
-5333,
-5340,
-5348,
-5355,
-5366,
-5377,
-5384,
-5408,
-5415,
-5422,
-5435,
-5442,
-5482,
-5482,
-5482,
-5482,
-5498,
-5527,
-5534,
-5541,
-5572,
-5572,
-5572,
-5579,
-5591,
-5602,
-5602,
-5621,
-5621,
-5646,
-5664,
-5671,
-5671,
-5681,
-5696,
-5707,
-5716,
-5716,
-5723,
-5723,
-5732,
-5742,
-5742,
-5763,
-5770,
-5776,
-5790,
-5790,
-5797,
-5806,
-5816,
-5832,
-5882,
-5882,
-5882,
-5882,
-5893,
-5934,
-5934,
-5965,
-5965,
-5980,
-5980,
-5980,
-5980,
-6007,
-6017,
-6034,
-6051,
-6065,
-6075,
-6075,
-6091,
-6097,
-6097,
-6109,
-6109,
-6109,
-6122,
-6147,
-6168,
-6168,
-6191,
-6191,
-6191,
-6191,
-6191,
-6198,
-6217,
-6224,
-6224,
-6231,
-6245,
-6252,
-6252,
-6270,
-6270,
-6284,
-6305,
-6315,
-6322,
-6322,
-6322,
-6329,
-6329,
-6329,
-6353,
-6361,
-6361,
-6375,
-6391,
-6405,
-6405,
-6415,
-6431,
-6431,
-6431,
-6431,
-6458,
-6475,
-6475,
-6475,
-6482,
-6489,
-6496,
-6496,
-6503,
-6520,
-6520,
-6520,
-6527,
-6527,
-6544,
-6561,
-6573,
-6573,
-6580,
-6580,
-6587,
-6587,
-6592,
-6592,
-6592,
-6592,
-6599,
-6599,
-6612,
-6633,
-6649,
-6649,
-6669,
-6676,
-6683,
-6683,
-6713,
-6720,
-6727,
-6736,
-6736,
-6746,
-6770,
-6807,
-6814,
-6827,
-6846,
-6846,
-6864,
-6864,
-6864,
-6864,
-6881,
-6888,
-6888,
-6888,
-6914,
-6935,
-6935,
-6935,
-6953,
-6959,
-6966,
-6983,
-6983,
-6983,
-7013,
-7023,
-7023,
-7023,
-7023,
-7037,
-7044,
-7058,
-7079,
-7086,
-7123,
-7134,
-7155,
-7168,
-7178,
-7203,
-7227,
-7236,
-7258,
-7265,
-7274,
-7274,
-7303,
-7303,
-7314,
-7314,
-7345,
-7352,
-7367,
-7377,
-7388,
-7388,
-7402,
-7402,
-7409,
-7421,
-7421,
-7467,
-7484,
-7484,
-7484,
-7491,
-7532,
-7532,
-7539,
-7546,
-7546,
-7553,
-7573,
-7573,
-7573,
-7580,
-7587,
-7594,
-7594,
-7594,
-7606,
-7606,
-7637,
-7637,
-7637,
-7664,
-7664,
-7664,
-7677,
-7684,
-7701,
-7723,
-7723,
-7723,
-7723,
-7734,
-7734,
-7734,
-7748,
-7748,
-7748,
-7748,
-7759,
-7759,
-7775,
-7775,
-7782,
-7789,
-7817,
-7824,
-7831,
-7836,
-7865,
-7865,
-7876,
-7901,
-7901,
-7908,
-7918,
-7938,
-7945,
-7945,
-7957,
-7964,
-7979,
-7986,
-7994,
-8007,
-8007,
-8014,
-8021,
-8061,
-8061,
-8071,
-8088,
-8131,
-8138,
-8153,
-8160,
-8160,
-8160,
-8175,
-8183,
-8197,
-8211,
-8211,
-8211,
-8243,
-8243,
-8243,
-8243,
-8259,
-8259,
-8259,
-8259,
-8259,
-8266,
-8275,
-8281,
-8281,
-8281,
-8281,
-8288,
-8288,
-8309,
-8309,
-8309,
-8330,
-8330,
-8330,
-8330,
-8337,
-8343,
-8343,
-8360,
-8370,
-8370,
-8380,
-8380,
-8386,
-8386,
-8397,
-8415,
-8415,
-8428,
-8454,
-8460,
-8475,
-8492,
-8526,
-8554,
-8554,
-8583,
-8583,
-8583,
-8598,
-8607,
-8617,
-8617,
-8642,
-8652,
-8652,
-8652,
-8662,
-8688,
-8688,
-8704,
-8704,
-8747,
-8765,
-8775,
-8783,
-8811,
-8835,
-8835,
-8835,
-8850,
-8859,
-8884,
-8910,
-8919,
-8952,
-8978,
-8978,
-8991,
-8991,
-8991,
-8999,
-8999,
-8999,
-9030,
-9030,
-9030,
-9030,
-9030,
-9041,
-9048,
-9048,
-9054,
-9054,
-9054,
-9086,
-9108,
-9108,
-9119,
-9119,
-9130,
-9144,
-9152,
-9161,
-9174,
-9194,
-9207,
-9207,
-9207,
-9232,
-9242,
-9242,
-9271,
-9290,
-9308,
-9308,
-9308,
-9308,
-9320,
-9333,
-9343,
-9356,
-9379,
-9379,
-9379,
-9395,
-9395,
-9406,
-9419,
-9419,
-9419,
-9419,
-9450,
-9485,
-9485,
-9497,
-9497,
-9505,
-9517,
-9528,
-9528,
-9551,
-9564,
-9586,
-9586,
-9608,
-9608,
-9626,
-9626,
-9626,
-9653,
-9653,
-9681,
-9681,
-9681,
-9698,
-9698,
-9698,
-9714,
-9729,
-9737,
-9737,
-9765,
-9765,
-9765,
-9765,
-9765,
-9825,
-9844,
-9866,
-9880,
-9880,
-9880,
-9880,
-9886,
-9895,
-9895,
-9895,
-9895,
-9895,
-9913,
-9913,
-9924,
-9958,
-9958,
-9967,
-9975,
-9975,
-9975,
-9981,
-9998,
-9998,
-9998,
-10012,
-10036,
-10036,
-10066,
-10079,
-10097,
-10121,
-10133,
-10142,
-10142,
-10142,
-10156,
-10173,
-10173,
-10173,
-10196,
-10205,
-10205,
-10205,
-10205,
-10218,
-10234,
-10240,
-10240,
-10240,
-10264,
-10273,
-10286,
-10286,
-10286,
-10286,
-10299,
-10299,
-10309,
-10309,
-10339,
-10358,
-10358,
-10374,
-10374,
-10390,
-10390,
-10413,
-10413,
-10439,
-10461,
-10467,
-10467,
-10467,
-10492,
-10492,
-10501,
-10528,
-10528,
-10528,
-10539,
-10583,
-10583,
-10583,
-10613,
-10613,
-10619,
-10628,
-10645,
-10645,
-10645,
-10650,
-10671,
-10687,
-10709,
-10709,
-10709,
-10709,
-10709,
-10727,
-10727,
-10727,
-10727,
-10733,
-10733,
-10768,
-10768,
-10773,
-10780,
-10788,
-10788,
-10797,
-10797,
-10835,
-10835,
-10845,
-10852,
-10861,
-10861,
-10861,
-10861,
-10861,
-10861,
-10861,
-10875,
-10888,
-10907,
-10907,
-10907,
-10920,
-10920,
-10932,
-10946,
-10977,
-10977,
-10997,
-11008,
-11037,
-11059,
-11059,
-11059,
-11067,
-11067,
-11067,
-11067,
-11067,
-11077,
-11091,
-11102,
-11102,
-11112,
-11125,
-11129,
-11129,
-11154,
-11154,
-11154,
-11164,
-11164,
-11189,
-11189,
-11189,
-11189,
-11189,
-11196,
-11196,
-11227,
-11238,
-11247,
-11256,
-11265,
-11265,
-11286,
-11307,
-11330,
-11337,
-11378,
-11378,
-11389,
-11413,
-11454,
-11469,
-11475,
-11475,
-11475,
-11475,
-11503,
-11503,
-11503,
-11503,
-11534,
-11534,
-11550,
-11550,
-11557,
-11557,
-11557,
-11557,
-11574,
-11585,
-11585,
-11603,
-11626,
-11643,
-11643,
-11643,
-11643,
-11663,
-11663,
-11682,
-11696,
-11696,
-11696,
-11696,
-11706,
-11706,
-11706,
-11706,
-11723,
-11723,
-11757,
-11757,
-11773,
-11795,
-11813,
-11836,
-11836,
-11883,
-11903,
-11952,
-11965,
-11965,
-11984,
-11997,
-12008,
-12008,
-12008,
-12024,
-12043,
-12065,
-12071,
-12099,
-12129,
-12140,
-12140,
-12146,
-12161,
-12161,
-12161,
-12161,
-12167,
-12167,
-12180,
-12186,
-12208,
-12226,
-12243,
-12243,
-12252,
-12252,
-12274,
-12289,
-12302,
-12302,
-12313,
-12313,
-12313,
-12313,
-12358,
-12358,
-12358,
-12369,
-12375,
-12391,
-12391,
-12391,
-12391,
-12405,
-12410,
-12416,
-12416,
-12436,
-12436,
-12436,
-12443,
-12443,
-12462,
-12477,
-12492,
-12492,
-12503,
-12519,
-12525,
-12531,
-12571,
-12571,
-12591,
-12591,
-12601,
-12641,
-12641,
-12641,
-12657,
-12657,
-12657,
-12699,
-12699,
-12699,
-12712,
-12728,
-12744,
-12761,
-12769,
-12782,
-12793,
-12823,
-12836,
-12851,
-12863,
-12890,
-12900,
-12900,
-12900,
-12910,
-12910,
-12924,
-12924,
-12924,
-12924,
-12924,
-12952,
-12984,
-13003,
-13038,
-13038,
-13056,
-13056,
-13056,
-13056,
-13074,
-13081,
-13093,
-13103,
-13103,
-13112,
-13119,
-13132,
-13132,
-13132,
-13141,
-13151,
-13183,
-13193,
-13206,
-13206,
-13206,
-13236,
-13252,
-13267,
-13267,
-13294,
-13307,
-13307,
-13307,
-13343,
-13349,
-13349,
-13349,
-13375,
-13375,
-13375,
-13384,
-13384,
-13384,
-13393,
-13393,
-13393,
-13402,
-13414,
-13425,
-13445,
-13467,
-13485,
-13499,
-13509,
-13528,
-13528,
-13549,
-13549,
-13559,
-13570,
-13570,
-13570,
-13570,
-13598,
-13637,
-13647,
-13647,
-13661,
-13673,
-13682,
-13687,
-13694,
-13694,
-13720,
-13733,
-13742,
-13748,
-13771,
-13795,
-13795,
-13814,
-13821,
-13821,
-13855,
-13862,
-13862,
-13862,
-13874,
-13874,
-13897,
-13909,
-13909,
-13947,
-13947,
-13952,
-13952,
-13970,
-13979,
-13979,
-13979,
-14008,
-14049,
-14049,
-14049,
-14049,
-14049,
-14049,
-14060,
-14083,
-14083,
-14091,
-14101,
-14101,
-14101,
-14101,
-14118,
-14136,
-14195,
-14195,
-14195,
-14213,
-14213,
-14232,
-14232,
-14253,
-14253,
-14258,
-14275,
-14275,
-14304,
-14311,
-14311,
-14318,
-14318,
-14318,
-14318,
-14318,
-14318,
-14325,
-14325,
-14345,
-14345,
-14379,
-14389,
-14422,
-14422,
-14422,
-14422,
-14435,
-14441,
-14441,
-14460,
-14460,
-14471,
-14471,
-14481,
-14495,
-14495,
-14495,
-14502,
-14502,
-14502,
-14502,
-14524,
-14533,
-14541,
-14552,
-14552,
-14552,
-14552,
-14563,
-14563,
-14568,
-14568,
-14585,
-14595,
-14602,
-14628,
-14628,
-14645,
-14672,
-14678,
-14697,
-14697,
-14734,
-14757,
-14764,
-14771,
-14771,
-14771,
-14796,
-14815,
-14822,
-14845,
-14861,
-14861,
-14861,
-14873,
-14873,
-14873,
-14902,
-14920,
-14920,
-14926,
-14926,
-14926,
-14941,
-14949,
-14949,
-14949,
-14949,
-14949,
-14960,
-14960,
-14971,
-14971,
-14998,
-15003,
-15003,
-15003,
-15013,
-15013,
-15027,
-15027,
-15027,
-15043,
-15053,
-15063,
-15074,
-15083,
-15093,
-15093,
-15121,
-15121,
-15128,
-15128,
-15144,
-15144,
-15144,
-15144,
-15165,
-15170,
-15170,
-15170,
-15170,
-15170,
-15170,
-15170,
-15186,
-15206,
-15206,
-15206,
-15224,
-15236,
-15236,
-15252,
-15258,
-15258,
-15258,
-15258,
-15264,
-15277,
-15288,
-15307,
-15307,
-15318,
-15328,
-15328,
-15334,
-15334,
-15363,
-15399,
-15399,
-15422,
-15438,
-15447,
-15447,
-15456,
-15456,
-15456,
-15495,
-15495,
-15495,
-15495,
-15511,
-15511,
-15530,
-15557,
-15566,
-15582,
-15590,
-15590,
-15604,
-15604,
-15625,
-15635,
-15655,
-15655,
-15655,
-15655,
-15655,
-15665,
-15675,
-15675,
-15675,
-15675,
-15675,
-15682,
-15682,
-15682,
-15694,
-15719,
-15749,
-15749,
-15794,
-15794,
-15837,
-15854,
-15854,
-15861,
-15861,
-15861,
-15861,
-15884,
-15900,
-15911,
-15931,
-15931,
-15931,
-15931,
-15931,
-15963,
-15963,
-15996,
-16006,
-16013,
-16024,
-16034,
-16049,
-16049,
-16049,
-16059,
-16059,
-16059,
-16059,
-16059,
-16059,
-16059,
-16064,
-16075,
-16104,
-16104,
-16117,
-16124,
-16124,
-16124,
-16124,
-16130,
-16145,
-16159,
-16190,
-16193,
-16196,
-16210,
-16224,
-16243,
-16255,
-16261,
-16280,
-16283,
-16292,
-16295,
-16295,
-16301,
-16304,
-16322,
-16325,
-16328,
-16349,
-16355,
-16382,
-16408,
-16414,
-16414,
-16414,
-16458,
-16477,
-16480,
-16485,
-16488,
-16514,
-16520,
-16530,
-16530,
-16546,
-16562,
-16597,
-16603,
-16622,
-16622,
-16646,
-16669,
-16672,
-16715,
-16715,
-16715,
-16718,
-16718,
-16727,
-16733,
-16752,
-16770,
-16787,
-16794,
-16810,
-16827,
-16840,
-16850,
-16876,
-16901,
-16901,
-16901,
-16916,
-16919,
-16926,
-16943,
-16972,
-16978,
-16978,
-16978,
-16981,
-17008,
-17008,
-17016,
-17053,
-17053,
-17053,
-17053,
-17072,
-17086,
-17105,
-17139,
-17153,
-17162,
-17162,
-17177,
-17177,
-17177,
-17177,
-17195,
-17218,
-17221,
-17221,
-17237,
-17276,
-17276,
-17300,
-17319,
-17339,
-17357,
-17370,
-17383,
-17400,
-17407,
-17419,
-17439,
-17439,
-17449,
-17465,
-17475,
-17504,
-17527,
-17527,
-17534,
-17548,
-17564,
-17564,
-17598,
-17598,
-17601,
-17630,
-17649,
-17669,
-17669,
-17679,
-17686,
-17757,
-17776,
-17796,
-17810,
-17840,
-17840,
-17877,
-17884,
-17884,
-17911,
-17936,
-17970,
-17980,
-17995,
-17995,
-18008,
-18011,
-18036,
-18075,
-18097,
-18097,
-18104,
-18115,
-18115,
-18129,
-18134,
-18141,
-18141,
-18157,
-18180,
-18190,
-18217,
-18224,
-18252,
-18284,
-18284,
-18296,
-18299,
-18312,
-18322,
-18338,
-18348,
-18348,
-18363,
-18372,
-18387,
-18387,
-18390,
-18402,
-18445,
-18445,
-18445,
-18466,
-18479,
-18479,
-18498,
-18508,
-18511,
-18511,
-18511,
-18511,
-18511,
-18526,
-18558,
-18586,
-18622,
-18643,
-18670,
-18686,
-18710,
-18720,
-18739,
-18739,
-18742,
-18745,
-18771,
-18774,
-18777,
-18791,
-18813,
-18816,
-18822,
-18839,
-18845,
-18851,
-18854,
-18878,
-18881,
-18896,
-18896,
-18899,
-18926,
-18937,
-18940,
-18953,
-18963,
-19010,
-19010,
-19017,
-19046,
-19060,
-19060,
-19087,
-19095,
-19101,
-19101,
-19128,
-19146,
-19157,
-19157,
-19188,
-19198,
-19205,
-19223,
-19230,
-19255,
-19280,
-19280,
-19283,
-19292,
-19301,
-19320,
-19323,
-19323,
-19341,
-19341,
-19365,
-19378,
-19381,
-19394,
-19423,
-19433,
-19440,
-19466,
-19490,
-19490,
-19490,
-19497,
-19504,
-19511,
-19511,
-19518,
-19545,
-19568,
-19575,
-19575,
-19583,
-19586,
-19592,
-19592,
-19609,
-19622,
-19629,
-19641,
-19641,
-19656,
-19673,
-19700,
-19723,
-19733,
-19746,
-19759,
-19769,
-19782,
-19789,
-19795,
-19831,
-19834,
-19841,
-19851,
-19854,
-19880,
-19895,
-19898,
-19898,
-19911,
-19922,
-19950,
-20020,
-20030,
-20059,
-20062,
-20089,
-20107,
-20151,
-20154,
-20175,
-20205,
-20208,
-20229,
-20229,
-20255,
-20261,
-20261,
-20283,
-20335,
-20362,
-20385,
-20392,
-20392,
-20392,
-20392,
-20418,
-20418,
-20418,
-20418,
-20443,
-20446,
-20446,
-20452,
-20452,
-20467,
-20467,
-20489,
-20489,
-20498,
-20525,
-20554,
-20560,
-20575,
-20589,
-20589,
-20589,
-20614,
-20652,
-20659,
-20659,
-20668,
-20679,
-20679,
-20679,
-20685,
-20685,
-20685,
-20685,
-20685,
-20685,
-20696,
-20733,
-20760,
-20766,
-20769,
-20792,
-20792,
-20792,
-20792,
-20813,
-20813,
-20813,
-20813,
-20826,
-20826,
-20846,
-20862,
-20880,
-20887,
-20901,
-20908,
-20933,
-20938,
-20958,
-20969,
-20978,
-20978,
-20978,
-20985,
-20985,
-20985,
-21000,
-21010,
-21010,
-21014,
-21026,
-21033,
-21041,
-21041,
-21051,
-21051,
-21064,
-21071,
-21113,
-21120,
-21120,
-21127,
-21134,
-21142,
-21164,
-21164,
-21164,
-21188,
-21195,
-21208,
-21215,
-21226,
-21226,
-21226,
-21238,
-21249,
-21255,
-21255,
-21265,
-21279,
-21296,
-21301,
-21315,
-21321,
-21331,
-21331,
-21331,
-21337,
-21343,
-21343,
-21351,
-21351,
-21361,
-21368,
-21383,
-21383,
-21389,
-21413,
-21437,
-21449,
-21462,
-21478,
-21506,
-21534,
-21546,
-21546,
-21557,
-21580,
-21595,
-21595,
-21595,
-21595,
-21626,
-21626,
-21646,
-21670,
-21676,
-21688,
-21688,
-21716,
-21731,
-21762,
-21762,
-21762,
-21762,
-21762,
-21783,
-21789,
-21799,
-21828,
-21828,
-21837,
-21845,
-21868,
-21874,
-21874,
-21880,
-21880,
-21889,
-21901,
-21910,
-21941,
-21941,
-21959,
-21959,
-21959,
-21966,
-21966,
-21972,
-21972,
-21995,
-22011,
-22043,
-22043,
-22051,
-22051,
-22060,
-22060,
-22060,
-22074,
-22086,
-22086,
-22099,
-22099,
-22120,
-22120,
-22134,
-22144,
-22150,
-22158,
-22164,
-22164,
-22171,
-22199,
-22210,
-22210,
-22210,
-22220,
-22228,
-22228,
-22239,
-22261,
-22304,
-22304,
-22312,
-22349,
-22349,
-22349,
-22357,
-22381,
-22381,
-22390,
-22390,
-22390,
-22390,
-22402,
-22413,
-22413,
-22422,
-22422,
-22445,
-22445,
-22445,
-22456,
-22456,
-22469,
-22479,
-22501,
-22512,
-22528,
-22528,
-22528,
-22528,
-22528,
-22528,
-22528,
-22540,
-22540,
-22546,
-22546,
-22546,
-22546,
-22569,
-22591,
-22591,
-22591,
-22591,
-22610,
-22655,
-22655,
-22667,
-22667,
-22677,
-22677,
-22692,
-22692,
-22702,
-22702,
-22702,
-22717,
-22736,
-22750,
-22755,
-22780,
-22785,
-22822,
-22844,
-22859,
-22871,
-22909,
-22949,
-22962,
-22973,
-22979,
-22988,
-23007,
-23027,
-23035,
-23072,
-23082,
-23082,
-23109,
-23116,
-23130,
-23158,
-23166,
-23166,
-23172,
-23177,
-23189,
-23219,
-23219,
-23250,
-23273,
-23281,
-23281,
-23281,
-23281,
-23281,
-23287,
-23287,
-23299,
-23305,
-23315,
-23315,
-23321,
-23328,
-23334,
-23355,
-23355,
-23355,
-23355,
-23355,
-23366,
-23390,
-23396,
-23396,
-23396,
-23396,
-23402,
-23418,
-23424,
-23424,
-23438,
-23446,
-23446,
-23446,
-23500,
-23525,
-23569,
-23592,
-23592,
-23592,
-23605,
-23614,
-23614,
-23614,
-23627,
-23633,
-23657,
-23673,
-23673,
-23673,
-23689,
-23689,
-23701,
-23701,
-23701,
-23713,
-23713,
-23713,
-23738,
-23758,
-23775,
-23775,
-23794,
-23794,
-23803,
-23803,
-23803,
-23803,
-23813,
-23826,
-23845,
-23845,
-23845,
-23845,
-23872,
-23872,
-23872,
-23888,
-23888,
-23900,
-23900,
-23906,
-23906,
-23906,
-23906,
-23906,
-23924,
-23924,
-23924,
-23930,
-23930,
-23939,
-23949,
-23971,
-23971,
-23971,
-24000,
-24012,
-24042,
-24042,
-24042,
-24042,
-24042,
-24070,
-24076,
-24094,
-24094,
-24094,
-24123,
-24123,
-24123,
-24134,
-24150,
-24150,
-24150,
-24150,
-24150,
-24150,
-24155,
-24179,
-24179,
-24189,
-24189,
-24189,
-24198,
-24198,
-24218,
-24218,
-24218,
-24234,
-24251,
-24257,
-24276,
-24305,
-24321,
-24321,
-24321,
-24334,
-24334,
-24334,
-24349,
-24356,
-24361,
-24372,
-24372,
-24372,
-24388,
-24396,
-24396,
-24402,
-24410,
-24410,
-24428,
-24428,
-24450,
-24450,
-24467,
-24485,
-24495,
-24495,
-24495,
-24507,
-24507,
-24514,
-24531,
-24531,
-24531,
-24531,
-24531,
-24537,
-24537,
-24558,
-24572,
-24584,
-24584,
-24601,
-24601,
-24607,
-24615,
-24615,
-24632,
-24632,
-24632,
-24632,
-24661,
-24676,
-24676,
-24724,
-24751,
-24751,
-24774,
-24774,
-24783,
-24793,
-24793,
-24793,
-24813,
-24819,
-24819,
-24826,
-24826,
-24842,
-24858,
-24872,
-24872,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24906,
-24906,
-24917,
-24928,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24963,
-24983,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-25007,
-25007,
-25007,
-25007,
-25007,
-25007,
-25007,
-25030,
-25040,
-25040,
-25040,
-25040,
-25040,
-25059,
-25097,
-25132,
-25149,
-25159,
-25169,
-25169,
-25169,
-25192,
-25192,
-25192,
-25192,
-25205,
-25205,
-25216,
-25221,
-25221,
-25233,
-25233,
-25240,
-25250,
-25256,
-25273,
-25273,
-25303,
-25321,
-25321,
-25321,
-25333,
-25333,
-25333,
-25333,
-25370,
-25370,
-25402,
-25418,
-25418,
-25439,
-25439,
-25454,
-25454,
-25454,
-25463,
-25477,
-25526,
-25526,
-25526,
-25526,
-25545,
-25562,
-25572,
-25572,
-25582,
-25582,
-25582,
-25597,
-25610,
-25634,
-25641,
-25641,
-25641,
-25668,
-25668,
-25675,
-25707,
-25727,
-25727,
-25741,
-25756,
-25756,
-25779,
-25811,
-25811,
-25811,
-25817,
-25817,
-25817,
-25827,
-25827,
-25827,
-25827,
-25827,
-25836,
-25836,
-25836,
-25836,
-25850,
-25850,
-25860,
-25884,
-25901,
-25922,
-25936,
-25946,
-25969,
-25969,
-25969,
-25969,
-25975,
-25975,
-25987,
-25987,
-26065,
-26065,
-26065,
-26084,
-26084,
-26103,
-26128,
-26141,
-26151,
-26169,
-26169,
-26169,
-26180,
-26191,
-26191,
-26191,
-26197,
-26197,
-26205,
-26211,
-26235,
-26235,
-26235,
-26235,
-26235,
-26235,
-26245,
-26245,
-26259,
-26259,
-26259,
-26259,
-26284,
-26284,
-26325,
-26325,
-26355,
-26364,
-26364,
-26402,
-26418,
-26418,
-26425,
-26432,
-26432,
-26454,
-26504,
-26513,
-26525,
-26525,
-26525,
-26525,
-26525,
-26545,
-26545,
-26571,
-26590,
-26597,
-26597,
-26597,
-26597,
-26597,
-26639,
-26648,
-26659,
-26666,
-26672,
-26672,
-26672,
-26672,
-26672,
-26694,
-26701,
-26701,
-26701,
-26724,
-26724,
-26746,
-26753,
-26774,
-26774,
-26774,
-26774,
-26806,
-26824,
-26824,
-26830,
-26852,
-26882,
-26882,
-26889,
-26889,
-26889,
-26889,
-26889,
-26889,
-26903,
-26911,
-26918,
-26918,
-26928,
-26948,
-26948,
-26970,
-26970,
-26985,
-26996,
-27045,
-27045,
-27058,
-27058,
-27068,
-27068,
-27104,
-27155,
-27155,
-27155,
-27155,
-27155,
-27172,
-27172,
-27172,
-27172,
-27189,
-27195,
-27195,
-27223,
-27223,
-27223,
-27223,
-27244,
-27290,
-27322,
-27332,
-27364,
-27371,
-27371,
-27371,
-27401,
-27401,
-27417,
-27417,
-27431,
-27470,
-27470,
-27470,
-27470,
-27484,
-27484,
-27484,
-27484,
-27484,
-27496,
-27496,
-27515,
-27515,
-27543,
-27560,
-27576,
-27604,
-27622,
-27631,
-27631,
-27638,
-27649,
-27672,
-27682,
-27682,
-27682,
-27707,
-27717,
-27724,
-27732,
-27755,
-27755,
-27755,
-27768,
-27768,
-27783,
-27789,
-27799,
-27799,
-27799,
-27818,
-27826,
-27838,
-27848,
-27848,
-27848,
-27855,
-27862,
-27862,
-27896,
-27921,
-27921,
-27943,
-27954,
-27954,
-27976,
-27976,
-27976,
-27985,
-28002,
-28012,
-28019,
-28034,
-28034,
-28046,
-28068,
-28097,
-28122,
-28122,
-28131,
-28137,
-28151,
-28151,
-28172,
-28190,
-28196,
-28211,
-28211,
-28264,
-28273,
-28286,
-28324,
-28324,
-28324,
-28354,
-28354,
-28361,
-28397,
-28417,
-28417,
-28424,
-28435,
-28461,
-28461,
-28470,
-28483,
-28483,
-28483,
-28483,
-28483,
-28483,
-28483,
-28515,
-28531,
-28531,
-28549,
-28575,
-28575,
-28575,
-28582,
-28599,
-28615,
-28630,
-28630,
-28672,
-28711,
-28723,
-28723,
-28731,
-28752,
-28752,
-28752,
-28763,
-28763,
-28775,
-28775,
-28775,
-28775,
-28775,
-28775,
-28805,
-28814,
-28830,
-28861,
-28882,
-28882,
-28902,
-28918,
-28937,
-28952,
-28959,
-28998,
-29009,
-29009,
-29009,
-29009,
-29019,
-29019,
-29019,
-29019,
-29019,
-29057,
-29069,
-29076,
-29076,
-29076,
-29076,
-29076,
-29082,
-29082,
-29082,
-29117,
-29117,
-29117,
-29117,
-29134,
-29134,
-29159,
-29159,
-29185,
-29185,
-29196,
-29196,
-29242,
-29248,
-29256,
-29280,
-29301,
-29307,
-29307,
-29307,
-29314,
-29314,
-29338,
-29356,
-29367,
-29367,
-29381,
-29391,
-29399,
-29399,
-29414,
-29434,
-29434,
-29441,
-29473,
-29484,
-29503,
-29520,
-29520,
-29548,
-29565,
-29572,
-29572,
-29572,
-29572,
-29572,
-29597,
-29597,
-29620,
-29655,
-29660,
-29660,
-29660,
-29667,
-29674,
-29688,
-29698,
-29705,
-29728,
-29740,
-29740,
-29761,
-29761,
-29767,
-29780,
-29787,
-29794,
-29794,
-29807,
-29820,
-29820,
-29820,
-29820,
-29832,
-29844,
-29855,
-29855,
-29855,
-29867,
-29867,
-29867,
-29867,
-29881,
-29881,
-29905,
-29905,
-29905,
-29923,
-29923,
-29948,
-29948,
-29948,
-29976,
-29986,
-29996,
-29996,
-30030,
-30030,
-30054,
-30054,
-30070,
-30070,
-30070,
-30077,
-30077,
-30087,
-30107,
-30107,
-30115,
-30141,
-30178,
-30178,
-30201,
-30201,
-30201,
-30207,
-30229,
-30239,
-30254,
-30268,
-30277,
-30311,
-30323,
-30323,
-30331,
-30331,
-30331,
-30331,
-30331,
-30353,
-30365,
-30374,
-30374,
-30374,
-30380,
-30380,
-30380,
-30380,
-30410,
-30410,
-30410,
-30443,
-30443,
-30453,
-30462,
-30472,
-30472,
-30472,
-30472,
-30472,
-30472,
-30489,
-30500,
-30500,
-30500,
-30532,
-30532,
-30553,
-30577,
-30599,
-30599,
-30609,
-30640,
-30640,
-30640,
-30664,
-30676,
-30676,
-30676,
-30692,
-30719,
-30728,
-30728,
-30742,
-30742,
-30749,
-30749,
-30760,
-30770,
-30770,
-30783,
-30783,
-30783,
-30804,
-30847,
-30847,
-30847,
-30847,
-30857,
-30857,
-30857,
-30857,
-30875,
-30875,
-30895,
-30895,
-30921,
-30926,
-30926,
-30926,
-30945,
-30945,
-30945,
-30945,
-30959,
-30959,
-30959,
-30959,
-30972,
-30972,
-30984,
-31011,
-31011,
-31048,
-31056,
-31056,
-31070,
-31077,
-31077,
-31110,
-31110,
-31115,
-31122,
-31139,
-31159,
-31159,
-31165,
-31171,
-31171,
-31197,
-31204,
-31211,
-31211,
-31221,
-31228,
-31228,
-31245,
-31245,
-31245,
-31252,
-31265,
-31265,
-31265,
-31265,
-31294,
-31305,
-31320,
-31333,
-31333,
-31333,
-31343,
-31350,
-31357,
-31369,
-31369,
-31379,
-31385,
-31391,
-31407,
-31407,
-31407,
-31423,
-31423,
-31423,
-31434,
-31454,
-31470,
-31511,
-31521,
-31521,
-31521,
-31542,
-31582,
-31582,
-31597,
-31597,
-31597,
-31614,
-31623,
-31645,
-31645,
-31661,
-31661,
-31669,
-31669,
-31676,
-31676,
-31706,
-31720,
-31726,
-31743,
-31785,
-31804,
-31817,
-31817,
-31835,
-31846,
-31863,
-31885,
-31885,
-31896,
-31907,
-31907,
-31907,
-31922,
-31922,
-31929,
-31929,
-31929,
-31936,
-31943,
-31949,
-31949,
-31949,
-31959,
-32006,
-32024,
-32031,
-32031,
-32038,
-32063,
-32095,
-32095,
-32105,
-32105,
-32105,
-32105,
-32105,
-32125,
-32134,
-32140,
-32176,
-32185,
-32195,
-32195,
-32195,
-32195,
-32202,
-32202,
-32218,
-32236,
-32259,
-32294,
-32300,
-32305,
-32305,
-32305,
-32323,
-32337,
-32352,
-32359,
-32374,
-32381,
-32388,
-32388,
-32388,
-32402,
-32402,
-32402,
-32402,
-32418,
-32428,
-32428,
-32428,
-32450,
-32450,
-32450,
-32462,
-32467,
-32480,
-32480,
-32480,
-32487,
-32502,
-32509,
-32525,
-32560,
-32570,
-32583,
-32597,
-32623,
-32637,
-32644,
-32667,
-32707,
-32725,
-32725,
-32747,
-32747,
-32751,
-32758,
-32789,
-32807,
-32824,
-32824,
-32824,
-32824,
-32843,
-32843,
-32850,
-32876,
-32908,
-32915,
-32946,
-32965,
-32965,
-32982,
-33002,
-33009,
-33029,
-33064,
-33084,
-33098,
-33098,
-33098,
-33098,
-33110,
-33110,
-33151,
-33158,
-33180,
-33198,
-33205,
-33227,
-33227,
-33237,
-33237,
-33253,
-33258,
-33277,
-33292,
-33315,
-33315,
-33333,
-33348,
-33348,
-33348,
-33348,
-33348,
-33348,
-33348,
-33355,
-33355,
-33355,
-33390,
-33408,
-33423,
-33437,
-33452,
-33458,
-33465,
-33480,
-33480,
-33487,
-33494,
-33504,
-33511,
-33551,
-33551,
-33558,
-33589,
-33595,
-33595,
-33602,
-33627,
-33644,
-33668,
-33668,
-33668,
-33676,
-33676,
-33716,
-33728,
-33747,
-33747,
-33769,
-33775,
-33789,
-33803,
-33803,
-33810,
-33810,
-33810,
-33820,
-33820,
-33820,
-33820,
-33843,
-33843,
-33843,
-33879,
-33889,
-33889,
-33889,
-33903,
-33917,
-33931,
-33959,
-33993,
-34000,
-34014,
-34037,
-34043,
-34055,
-34055,
-34077,
-34083,
-34090,
-34099,
-34099,
-34115,
-34115,
-34133,
-34140,
-34167,
-34172,
-34184,
-34221,
-34245,
-34252,
-34252,
-34259,
-34318,
-34318,
-34325,
-34325,
-34352,
-34400,
-34415,
-34422,
-34422,
-34431,
-34438,
-34445,
-34445,
-34473,
-34473,
-34489,
-34489,
-34489,
-34489,
-34499,
-34499,
-34499,
-34516,
-34536,
-34551,
-34564,
-34580,
-34580,
-34580,
-34589,
-34589,
-34589,
-34613,
-34648,
-34648,
-34648,
-34655,
-34664,
-34681,
-34681,
-34698,
-34698,
-34720,
-34736,
-34749,
-34749,
-34765,
-34778,
-34785,
-34795,
-34819,
-34819,
-34829,
-34841,
-34848,
-34854,
-34854,
-34854,
-34878,
-34894,
-34894,
-34900,
-34917,
-34934,
-34940,
-34970,
-34998,
-34998,
-35004,
-35004,
-35012,
-35012,
-35012,
-35020,
-35020,
-35032,
-35038,
-35062,
-35062,
-35062,
-35068,
-35068,
-35082,
-35092,
-35096,
-35107,
-35118,
-35134,
-35155,
-35155,
-35166,
-35178,
-35178,
-35195,
-35201,
-35201,
-35201,
-35226,
-35226,
-35226,
-35226,
-35256,
-35262,
-35272,
-35280,
-35299,
-35332,
-35354,
-35354,
-35354,
-35370,
-35386,
-35417,
-35417,
-35460,
-35473,
-35478,
-35495,
-35504,
-35504,
-35518,
-35552,
-35589,
-35624,
-35624,
-35637,
-35637,
-35643,
-35643,
-35669,
-35682,
-35695,
-35702,
-35709,
-35709,
-35726,
-35739,
-35749,
-35756,
-35756,
-35778,
-35803,
-35810,
-35829,
-35883,
-35899,
-35905,
-35911,
-35911,
-35923,
-35947,
-35954,
-35980,
-35987,
-36034,
-36052,
-36063,
-36095,
-36106,
-36106,
-36113,
-36120,
-36140,
-36140,
-36153,
-36160,
-36160,
-36167,
-36203,
-36203,
-36218,
-36218,
-36225,
-36253,
-36259,
-36284,
-36296,
-36310,
-36324,
-36331,
-36344,
-36367,
-36367,
-36367,
-36412,
-36412,
-36422,
-36463,
-36463,
-36463,
-36479,
-36490,
-36513,
-36520,
-36520,
-36527,
-36527,
-36527,
-36540,
-36574,
-36594,
-36594,
-36605,
-36621,
-36621,
-36641,
-36641,
-36641,
-36659,
-36682,
-36682,
-36682,
-36682,
-36705,
-36705,
-36705,
-36720,
-36720,
-36755,
-36755,
-36771,
-36771,
-36771,
-36788,
-36806,
-36835,
-36845,
-36875,
-36875,
-36903,
-36921,
-36928,
-36928,
-36940,
-36940,
-36940,
-36966,
-36966,
-36973,
-36983,
-36998,
-37004,
-37014,
-37024,
-37024,
-37032,
-37038,
-37038,
-37061,
-37074,
-37074,
-37091,
-37098,
-37105,
-37105,
-37133,
-37141,
-37141,
-37148,
-37191,
-37191,
-37197,
-37197,
-37210,
-37224,
-37224,
-37231,
-37250,
-37257,
-37273,
-37273,
-37280,
-37287,
-37294,
-37300,
-37307,
-37330,
-37348,
-37348,
-37359,
-37359,
-37359,
-37377,
-37392,
-37398,
-37412,
-37431,
-37469,
-37486,
-37508,
-37517,
-37535,
-37535,
-37542,
-37542,
-37549,
-37549,
-37549,
-37549,
-37556,
-37576,
-37576,
-37583,
-37590,
-37597,
-37604,
-37604,
-37621,
-37635,
-37676,
-37676,
-37704,
-37711,
-37728,
-37728,
-37737,
-37737,
-37737,
-37750,
-37757,
-37778,
-37785,
-37785,
-37819,
-37826,
-37833,
-37843,
-37850,
-37869,
-37914,
-37921,
-37935,
-37942,
-37949,
-37982,
-38013,
-38013,
-38013,
-38023,
-38057,
-38077,
-38097,
-38110,
-38117,
-38123,
-38133,
-38133,
-38133,
-38140,
-38140,
-38148,
-38159,
-38179,
-38192,
-38205,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38225,
-38225,
-38230,
-38246,
-38258,
-38280,
-38287,
-38294,
-38294,
-38294,
-38301,
-38318,
-38318,
-38340,
-38371,
-38371,
-38384,
-38420,
-38440,
-38453,
-38481,
-38506,
-38522,
-38534,
-38559,
-38559,
-38559,
-38564,
-38564,
-38581,
-38604,
-38604,
-38611,
-38620,
-38626,
-38635,
-38635,
-38635,
-38666,
-38674,
-38688,
-38693,
-38710,
-38722,
-38722,
-38722,
-38729,
-38734,
-38752,
-38792,
-38818,
-38825,
-38861,
-38902,
-38934,
-38949,
-38949,
-38960,
-38969,
-38985,
-38985,
-38996,
-39013,
-39024,
-39024,
-39032,
-39061,
-39074,
-39089,
-39123,
-39123,
-39123,
-39140,
-39161,
-39180,
-39206,
-39215,
-39254,
-39261,
-39277,
-39284,
-39314,
-39314,
-39330,
-39340,
-39340,
-39371,
-39371,
-39392,
-39430,
-39430,
-39437,
-39444,
-39461,
-39468,
-39468,
-39485,
-39517,
-39524,
-39538,
-39543,
-39548,
-39555,
-39581,
-39588,
-39588,
-39609,
-39609,
-39616,
-39652,
-39670,
-39677,
-39677,
-39684,
-39691,
-39702,
-39717,
-39717,
-39717,
-39724,
-39749,
-39760,
-39766,
-39775,
-39791,
-39791,
-39814,
-39827,
-39827,
-39837,
-39859};
-
-static const char tldData[] = {
-"com.cn\0"
-"com.co\0"
-"hb.cn\0"
-"med.br\0conf.lv\0wallonie.museum\0"
-"namsos.no\0"
-"\xe7\xb6\xb2\xe7\xbb\x9c.hk\0farmers.museum\0rel.pl\0"
-"com.cu\0"
-"military.museum\0"
-"*.jm\0convent.museum\0cymru.museum\0malvik.no\0"
-"univ.sn\0"
-"gliding.aero\0"
-"wodzislaw.pl\0"
-"com.dm\0!pref.iwate.jp\0tran\xc3\xb8y.no\0pila.pl\0"
-"mb.it\0*.ke\0lib.ri.us\0"
-"com.ec\0*.kh\0tr\xc3\xb8gstad.no\0"
-"com.ee\0"
-"mobi.gp\0"
-"gran.no\0"
-"wa.gov.au\0"
-"com.dz\0kg.kr\0"
-"zoological.museum\0gjerstad.no\0haugesund.no\0kharkov.ua\0"
-"walbrzych.pl\0"
-"civilization.museum\0"
-"ha.no\0"
-"*.kw\0"
-"med.ec\0com.es\0"
-"med.ee\0otago.museum\0svelvik.no\0"
-"art.ht\0amber.museum\0elvendrell.museum\0rost.no\0"
-"jx.cn\0gratangen.no\0"
-"association.aero\0ca.it\0"
-"zaporizhzhe.ua\0"
-"com.fr\0"
-"szex.hu\0"
-"e-burg.ru\0"
-"com.ge\0bokn.no\0"
-"mordovia.ru\0"
-"com.gh\0*.mm\0"
-"com.gi\0z.se\0"
-"cahcesuolo.no\0"
-"hurdal.no\0joshkar-ola.ru\0"
-"cadaques.museum\0ma.us\0"
-"a.bg\0"
-"com.gn\0bozen.it\0tambov.ru\0"
-"*.gifu.jp\0*.tokyo.jp\0*.mt\0"
-"com.gp\0travel\0cc.tx.us\0"
-"com.gr\0hemne.no\0"
-"*.ni\0"
-"*.mz\0"
-"cc.il.us\0"
-"com.gy\0"
-"zj.cn\0oksnes.no\0museum.tt\0"
-"com.hk\0*.np\0"
-"rc.it\0baseball.museum\0"
-"com.hn\0exhibition.museum\0"
-"h\xc3\xa1""bmer.no\0"
-"com.hr\0"
-"fg.it\0stathelle.no\0defense.tn\0"
-"com.ht\0"
-"qld.gov.au\0*.nz\0"
-"davvenj\xc3\xa1rga.no\0*.om\0"
-"vang.no\0"
-"*.kumamoto.jp\0"
-"vercelli.it\0usenet.pl\0"
-"com.io\0stalbans.museum\0"
-"com.iq\0"
-"*.pg\0"
-"com.is\0klabu.no\0skiptvet.no\0"
-"med.ht\0field.museum\0"
-"gr.it\0gj\xc3\xb8vik.no\0tromsa.no\0lib.mi.us\0"
-"ca.na\0"
-"hagebostad.no\0k12.ma.us\0"
-"*.qa\0"
-"*.niigata.jp\0"
-"monzaebrianza.it\0com.jo\0comunica\xc3\xa7\xc3\xb5""es.museum\0"
-"gr.jp\0"
-"ballangen.no\0*.py\0"
-"scienceandindustry.museum\0"
-"nuoro.it\0com.kg\0"
-"com.ki\0"
-"im.it\0idv.tw\0"
-"*.akita.jp\0com.km\0r\xc3\xb8ros.no\0sopot.pl\0"
-"!pref.yamanashi.jp\0"
-"com.kp\0"
-"!pref.kochi.jp\0com.la\0"
-"com.lb\0"
-"com.lc\0stjordalshalsen.no\0sigdal.no\0cc.nm.us\0"
-"samnanger.no\0"
-"drobak.no\0"
-"vt.it\0"
-"catering.aero\0com.ky\0"
-"com.kz\0cc.ca.us\0"
-"com.lk\0"
-"grosseto.it\0mosvik.no\0"
-"namsskogan.no\0"
-"loten.no\0"
-"chirurgiens-dentistes.fr\0"
-"com.lr\0bremanger.no\0"
-"gs.cn\0"
-"com.lv\0lib.co.us\0"
-"com.mg\0"
-"passenger-association.aero\0"
-"com.ly\0yekaterinburg.ru\0"
-"vladivostok.ru\0"
-"com.mk\0beeldengeluid.museum\0"
-"com.ml\0"
-"art.pl\0"
-"com.mo\0"
-"britishcolumbia.museum\0tx.us\0"
-"com.na\0sakhalin.ru\0*.sv\0"
-"mc.it\0"
-"amsterdam.museum\0udm.ru\0"
-"com.mu\0"
-"com.mv\0com.nf\0"
-"com.mw\0com.ng\0il.us\0"
-"geometre-expert.fr\0com.mx\0"
-"med.ly\0com.my\0"
-"ag.it\0"
-"*.tr\0"
-"!pref.oita.jp\0"
-"hoyanger.no\0skedsmo.no\0"
-"com.nr\0turystyka.pl\0"
-"koebenhavn.museum\0"
-"quebec.museum\0"
-"stord.no\0*.uk\0"
-"act.au\0"
-"br.it\0cb.it\0gyeonggi.kr\0jobs.tt\0lib.hi.us\0"
-"*.ve\0"
-"*.saga.jp\0wildlife.museum\0com.pa\0"
-"monzabrianza.it\0sciencehistory.museum\0stange.no\0oskol.ru\0principe.st\0*.uy\0"
-"plaza.museum\0com.pe\0"
-"com.pf\0"
-"eigersund.no\0"
-"com.ph\0"
-"manx.museum\0marylhurst.museum\0"
-"md.ci\0pi.it\0schweiz.museum\0com.pk\0"
-"grp.lk\0fr\xc3\xb8ya.no\0com.pl\0press.se\0"
-"us.com\0"
-"b.bg\0cremona.it\0communication.museum\0art.sn\0"
-"med.pa\0"
-"com.pr\0"
-"com.ps\0"
-"com.pt\0"
-"k12.in.us\0"
-"ah.cn\0bahcavuotna.no\0"
-"sondrio.it\0arkhangelsk.ru\0"
-"cargo.aero\0"
-"council.aero\0"
-"museum.mv\0hattfjelldal.no\0spydeberg.no\0med.pl\0"
-"niepce.museum\0museum.mw\0"
-"anthropology.museum\0"
-"pharmacien.fr\0smola.no\0"
-"fin.ec\0"
-"selbu.no\0"
-"workinggroup.aero\0nm.us\0"
-"museum.no\0com.re\0"
-"cc.vt.us\0"
-"village.museum\0"
-"ca.us\0"
-"*.sapporo.jp\0"
-"teramo.it\0"
-"com.ro\0"
-"*.ye\0"
-"com.sa\0"
-"com.sb\0"
-"so.it\0com.sc\0"
-"jolster.no\0com.sd\0"
-"com.ru\0"
-"com.rw\0com.sg\0"
-"sydney.museum\0"
-"sa.edu.au\0"
-"tom.ru\0"
-"com.sl\0*.za\0"
-"\xe7\xbd\x91\xe7\xb5\xa1.hk\0naturbruksgymn.se\0com.sn\0"
-"assedic.fr\0com.so\0"
-"!pref.mie.jp\0*.yu\0"
-"med.sa\0"
-"newspaper.museum\0holmestrand.no\0dnepropetrovsk.ua\0"
-"christiansburg.museum\0roan.no\0"
-"pesaro-urbino.it\0med.sd\0com.st\0"
-"s\xc3\xb8gne.no\0"
-"nuernberg.museum\0"
-"*.zm\0"
-"com.sy\0"
-"*.nagano.jp\0com.tj\0"
-"nt.gov.au\0news.hu\0paderborn.museum\0"
-"boston.museum\0"
-"com.tn\0"
-"com.to\0"
-"broadcast.museum\0"
-"com.ua\0"
-"*.zw\0"
-"baikal.ru\0"
-"bykle.no\0com.tt\0"
-"verdal.no\0"
-"roros.no\0"
-"fi.cr\0carboniaiglesias.it\0chuvashia.ru\0com.tw\0"
-"k12.ca.us\0"
-"eidsvoll.no\0"
-"*.ishikawa.jp\0"
-"dolls.museum\0"
-"naval.museum\0"
-"karasjok.no\0tysvar.no\0"
-"bielawa.pl\0com.vc\0"
-"svalbard.no\0deatnu.no\0rnd.ru\0"
-"grandrapids.museum\0"
-"bauern.museum\0k12.pr.us\0"
-"press.ma\0"
-"*.kagawa.jp\0fribourg.museum\0przeworsk.pl\0com.vi\0"
-"com.uz\0"
-"babia-gora.pl\0"
-"com.vn\0"
-"med.pro\0"
-"suedtirol.it\0kursk.ru\0"
-"bonn.museum\0"
-"lt.it\0"
-"design.aero\0microlight.aero\0americanantiques.museum\0meland.no\0"
-"insurance.aero\0aarborte.no\0"
-"kh.ua\0"
-"macerata.it\0architecture.museum\0"
-"rovigo.it\0rawa-maz.pl\0"
-"store.nf\0levanger.no\0"
-"b\xc3\xa1jddar.no\0"
-"not.br\0"
-"com.ws\0"
-"!pref.kagawa.jp\0"
-"!omanpost.om\0"
-"vt.us\0"
-"gs.ah.no\0vladikavkaz.ru\0"
-"no.it\0"
-"in.na\0szkola.pl\0a.se\0"
-"aid.pl\0"
-"workshop.museum\0vegarshei.no\0"
-"sund.no\0"
-"bs.it\0flora.no\0"
-"agriculture.museum\0"
-"koeln.museum\0"
-"minnesota.museum\0k12.il.us\0"
-"froya.no\0"
-"aeroport.fr\0"
-"davvenjarga.no\0zgora.pl\0ivano-frankivsk.ua\0"
-"*.gunma.jp\0"
-"amot.no\0"
-"mus.br\0chungbuk.kr\0"
-"ggf.br\0lorenskog.no\0"
-"jeonbuk.kr\0"
-"k12.vi.us\0"
-"c.bg\0sande.more-og-romsdal.no\0"
-"perugia.it\0"
-"massa-carrara.it\0"
-"michigan.museum\0"
-"archaeology.museum\0mosj\xc3\xb8""en.no\0czest.pl\0koenig.ru\0\xe0\xb6\xbd\xe0\xb6\x82\xe0\xb6\x9a\xe0\xb7\x8f\0"
-"mobi.tt\0"
-"kraanghke.no\0"
-"cc.in.us\0"
-"re.it\0lib.vt.us\0"
-"dell-ogliastra.it\0"
-"s\xc3\xb8mna.no\0"
-"k12.wv.us\0"
-"gok.pk\0fh.se\0"
-"luzern.museum\0"
-"fi.it\0swidnica.pl\0"
-"cbg.ru\0"
-"latina.it\0"
-"vibovalentia.it\0"
-"modum.no\0"
-"safety.aero\0"
-"sp.it\0"
-"science.museum\0ah.no\0"
-"norddal.no\0"
-"cc.na\0"
-"re.kr\0"
-"dielddanuorri.no\0"
-"force.museum\0"
-"torino.it\0cc.md.us\0"
-"artanddesign.museum\0pisz.pl\0"
-"olsztyn.pl\0"
-"unsa.ba\0rade.no\0vinnica.ua\0"
-"in.rs\0astrakhan.ru\0"
-"sogne.no\0"
-"homebuilt.aero\0"
-"polkowice.pl\0"
-"hole.no\0health.vn\0"
-"fj.cn\0"
-"davvesiida.no\0"
-"vic.au\0"
-"kongsberg.no\0"
-"pub.sa\0"
-"vv.it\0"
-"!pref.tottori.jp\0"
-"*.sendai.jp\0in.th\0"
-"lib.pa.us\0"
-"chiropractic.museum\0"
-"mobi.na\0aca.pro\0"
-"konyvelo.hu\0sciencecenters.museum\0"
-"he.cn\0"
-"in.ua\0"
-"!city.nagoya.jp\0"
-"muenchen.museum\0"
-"psi.br\0"
-"maryland.museum\0"
-"!statecouncil.om\0"
-"tr\xc3\xa6na.no\0"
-"!pref.yamagata.jp\0jewishart.museum\0"
-"lu.it\0me.it\0"
-"chel.ru\0"
-"tatarstan.ru\0"
-"adult.ht\0in.us\0"
-"kafjord.no\0"
-"\xd7\x99\xd7\xa8\xd7\x95\xd7\xa9\xd7\x9c\xd7\x99\xd7\x9d.museum\0"
-"net.ac\0k12.ec\0"
-"net.ae\0bashkiria.ru\0"
-"net.af\0!omantel.om\0"
-"net.ag\0"
-"net.ai\0"
-"!pref.toyama.jp\0"
-"net.al\0timekeeping.museum\0"
-"net.an\0design.museum\0fin.tn\0"
-"ethnology.museum\0"
-"perso.ht\0asker.no\0b.se\0"
-"net.ba\0"
-"net.bb\0flanders.museum\0"
-"mincom.tn\0"
-"frana.no\0"
-"bt.it\0"
-"net.bh\0"
-"auto.pl\0"
-"net.az\0"
-"treviso.it\0"
-"war.museum\0"
-"net.bm\0"
-"langevag.no\0m\xc3\xa5lselv.no\0"
-"net.bo\0"
-"gol.no\0"
-"folkebibl.no\0"
-"net.br\0"
-"net.bs\0troandin.no\0saotome.st\0lib.tn.us\0"
-"md.us\0k12.ut.us\0"
-"d.bg\0cambridge.museum\0\xc3\xa5s.no\0"
-"net.ci\0"
-"net.bz\0"
-"sch.ae\0undersea.museum\0odda.no\0"
-"net.cn\0"
-"net.co\0c.la\0"
-"gliwice.pl\0"
-"aurskog-h\xc3\xb8land.no\0"
-"andria-trani-barletta.it\0"
-"net.cu\0loab\xc3\xa1t.no\0"
-"rep.kp\0"
-"\xe7\xbb\x84\xe7\xb9\x94.hk\0"
-"gallery.museum\0"
-"\xc3\xb8rland.no\0"
-"store.ro\0"
-"net.dm\0"
-"somna.no\0"
-"hemnes.no\0"
-"ringebu.no\0k12.ky.us\0"
-"net.ec\0"
-"dn.ua\0"
-"tarnobrzeg.pl\0"
-"soc.lk\0"
-"romsa.no\0"
-"bamble.no\0"
-"net.dz\0lutsk.ua\0"
-"barlettatraniandria.it\0ta.it\0countryestate.museum\0"
-"kaszuby.pl\0"
-"*.yamaguchi.jp\0cranbrook.museum\0store.st\0"
-"southcarolina.museum\0lib.md.us\0"
-"textile.museum\0"
-"cheltenham.museum\0hurum.no\0"
-"*.oita.jp\0"
-"shop.ht\0cc.me.us\0"
-"shop.hu\0turin.it\0"
-"louvre.museum\0"
-"k12.ar.us\0"
-"consulting.aero\0"
-"gv.ao\0"
-"sauherad.no\0"
-"gv.at\0net.ge\0"
-"ostre-toten.no\0lib.ok.us\0"
-"net.gg\0pilots.museum\0"
-"2000.hu\0geology.museum\0"
-"net.gn\0"
-"mazowsze.pl\0bir.ru\0"
-"net.gp\0"
-"net.gr\0"
-"oxford.museum\0"
-"per.la\0"
-"eastafrica.museum\0"
-"meeres.museum\0"
-"net.gy\0*.shizuoka.jp\0"
-"\xe5\x95\x86\xe6\xa5\xad.tw\0"
-"net.hk\0"
-"net.hn\0"
-"philadelphiaarea.museum\0"
-"osen.no\0"
-"net.ht\0net.id\0"
-"fundacio.museum\0"
-"j\xc3\xb8rpeland.no\0"
-"\xe6\x95\x99\xe8\x82\xb2.hk\0"
-"divtasvuodna.no\0"
-"student.aero\0sch.gg\0net.im\0"
-"\xe7\xbd\x91\xe7\xbb\x9c.cn\0net.in\0"
-"net.iq\0"
-"net.ir\0"
-"net.is\0"
-"net.je\0"
-"kepno.pl\0lapy.pl\0"
-"per.nf\0"
-"gov\0*.shimane.jp\0"
-"artcenter.museum\0"
-"k\xc3\xa5""fjord.no\0"
-"net.jo\0"
-"eu.int\0"
-"c.se\0"
-"net.kg\0"
-"ce.it\0net.ki\0"
-"sch.id\0os.hedmark.no\0"
-"columbus.museum\0"
-"arteducation.museum\0"
-"net.kn\0"
-"kr.com\0"
-"net.la\0bushey.museum\0cc.gu.us\0"
-"net.lb\0"
-"net.lc\0"
-"gs.bu.no\0"
-"e164.arpa\0"
-"chieti.it\0labour.museum\0"
-"sch.ir\0creation.museum\0krodsherad.no\0"
-"net.ky\0"
-"net.kz\0me.us\0"
-"e.bg\0sch.je\0net.lk\0"
-"zlg.br\0suwalki.pl\0"
-"\xe5\x80\x8b\xe4\xba\xba.hk\0net.ma\0"
-"net.lr\0"
-"sch.jo\0notaires.km\0net.me\0"
-"net.lv\0karate.museum\0"
-"net.ly\0karm\xc3\xb8y.no\0"
-"rg.it\0"
-"net.mk\0"
-"net.ml\0evenes.no\0"
-"ngo.lk\0net.mo\0egyptian.museum\0"
-"marine.ru\0"
-"realestate.pl\0"
-"net.mu\0"
-"net.mv\0net.nf\0"
-"net.mw\0net.ng\0gda.pl\0"
-"net.mx\0"
-"freemasonry.museum\0net.my\0enebakk.no\0"
-"karlsoy.no\0"
-"\xe7\xbd\x91\xe7\xbb\x9c.hk\0\xc3\xb8rskog.no\0"
-"randaberg.no\0"
-"club.aero\0"
-"certification.aero\0sr.it\0"
-"center.museum\0so.gov.pl\0"
-"caa.aero\0"
-"sch.lk\0tvedestrand.no\0"
-"net.nr\0"
-"luroy.no\0"
-"aukra.no\0s\xc3\xa1lat.no\0lib.me.us\0"
-"ddr.museum\0"
-"york.museum\0stryn.no\0k12.nm.us\0"
-"per.sg\0"
-"judaica.museum\0"
-"verona.it\0"
-"agdenes.no\0"
-"cng.br\0sch.ly\0"
-"net.pa\0"
-"author.aero\0"
-"naturalhistory.museum\0steiermark.museum\0bu.no\0"
-"sn\xc3\xa5sa.no\0net.pe\0"
-"net.ph\0"
-"savannahga.museum\0batsfjord.no\0lib.oh.us\0"
-"net.pk\0"
-"net.pl\0"
-"net.pn\0"
-"washingtondc.museum\0"
-"net.pr\0"
-"net.ps\0"
-"net.pt\0"
-"nordkapp.no\0"
-"emergency.aero\0krokstadelva.no\0"
-"satx.museum\0ngo.ph\0omsk.ru\0"
-"texas.museum\0"
-"ngo.pl\0"
-"mantova.it\0gu.us\0"
-"!pref.shiga.jp\0isa.us\0"
-"usa.museum\0"
-"gb.net\0k12.vi\0"
-"iveland.no\0"
-"tempio-olbia.it\0"
-"net.sa\0"
-"net.sb\0"
-"works.aero\0net.sc\0komvux.se\0"
-"net.sd\0"
-"net.ru\0"
-"0.bg\0"
-"forlicesena.it\0net.rw\0net.sg\0"
-"klodzko.pl\0"
-"detroit.museum\0wegrow.pl\0"
-"net.sl\0"
-"glogow.pl\0"
-"store.bb\0air.museum\0"
-"net.so\0"
-"katowice.pl\0"
-"nsk.ru\0"
-"pisa.it\0eid.no\0"
-"net.st\0"
-"film.hu\0"
-"tuva.ru\0d.se\0"
-"net.th\0"
-"net.sy\0"
-"viterbo.it\0tsaritsyn.ru\0perso.sn\0net.tj\0"
-"lib.gu.us\0"
-"plc.co.im\0sec.ps\0"
-"r\xc3\xa1hkker\xc3\xa1vju.no\0kazimierz-dolny.pl\0net.tn\0"
-"net.to\0"
-"veterinaire.km\0"
-"net.ua\0"
-"info.ht\0net.tt\0"
-"info.hu\0"
-"exchange.aero\0"
-"sch.sa\0net.tw\0"
-"andriatranibarletta.it\0perso.tn\0"
-"f.bg\0malselv.no\0"
-"net.vc\0"
-"trana.no\0"
-"ns.ca\0"
-"net.vi\0"
-"lucca.it\0oristano.it\0"
-"usarts.museum\0net.vn\0"
-"gon.pk\0"
-"pl.ua\0"
-"eastcoast.museum\0"
-"novara.it\0"
-"k12.ks.us\0"
-"dp.ua\0"
-"nesseby.no\0"
-"!pref.wakayama.jp\0"
-"repbody.aero\0"
-"jamison.museum\0lugansk.ua\0"
-"ss.it\0"
-"alessandria.it\0"
-"hadsel.no\0net.ws\0"
-"\xe0\xae\x9a\xe0\xae\xbf\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xae\xaa\xe0\xaf\x8d\xe0\xae\xaa\xe0\xaf\x82\xe0\xae\xb0\xe0\xaf\x8d\0"
-"veterinaire.fr\0leirfjord.no\0"
-"massacarrara.it\0north.museum\0"
-"project.museum\0"
-"other.nf\0"
-"k12.nh.us\0"
-"mat.br\0artgallery.museum\0"
-"sr.gov.pl\0"
-"gamvik.no\0"
-"info.ec\0lancashire.museum\0"
-"fm.br\0ltd.co.im\0"
-"americana.museum\0southwest.museum\0cc.ak.us\0"
-"enna.it\0lunner.no\0"
-"v\xc3\xa5gan.no\0"
-"mari.ru\0"
-"accident-investigation.aero\0"
-"sor-aurdal.no\0lib.ny.us\0"
-"novosibirsk.ru\0"
-"bjugn.no\0"
-"n\xc3\xa6r\xc3\xb8y.no\0ostrowwlkp.pl\0"
-"info.bb\0foundation.museum\0"
-"brand.se\0"
-"info.at\0!pref.akita.jp\0l\xc3\xb8ten.no\0"
-"coal.museum\0miners.museum\0"
-"glass.museum\0"
-"info.az\0"
-"frog.museum\0szczytno.pl\0nov.ru\0"
-"sunndal.no\0"
-"gen.in\0"
-"gx.cn\0"
-"web.co\0*.mie.jp\0hobol.no\0\xe5\x8f\xb0\xe6\xb9\xbe\0"
-"logistics.aero\0plo.ps\0"
-"erotika.hu\0"
-"torsken.no\0"
-"exeter.museum\0"
-"info.co\0"
-"selje.no\0"
-"storfjord.no\0"
-"barum.no\0lind\xc3\xa5s.no\0"
-"leasing.aero\0"
-"championship.aero\0fst.br\0"
-"lierne.no\0"
-"!gobiernoelectronico.ar\0""1.bg\0"
-"corporation.museum\0"
-"al.it\0*.miyagi.jp\0"
-"*.aomori.jp\0"
-"\xd8\xa7\xd9\x84\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x86\0"
-"amursk.ru\0"
-"vestvagoy.no\0"
-"\xd8\xa7\xdb\x8c\xd8\xb1\xd8\xa7\xd9\x86.ir\0cc.fl.us\0"
-"os.hordaland.no\0"
-"pistoia.it\0"
-"tver.ru\0e.se\0"
-"res.in\0*.yamagata.jp\0syzran.ru\0"
-"capebreton.museum\0sandnessj\xc3\xb8""en.no\0"
-"ternopil.ua\0"
-"shop.pl\0"
-"tank.museum\0"
-"m\xc3\xa5s\xc3\xb8y.no\0"
-"potenza.it\0time.museum\0"
-"mjondalen.no\0"
-"eng.br\0nedre-eiker.no\0"
-"air-surveillance.aero\0"
-"nt.au\0am.br\0pn.it\0"
-"oystre-slidre.no\0ug.gov.pl\0"
-"g.bg\0nesodden.no\0vologda.ru\0"
-"parma.it\0tula.ru\0"
-"*.nara.jp\0ak.us\0"
-"nt.ca\0konin.pl\0"
-"kiev.ua\0"
-"skierv\xc3\xa1.no\0vestre-toten.no\0"
-"ri.it\0botanical.museum\0farsund.no\0veg\xc3\xa5rshei.no\0dagestan.ru\0"
-"ind.br\0k-uralsk.ru\0"
-"rahkkeravju.no\0cmw.ru\0"
-"canada.museum\0"
-"fm.it\0"
-"cc.wi.us\0"
-"web.id\0aver\xc3\xb8y.no\0"
-"dudinka.ru\0"
-"baghdad.museum\0fitjar.no\0grane.no\0"
-"gs.fm.no\0"
-"sumy.ua\0"
-"al.no\0"
-"westfalen.museum\0"
-"oregon.museum\0"
-"bruxelles.museum\0elk.pl\0"
-"planetarium.museum\0sn\xc3\xa5""ase.no\0"
-"s\xc3\xb8rreisa.no\0"
-"gs.st.no\0skien.no\0"
-"bible.museum\0ivanovo.ru\0"
-"avellino.it\0"
-"tgory.pl\0"
-"family.museum\0"
-"ppg.br\0k12.as.us\0"
-"trader.aero\0gorlice.pl\0"
-"cc.al.us\0"
-"ogliastra.it\0"
-"is.it\0lib.nv.us\0"
-"dr.na\0"
-"media.hu\0nesna.no\0fl.us\0"
-"uri.arpa\0"
-"bjerkreim.no\0"
-"charter.aero\0"
-"genova.it\0"
-"it.ao\0botany.museum\0hapmir.no\0"
-"educational.museum\0"
-"helsinki.museum\0"
-"memorial.museum\0"
-"web.lk\0pharmacy.museum\0"
-"aircraft.aero\0appspot.com\0"
-"ferrara.it\0beskidy.pl\0"
-"hi.cn\0"
-"taxi.aero\0flekkefjord.no\0"
-"varoy.no\0"
-"ragusa.it\0ambulance.museum\0"
-"can.museum\0"
-"*.osaka.jp\0isleofman.museum\0fm.no\0warmia.pl\0"
-"educator.aero\0asmatart.museum\0"
-"mi.it\0"
-"kutno.pl\0"
-"skedsmokorset.no\0"
-"2.bg\0"
-"*.kagoshima.jp\0km.ua\0"
-"!city.sendai.jp\0"
-"web.nf\0st.no\0cc.ri.us\0"
-"reggiocalabria.it\0"
-"wi.us\0"
-"ancona.it\0newjersey.museum\0nnov.ru\0"
-"f.se\0"
-"ind.in\0"
-"info.vn\0"
-"andoy.no\0"
-"ch.it\0fredrikstad.no\0guovdageaidnu.no\0"
-"fjaler.no\0"
-"sa.com\0"
-"gs.nt.no\0"
-"masfjorden.no\0"
-"pordenone.it\0"
-"po.it\0basel.museum\0"
-"chambagri.fr\0"
-"h.bg\0web.pk\0"
-"london.museum\0"
-"sciencecenter.museum\0\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2\0"
-"unbi.ba\0augustow.pl\0"
-"wolomin.pl\0"
-"notaires.fr\0tcm.museum\0al.us\0"
-"nu.ca\0!pref.nagano.jp\0"
-"info.tn\0"
-"lib.wa.us\0"
-"ed.ao\0info.tt\0"
-"barreau.bj\0"
-"k12.wy.us\0"
-"pp.az\0gop.pk\0"
-"int\0"
-"l\xc3\xb8renskog.no\0podhale.pl\0"
-"voagat.no\0"
-"telekommunikation.museum\0"
-"qld.au\0"
-"te.it\0freiburg.museum\0snasa.no\0"
-"gjemnes.no\0"
-"sejny.pl\0"
-"media.pl\0"
-"skjak.no\0"
-"watchandclock.museum\0"
-"ed.ci\0pacific.museum\0"
-"theater.museum\0info.ro\0"
-"uk.com\0"
-"campobasso.it\0aquarium.museum\0tysv\xc3\xa6r.no\0"
-"kragero.no\0"
-"windmill.museum\0info.sd\0"
-"sologne.museum\0sande.m\xc3\xb8re-og-romsdal.no\0"
-"nt.no\0cc.mi.us\0"
-"ed.cr\0"
-"academy.museum\0zachpomor.pl\0"
-"tananger.no\0v\xc3\xa1rgg\xc3\xa1t.no\0ri.us\0"
-"federation.aero\0"
-"web.tj\0"
-"matta-varjjat.no\0"
-"steigen.no\0"
-"local\0akrehamn.no\0"
-"!pref.chiba.jp\0info.pk\0"
-"info.pl\0""6bone.pl\0"
-"klepp.no\0kherson.ua\0"
-"ketrzyn.pl\0info.pr\0"
-"sweden.museum\0"
-"lardal.no\0"
-"!retina.ar\0gz.cn\0"
-"barletta-trani-andria.it\0vikna.no\0"
-"bearalv\xc3\xa1hki.no\0"
-"broker.aero\0gov.nc.tr\0"
-"info.na\0k12.fl.us\0"
-"hembygdsforbund.museum\0"
-"entertainment.aero\0jerusalem.museum\0l\xc3\xa6rdal.no\0"
-"hitra.no\0sogndal.no\0"
-"farmequipment.museum\0info.mv\0info.nf\0\xc3\xa5lg\xc3\xa5rd.no\0"
-"la-spezia.it\0"
-"skanland.no\0fam.pk\0"
-"skole.museum\0"
-"art.museum\0"
-"presidio.museum\0"
-"3.bg\0public.museum\0"
-"h\xc3\xb8yanger.no\0zagan.pl\0"
-"an.it\0"
-"philadelphia.museum\0info.nr\0"
-"pesarourbino.it\0g\xc3\xa1ivuotna.no\0"
-"poltava.ua\0"
-"nt.ro\0"
-"station.museum\0"
-"mi.th\0"
-"altoadige.it\0"
-"nu.it\0"
-"usculture.museum\0g.se\0"
-"h\xc3\xa1mm\xc3\xa1rfeasta.no\0"
-"daegu.kr\0info.la\0"
-"dovre.no\0"
-"ci.it\0horology.museum\0"
-"bergbau.museum\0"
-"press.museum\0"
-"gangwon.kr\0"
-"!city.kitakyushu.jp\0sor-varanger.no\0cc.hi.us\0"
-"fuossko.no\0"
-"zp.ua\0"
-"american.museum\0"
-"fl\xc3\xa5.no\0mi.us\0"
-"i.bg\0"
-"od.ua\0"
-"encyclopedic.museum\0"
-"ind.tn\0"
-"midatlantic.museum\0"
-"newyork.museum\0"
-"castres.museum\0"
-"act.edu.au\0"
-"topology.museum\0"
-"ed.jp\0"
-"of.by\0"
-"iris.arpa\0inf.br\0askim.no\0pyatigorsk.ru\0"
-"nord-fron.no\0nsn.us\0"
-"beardu.no\0"
-"agrar.hu\0corvette.museum\0chtr.k12.ma.us\0"
-"figueres.museum\0"
-"!pref.gunma.jp\0medizinhistorisches.museum\0"
-"tjeldsund.no\0"
-"nebraska.museum\0"
-"bellevue.museum\0"
-"abo.pa\0k12.al.us\0"
-"info.ki\0"
-"inf.cu\0sv.it\0"
-"jfk.museum\0"
-"!city.osaka.jp\0swinoujscie.pl\0"
-"bydgoszcz.pl\0"
-"!city.kyoto.jp\0"
-"uvic.museum\0"
-"madrid.museum\0steinkjer.no\0"
-"lib.ma.us\0"
-"sirdal.no\0"
-"n\xc3\xb8tter\xc3\xb8y.no\0"
-"taranto.it\0starnberg.museum\0"
-"vic.gov.au\0pvt.ge\0pors\xc3\xa1\xc5\x8bgu.no\0"
-"naroy.no\0ris\xc3\xb8r.no\0"
-"va.it\0salem.museum\0starachowice.pl\0"
-"!nawrastelecom.om\0"
-"town.museum\0te.ua\0"
-"se.net\0"
-"kemerovo.ru\0"
-"lerdal.no\0"
-"gs.va.no\0"
-"kms.ru\0"
-"consulado.st\0"
-"haram.no\0"
-"tysnes.no\0"
-"!pref.ibaraki.jp\0hamburg.museum\0"
-"\xc3\xa5rdal.no\0"
-"airline.aero\0"
-"crew.aero\0newhampshire.museum\0"
-"muenster.museum\0"
-"aerodrome.aero\0"
-"heroy.nordland.no\0belau.pw\0"
-"kamchatka.ru\0"
-"b\xc3\xa5""d\xc3\xa5""ddj\xc3\xa5.no\0lillehammer.no\0hi.us\0"
-"hk.cn\0"
-"!city.kobe.jp\0berlevag.no\0"
-"ardal.no\0"
-"askoy.no\0"
-"vardo.no\0"
-"fyresdal.no\0"
-"sassari.it\0"
-"video.hu\0drammen.no\0"
-"lyngen.no\0nakhodka.ru\0"
-"ip6.arpa\0games.hu\0"
-"online.museum\0"
-"k12.sd.us\0"
-"4.bg\0sebastopol.ua\0"
-"ao.it\0atlanta.museum\0"
-"lebork.pl\0"
-"ravenna.it\0"
-"railway.museum\0songdalen.no\0"
-"!pref.shimane.jp\0delaware.museum\0ed.pw\0"
-"f\xc3\xb8rde.no\0"
-"living.museum\0"
-"juif.museum\0"
-"lomza.pl\0"
-"h.se\0"
-"!bl.uk\0"
-"portland.museum\0\xe7\xb5\x84\xe7\xb9\x94.tw\0"
-"stj\xc3\xb8rdal.no\0"
-"lecce.it\0"
-"bz.it\0"
-"farmstead.museum\0va.no\0"
-"express.aero\0!nacion.ar\0"
-"presse.km\0gs.of.no\0"
-"\xe5\x8f\xb0\xe7\x81\xa3\0"
-"og.ao\0gyeongbuk.kr\0vestv\xc3\xa5g\xc3\xb8y.no\0"
-"prd.fr\0"
-"pp.ru\0pp.se\0"
-"forum.hu\0!pref.saga.jp\0"
-"kvalsund.no\0"
-"!city.kawasaki.jp\0n\xc3\xa5\xc3\xa5mesjevuemie.no\0"
-"j.bg\0"
-"vlaanderen.museum\0"
-"cc.va.us\0"
-"\xd8\xa7\xd9\x8a\xd8\xb1\xd8\xa7\xd9\x86.ir\0alabama.museum\0"
-"school.museum\0her\xc3\xb8y.m\xc3\xb8re-og-romsdal.no\0"
-"\xc3\xa5seral.no\0"
-"traniandriabarletta.it\0"
-"flog.br\0"
-"presse.ml\0"
-"k\xc3\xa1r\xc3\xa1\xc5\xa1johka.no\0"
-"historisch.museum\0"
-"farm.museum\0palmsprings.museum\0oslo.no\0dyroy.no\0stranda.no\0"
-"gs.rl.no\0r\xc3\xa5""de.no\0"
-"bomlo.no\0s\xc3\xb8rum.no\0"
-"jan-mayen.no\0ivgu.no\0"
-"coop\0"
-"agr.br\0k12.ak.us\0"
-"!nic.ar\0catanzaro.it\0fusa.no\0"
-"hu.com\0"
-"inf.mk\0"
-"vet.br\0"
-"k12.mt.us\0k12.nd.us\0"
-"vlog.br\0\xe5\x85\xac\xe5\x8f\xb8.cn\0sandnessjoen.no\0"
-"lib.az.us\0"
-"nsw.edu.au\0of.no\0\xc3\xb8stre-toten.no\0"
-"*.okinawa.jp\0"
-"vb.it\0"
-"asso.fr\0firenze.it\0"
-"trieste.it\0"
-"\xe5\x85\xac\xe5\x8f\xb8.hk\0"
-"museet.museum\0"
-"prd.km\0"
-"navuotna.no\0lib.ca.us\0"
-"cc.nv.us\0"
-"asso.gp\0"
-"meraker.no\0"
-"h\xc3\xa1pmir.no\0"
-"i.ph\0"
-"sx.cn\0jeonnam.kr\0"
-"halden.no\0"
-"fed.us\0"
-"medio-campidano.it\0tsk.ru\0"
-"barcelona.museum\0"
-"giessen.museum\0roma.museum\0"
-"hl.cn\0"
-"\xe0\xae\x87\xe0\xae\xb2\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xaf\x88\0"
-"biz.bb\0benevento.it\0rl.no\0bygland.no\0"
-"port.fr\0asso.ht\0prd.mg\0"
-"biz.at\0"
-"tra.kp\0"
-"*.aichi.jp\0khabarovsk.ru\0"
-"campidano-medio.it\0"
-"biz.az\0"
-"newmexico.museum\0va.us\0"
-"finearts.museum\0"
-"murmansk.ru\0"
-"\xc3\xb8rsta.no\0radom.pl\0k12.sc.us\0"
-"5.bg\0kvinesdal.no\0"
-"ap.it\0"
-"*.fukushima.jp\0"
-"asso.bj\0"
-"mad.museum\0"
-"lebesby.no\0"
-"og.it\0glas.museum\0sauda.no\0"
-"i.se\0"
-"k12.tx.us\0"
-"asso.ci\0mk.ua\0"
-"cesena-forli.it\0"
-"lowicz.pl\0"
-"k12.id.us\0"
-"tas.gov.au\0"
-"lukow.pl\0"
-"utazas.hu\0"
-"maritimo.museum\0bjark\xc3\xb8y.no\0"
-"adm.br\0"
-"pr.it\0lib.vi.us\0"
-"bergamo.it\0k12.va.us\0"
-"k.bg\0"
-"railroad.museum\0"
-"!british-library.uk\0"
-"cincinnati.museum\0"
-"sorreisa.no\0"
-"asso.dz\0!nel.uk\0"
-"rm.it\0"
-"nv.us\0"
-"nx.cn\0gos.pk\0"
-"vic.edu.au\0"
-"biella.it\0tjome.no\0"
-"r\xc3\xb8yken.no\0"
-"beiarn.no\0"
-"qc.ca\0"
-"georgia.museum\0square.museum\0"
-"labor.museum\0omasvuotna.no\0cc.la.us\0"
-"br.com\0reggioemilia.it\0"
-"kristiansund.no\0"
-"sorum.no\0"
-"orsta.no\0"
-"furniture.museum\0surrey.museum\0eng.pro\0"
-"asn.lv\0balat.no\0"
-"lavangen.no\0sld.pa\0"
-"fla.no\0k12.ms.us\0k12.nc.us\0"
-"bardu.no\0"
-"donostia.museum\0"
-"club.tw\0"
-"elburg.museum\0"
-"gs.hl.no\0lodingen.no\0"
-"samara.ru\0"
-"vc.it\0*.nagasaki.jp\0"
-"fosnes.no\0"
-"fuel.aero\0"
-"qc.com\0"
-"skjervoy.no\0"
-"bill.museum\0kv\xc3\xa6""fjord.no\0"
-"skydiving.aero\0*.tokushima.jp\0"
-"!congresodelalengua3.ar\0laquila.it\0k12.ct.us\0"
-"gorge.museum\0linz.museum\0sherbrooke.museum\0"
-"tranoy.no\0ing.pa\0"
-"ptz.ru\0"
-"kr.it\0prato.it\0stat.no\0"
-"\xd0\xb8\xd0\xba\xd0\xbe\xd0\xbc.museum\0"
-"cosenza.it\0"
-"stj\xc3\xb8rdalshalsen.no\0"
-"finland.museum\0leka.no\0cc.pr.us\0"
-"historichouses.museum\0s\xc3\xa1l\xc3\xa1t.no\0"
-"venice.it\0"
-"biz.ki\0"
-"g\xc3\xa1ls\xc3\xa1.no\0"
-"\xe7\xbb\x84\xe7\xbb\x87.hk\0"
-"*.yamanashi.jp\0"
-"rad\xc3\xb8y.no\0"
-"6.bg\0"
-"fareast.ru\0"
-"paragliding.aero\0ba.it\0aq.it\0"
-"sk\xc3\xa5nland.no\0"
-"its.me\0"
-"us.na\0"
-"hl.no\0cc.ga.us\0"
-"ac\0granvin.no\0"
-"ad\0qld.edu.au\0!city.sapporo.jp\0"
-"ae\0"
-"af\0"
-"ag\0crotone.it\0"
-"dallas.museum\0"
-"ai\0brussels.museum\0"
-"dali.museum\0"
-"la.us\0"
-"al\0salzburg.museum\0"
-"am\0"
-"an\0cl.it\0"
-"ao\0"
-"aq\0ba\0"
-"bb\0"
-"as\0lajolla.museum\0"
-"at\0"
-"be\0"
-"bf\0inderoy.no\0snz.ru\0"
-"aw\0bg\0"
-"ax\0bh\0cim.br\0ltd.gi\0biz.mv\0"
-"bi\0xz.cn\0\xe7\xb5\x84\xe7\xb9\x94.hk\0biz.mw\0"
-"az\0bj\0"
-"bm\0tranibarlettaandria.it\0naamesjevuemie.no\0"
-"chattanooga.museum\0"
-"bo\0"
-"l.bg\0"
-"ca\0"
-"br\0stateofdelaware.museum\0"
-"bs\0cc\0"
-"cd\0biz.nr\0"
-"cf\0berlev\xc3\xa5g.no\0"
-"bw\0cg\0snaase.no\0"
-"ch\0harvestcelebration.museum\0ck.ua\0"
-"by\0ci\0"
-"bz\0bahccavuotna.no\0"
-"cl\0yuzhno-sakhalinsk.ru\0"
-"cm\0halsa.no\0lyngdal.no\0"
-"cn\0"
-"co\0rn.it\0childrens.museum\0frankfurt.museum\0"
-"cr\0"
-"pskov.ru\0"
-"cu\0de\0"
-"cv\0fr.it\0lib.ky.us\0"
-"aseral.no\0kvam.no\0"
-"cx\0hellas.museum\0"
-"hof.no\0"
-"cz\0dj\0k12.la.us\0"
-"dk\0moscow.museum\0"
-"sosnowiec.pl\0"
-"dm\0biz.pk\0"
-"schokoladen.museum\0biz.pl\0"
-"far.br\0arna.no\0tynset.no\0"
-"even\xc3\xa1\xc5\xa1\xc5\xa1i.no\0"
-"ec\0"
-"biz.pr\0"
-"ee\0celtic.museum\0"
-"scientist.aero\0modern.museum\0"
-"pr.us\0"
-"dz\0"
-"mj\xc3\xb8ndalen.no\0s\xc3\xb8r-odal.no\0"
-"!nic.tr\0"
-"conference.aero\0vestnes.no\0k12.mn.us\0"
-"!pref.hiroshima.jp\0"
-"es\0trapani.it\0"
-"fermo.it\0vard\xc3\xb8.no\0"
-"eu\0gs.hm.no\0r\xc3\xb8""d\xc3\xb8y.no\0stordal.no\0"
-"gc.ca\0!nhs.uk\0"
-"jgora.pl\0"
-"fi\0stjordal.no\0"
-"fm\0!mediaphone.om\0"
-"kirov.ru\0pvt.k12.ma.us\0"
-"fo\0"
-"ga\0hyllestad.no\0"
-"gov.ac\0fr\0andriabarlettatrani.it\0ga.us\0"
-"gov.ae\0gd\0estate.museum\0"
-"gov.af\0ge\0tolga.no\0"
-"gf\0asso.re\0cc.oh.us\0"
-"gg\0florida.museum\0"
-"presse.ci\0gh\0"
-"gi\0k12.dc.us\0"
-"ltd.lk\0orland.no\0"
-"gov.al\0"
-"gl\0tokke.no\0"
-"hanggliding.aero\0gm\0"
-"hareid.no\0"
-"gov.ba\0tj.cn\0gp\0"
-"gov.bb\0gq\0"
-"gov.as\0gr\0agrigento.it\0lc.it\0"
-"gs\0kalmykia.ru\0aero.tt\0"
-"gov.bf\0"
-"county.museum\0"
-"gov.bh\0hn.cn\0gw\0"
-"gov.az\0gy\0assn.lk\0guernsey.museum\0"
-"hk\0"
-"gov.bm\0h\xc3\xa6gebostad.no\0biz.tj\0"
-"hm\0computer.museum\0"
-"gov.bo\0hn\0kl\xc3\xa6""bu.no\0"
-"pulawy.pl\0"
-"gov.br\0"
-"trd.br\0gov.bs\0hr\0reggio-calabria.it\0historyofscience.museum\0lipetsk.ru\0"
-"gov.cd\0*.nagoya.jp\0"
-"ht\0id\0spjelkavik.no\0"
-"hu\0ie\0aero.mv\0"
-"marketplace.aero\0mn.it\0biz.tt\0"
-"gov.by\0saintlouis.museum\0mer\xc3\xa5ker.no\0"
-"gov.bz\0"
-"7.bg\0gov.cl\0virtual.museum\0"
-"gov.cm\0vennesla.no\0kr.ua\0"
-"gov.cn\0im\0ar.it\0galsa.no\0rovno.ua\0"
-"gov.co\0in\0"
-"io\0limanowa.pl\0"
-"iq\0k12.ga.us\0"
-"ir\0"
-"riik.ee\0is\0\xc3\xa1laheadju.no\0"
-"gov.cu\0it\0hawaii.museum\0seaport.museum\0"
-"je\0pubol.museum\0hm.no\0"
-"gov.cx\0"
-"*.chiba.jp\0"
-"*.kawasaki.jp\0"
-"k.se\0"
-"gov.dm\0"
-"aland.fi\0vik.no\0"
-"yk.ca\0jo\0kobierzyce.pl\0"
-"jp\0biz.vn\0"
-"presse.fr\0lib.il.us\0\xe9\xa6\x99\xe6\xb8\xaf\0"
-"gov.ec\0"
-"transport.museum\0bronnoy.no\0"
-"slg.br\0gov.ee\0asso.nc\0bievat.no\0"
-"nyny.museum\0"
-"kg\0"
-"mo-i-rana.no\0"
-"gov.dz\0ki\0"
-"monmouth.museum\0"
-"suldal.no\0"
-"bc.ca\0km\0zt.ua\0"
-"pt.it\0kn\0"
-"fineart.museum\0"
-"la\0"
-"kr\0gulen.no\0"
-"m.bg\0mo.cn\0lc\0alaheadju.no\0g\xc3\xa1\xc5\x8bgaviika.no\0"
-"nowaruda.pl\0cc.ut.us\0"
-"br\xc3\xb8nn\xc3\xb8y.no\0"
-"ky\0li\0overhalla.no\0"
-"kz\0khv.ru\0"
-"lk\0"
-"artdeco.museum\0"
-"ma\0fortworth.museum\0kostroma.ru\0"
-"ro.it\0kirkenes.no\0vestby.no\0"
-"urbino-pesaro.it\0ls\0mc\0alstahaug.no\0"
-"blog.br\0gov.ge\0lt\0md\0"
-"lu\0me\0botanicgarden.museum\0"
-"gov.gg\0lv\0oh.us\0"
-"gov.gh\0mg\0valley.museum\0"
-"gov.gi\0mh\0"
-"ly\0sandiego.museum\0"
-"mk\0"
-"ml\0"
-"gov.gn\0rollag.no\0naklo.pl\0"
-"mn\0"
-"mo\0"
-"mp\0leirvik.no\0"
-"gov.gr\0mq\0na\0cc.ks.us\0"
-"mr\0"
-"ms\0nc\0"
-"valer.hedmark.no\0"
-"mu\0ne\0"
-"mv\0nf\0"
-"mw\0"
-"mx\0nord-odal.no\0jur.pro\0"
-"my\0"
-"gov.hk\0name.hr\0"
-"nl\0"
-"astronomy.museum\0lib.nm.us\0"
-"catania.it\0"
-"no\0"
-"skjerv\xc3\xb8y.no\0"
-"k12.ne.us\0"
-"monza-e-della-brianza.it\0!pref.fukushima.jp\0nr\0"
-"gov.ie\0"
-"stuttgart.museum\0nu\0cc.mn.us\0"
-"karasjohka.no\0"
-"engine.aero\0bearalvahki.no\0"
-"oyer.no\0"
-"ve.it\0"
-"gov.im\0froland.no\0cc.ar.us\0"
-"gov.in\0magadan.ru\0"
-"pescara.it\0"
-"gov.iq\0usdecorativearts.museum\0"
-"gov.ir\0pa\0"
-"gov.is\0"
-"gov.it\0lavagis.no\0"
-"gov.je\0"
-"naustdal.no\0pe\0k12.or.us\0"
-"gd.cn\0carraramassa.it\0pf\0"
-"ph\0"
-"cc.ny.us\0"
-"rissa.no\0"
-"info\0pk\0pomorze.pl\0"
-"pl\0"
-"gov.jo\0asso.km\0pn\0"
-"*.okayama.jp\0cieszyn.pl\0"
-"freight.aero\0"
-"pr\0"
-"narvik.no\0ps\0"
-"!pref.aichi.jp\0elverum.no\0pt\0"
-"edunet.tn\0"
-"gov.kg\0"
-"flatanger.no\0marker.no\0pw\0"
-"gov.ki\0nuremberg.museum\0"
-"aip.ee\0"
-"gov.km\0"
-"gov.kn\0"
-"gov.kp\0"
-"rieti.it\0gov.la\0bajddar.no\0"
-"gov.lb\0aviation.museum\0"
-"gov.lc\0"
-"asso.mc\0"
-"re\0"
-"ut.us\0"
-"sa.gov.au\0gov.ky\0"
-"mo.it\0gov.kz\0"
-"gov.lk\0"
-"iraq.museum\0"
-"badajoz.museum\0"
-"8.bg\0inder\xc3\xb8y.no\0"
-"monticello.museum\0ro\0ks.ua\0"
-"gov.ma\0svizzera.museum\0"
-"gov.lr\0sa\0"
-"matera.it\0sb\0"
-"gov.lt\0rs\0sc\0"
-"gov.me\0sd\0"
-"gov.lv\0ru\0se\0"
-"gov.mg\0"
-"rw\0sg\0"
-"gov.ly\0assisi.museum\0kids.museum\0sh\0"
-"si\0"
-"gov.mk\0"
-"gov.ml\0sk\0"
-"sl\0"
-"gov.mn\0airguard.museum\0sm\0"
-"gov.mo\0l.se\0sn\0"
-"so\0"
-"gov.mr\0ks.us\0"
-"name.az\0sr\0"
-"naturhistorisches.museum\0tc\0"
-"trainer.aero\0cn.it\0urbinopesaro.it\0gov.mu\0nativeamerican.museum\0st\0td\0"
-"gov.mv\0su\0"
-"trentino.it\0gov.mw\0gov.ng\0tf\0"
-"tg\0"
-"co.ae\0venezia.it\0gov.my\0th\0"
-"!pref.ehime.jp\0sy\0"
-"co.ag\0lewismiller.museum\0ostrowiec.pl\0sz\0tj\0"
-"tk\0"
-"motorcycle.museum\0tl\0"
-"birdart.museum\0trogstad.no\0tm\0"
-"tn\0"
-"humanities.museum\0to\0"
-"pu.it\0gov.nr\0ua\0lib.ut.us\0"
-"co.ao\0"
-"co.ba\0trondheim.no\0tt\0"
-"in-addr.arpa\0tempioolbia.it\0!city.yokohama.jp\0mn.us\0"
-"n.bg\0schoenbrunn.museum\0tv\0"
-"co.at\0aremark.no\0tw\0ug\0"
-"jus.br\0"
-"co.bi\0bialowieza.pl\0ar.us\0"
-"audnedaln.no\0kustanai.ru\0"
-"va\0"
-"us\0vc\0"
-"newport.museum\0"
-"kopervik.no\0gov.ph\0vg\0"
-"ny.us\0vi\0"
-"co.bw\0finn\xc3\xb8y.no\0gov.pk\0uz\0"
-"honefoss.no\0gov.pl\0lanbib.se\0"
-"co.ci\0"
-"gov.pn\0intl.tn\0"
-"act.gov.au\0vn\0"
-"television.museum\0gov.pr\0"
-"sykkylven.no\0v\xc3\xa5ler.hedmark.no\0gov.ps\0"
-"gov.pt\0"
-"co.cr\0vu\0"
-"legnica.pl\0"
-"sa.au\0"
-"bjarkoy.no\0"
-"openair.museum\0birkenes.no\0lib.nj.us\0"
-"fylkesbibl.no\0holt\xc3\xa5len.no\0"
-"iz.hr\0"
-"ws\0"
-"oceanographique.museum\0"
-"b\xc3\xa1id\xc3\xa1r.no\0cc.mo.us\0"
-"\xc3\xb8ygarden.no\0"
-"contemporary.museum\0"
-"gb.com\0cc.as.us\0"
-"belluno.it\0gov.sa\0"
-"gov.sb\0"
-"gov.rs\0gov.sc\0"
-"gov.sd\0"
-"!pref.nagasaki.jp\0gov.ru\0"
-"asia\0"
-"sa.cr\0gov.rw\0gov.sg\0"
-"kuzbass.ru\0"
-"gs.vf.no\0"
-"gov.sl\0"
-"norfolk.museum\0"
-"k12.de.us\0"
-"mil\0"
-"rendalen.no\0"
-"gov.st\0"
-"agro.pl\0"
-"orkdal.no\0"
-"le.it\0gov.sy\0"
-"gov.tj\0"
-"co.gg\0nore-og-uvdal.no\0v\xc3\xa5ler.\xc3\xb8stfold.no\0"
-"gov.tl\0"
-"gov.tn\0"
-"gov.to\0"
-"kids.us\0"
-"equipment.aero\0gov.ua\0"
-"!city.niigata.jp\0gov.tt\0"
-"sel.no\0"
-"l\xc3\xa4ns.museum\0"
-"gov.tw\0"
-"rennebu.no\0"
-"egersund.no\0"
-"medecin.km\0"
-"co.gy\0"
-"!mecon.ar\0"
-"berlin.museum\0"
-"carrara-massa.it\0"
-"9.bg\0"
-"pri.ee\0gov.vc\0"
-"at.it\0"
-"muosat.no\0"
-"co.id\0"
-"co.hu\0"
-"etne.no\0"
-"\xc3\xa1lt\xc3\xa1.no\0"
-"gov.vn\0"
-"modelling.aero\0"
-"co.im\0"
-"co.in\0\xc3\xa5krehamn.no\0m.se\0"
-"gouv.fr\0*.kitakyushu.jp\0"
-"narviika.no\0"
-"rennes\xc3\xb8y.no\0"
-"co.ir\0afjord.no\0"
-"lea\xc5\x8bgaviika.no\0buryatia.ru\0"
-"co.it\0coastaldefence.museum\0"
-"co.je\0vf.no\0"
-"osteroy.no\0"
-"uslivinghistory.museum\0"
-"aerobatic.aero\0"
-"mesaverde.museum\0mining.museum\0"
-"a\xc3\xa9roport.ci\0gov.ws\0"
-"co.jp\0copenhagen.museum\0"
-"pv.it\0"
-"r\xc3\xb8mskog.no\0"
-"vossevangen.no\0porsanger.no\0"
-"salat.no\0mo.us\0"
-"o.bg\0imperia.it\0carrier.museum\0"
-"carbonia-iglesias.it\0"
-"as.us\0"
-"alvdal.no\0"
-"state.museum\0mandal.no\0cn.ua\0"
-"cuneo.it\0"
-"gouv.ht\0"
-"!city.okayama.jp\0co.kr\0"
-"co.lc\0"
-"sa.it\0"
-"donna.no\0"
-"sortland.no\0"
-"tomsk.ru\0"
-"birthplace.museum\0l\xc3\xb8""dingen.no\0"
-"ge.it\0orenburg.ru\0"
-"cn.com\0"
-"co.ma\0"
-"co.ls\0skaun.no\0name.vn\0"
-"navigation.aero\0"
-"cagliari.it\0co.me\0portal.museum\0"
-"gouv.bj\0"
-"udine.it\0"
-"engineer.aero\0"
-"szczecin.pl\0"
-"wales.museum\0"
-"co.na\0bo.telemark.no\0"
-"austin.museum\0"
-"k12.mo.us\0"
-"co.mu\0"
-"gouv.ci\0"
-"co.mw\0"
-"esp.br\0"
-"naturalhistorymuseum.museum\0"
-"mosjoen.no\0"
-"solund.no\0"
-"name.tj\0"
-"sand\xc3\xb8y.no\0"
-"kunstunddesign.museum\0"
-"cartoonart.museum\0collection.museum\0gsm.pl\0"
-"aure.no\0"
-"!pref.yamaguchi.jp\0historical.museum\0"
-"name.tt\0"
-"england.museum\0valle.no\0"
-"cc.ok.us\0"
-"salangen.no\0"
-"gloppen.no\0"
-"cc.co.us\0"
-"contemporaryart.museum\0"
-"tas.edu.au\0"
-"trading.aero\0"
-"mazury.pl\0"
-"!pref.aomori.jp\0co.pl\0"
-"opoczno.pl\0"
-"*.kobe.jp\0co.pn\0"
-"oppegard.no\0"
-"co.pw\0"
-"saltdal.no\0smolensk.ru\0"
-"na.it\0\xc4\x8d\xc3\xa1hcesuolo.no\0"
-"vgs.no\0evenassi.no\0"
-"parachuting.aero\0jl.cn\0maritime.museum\0bd.se\0"
-"badaddja.no\0"
-"bergen.no\0"
-"brussel.museum\0"
-"avoues.fr\0"
-"cesenaforli.it\0"
-"oregontrail.museum\0"
-"ullensaker.no\0"
-"jobs\0"
-"accident-prevention.aero\0"
-"n.se\0"
-"association.museum\0california.museum\0"
-"cultural.museum\0co.rs\0"
-"zoology.museum\0"
-"pruszkow.pl\0"
-"control.aero\0nt.edu.au\0net\0komforb.se\0"
-"lincoln.museum\0aurland.no\0name.pr\0co.rw\0"
-"ostroleka.pl\0"
-"isernia.it\0"
-"tm.fr\0"
-"gs.ol.no\0"
-"nb.ca\0marnardal.no\0"
-"williamsburg.museum\0"
-"!jet.uk\0"
-"suisse.museum\0\xc3\xa5""fjord.no\0flakstad.no\0"
-"karmoy.no\0"
-"yn.cn\0chesapeakebay.museum\0"
-"nsw.au\0"
-"amur.ru\0co.st\0"
-"imb.br\0siellak.no\0\xe7\xb6\xb2\xe8\xb7\xaf.tw\0"
-"name.na\0"
-"co.th\0"
-"p.bg\0"
-"co.sz\0co.tj\0"
-"name.mv\0\xc3\xa5lesund.no\0lib.in.us\0"
-"lucerne.museum\0naumburg.museum\0"
-"society.museum\0name.my\0"
-"tinn.no\0"
-"co.tt\0"
-"unj\xc3\xa1rga.no\0"
-"co.ug\0"
-"lib.wy.us\0"
-"co.tz\0"
-"ass.km\0"
-"ok.us\0"
-"tm.hu\0kongsvinger.no\0"
-"ibestad.no\0"
-"juedisches.museum\0co.us\0"
-"cq.cn\0"
-"rs.ba\0"
-"wa.edu.au\0co.vi\0"
-"co.uz\0"
-"health.museum\0"
-"grue.no\0"
-"automotive.museum\0journalism.museum\0settlement.museum\0"
-"qh.cn\0interactive.museum\0"
-"snillfjord.no\0!national-library-scotland.uk\0"
-"balsfjord.no\0lib.nh.us\0"
-"kolobrzeg.pl\0"
-"gs.tm.no\0"
-"h\xc3\xb8nefoss.no\0"
-"ol.no\0"
-"music.museum\0moareke.no\0"
-"b\xc3\xb8.nordland.no\0"
-"name.mk\0lier.no\0"
-"eidfjord.no\0"
-"sc.cn\0tm.km\0"
-"jelenia-gora.pl\0sanok.pl\0"
-"intelligence.museum\0"
-"srv.br\0elblag.pl\0"
-"judygarland.museum\0"
-"padua.it\0"
-"k12.co.us\0"
-"lindesnes.no\0"
-"name.jo\0izhevsk.ru\0"
-"yorkshire.museum\0mel\xc3\xb8y.no\0"
-"tm.mc\0lib.pr.us\0"
-"hjartdal.no\0"
-"tm.mg\0"
-"bari.it\0milano.it\0"
-"lg.jp\0"
-"zgrad.ru\0"
-"sm\xc3\xb8la.no\0"
-"communications.museum\0"
-"arts.co\0seoul.kr\0engerdal.no\0"
-"oster\xc3\xb8y.no\0"
-"\xe6\x95\x8e\xe8\x82\xb2.hk\0foggia.it\0verran.no\0"
-"orskog.no\0voronezh.ru\0kv.ua\0"
-"av.it\0"
-"tm.no\0nissedal.no\0"
-"historisches.museum\0gs.mr.no\0"
-"medecin.fr\0"
-"montreal.museum\0"
-"o.se\0"
-"!metro.tokyo.jp\0sola.no\0"
-"k12.tn.us\0"
-"floro.no\0"
-"milan.it\0*.shiga.jp\0"
-"berkeley.museum\0"
-"maintenance.aero\0"
-"ws.na\0"
-"lindas.no\0cc.ia.us\0"
-"brescia.it\0embroidery.museum\0"
-"arezzo.it\0tm.pl\0"
-"r\xc3\xa6lingen.no\0"
-"burghof.museum\0"
-"rec.br\0"
-"q.bg\0"
-"!nawras.om\0"
-"hammarfeasta.no\0"
-"moss.no\0"
-"on.ca\0"
-"gouv.rw\0"
-"luxembourg.museum\0"
-"rec.co\0british.museum\0"
-"reggio-emilia.it\0"
-"gouv.sn\0lib.wv.us\0"
-"avocat.fr\0"
-"simbirsk.ru\0"
-"jar.ru\0"
-"monza-brianza.it\0"
-"tm.ro\0"
-"imageandsound.museum\0"
-"jpn.com\0mr.no\0"
-"siracusa.it\0"
-"norilsk.ru\0tm.se\0"
-"tn.it\0"
-"jeju.kr\0"
-"!pref.fukuoka.jp\0"
-"*.hyogo.jp\0portlligat.museum\0"
-"!pref.osaka.jp\0"
-"siena.it\0sc.kr\0omaha.museum\0saskatchewan.museum\0"
-"phoenix.museum\0vanylven.no\0"
-"botanicalgarden.museum\0"
-"turek.pl\0"
-"vagsoy.no\0"
-"riodejaneiro.museum\0"
-"vi.it\0"
-"uy.com\0"
-"kristiansand.no\0"
-"sd.cn\0trento.it\0"
-"muncie.museum\0"
-"berg.no\0meldal.no\0"
-"nes.buskerud.no\0"
-"saratov.ru\0"
-"gs.oslo.no\0"
-"harstad.no\0vaga.no\0"
-"research.museum\0"
-"brunel.museum\0ia.us\0"
-"test.tj\0"
-"columbia.museum\0"
-"ms.it\0stockholm.museum\0"
-"reklam.hu\0"
-"pomorskie.pl\0lg.ua\0"
-"bg.it\0historicalsociety.museum\0rns.tn\0"
-"mallorca.museum\0surgut.ru\0cc.sc.us\0"
-"ushistory.museum\0"
-"palana.ru\0"
-"snoasa.no\0"
-"naturalsciences.museum\0"
-"yaroslavl.ru\0"
-"unjarga.no\0"
-"p.se\0"
-"ingatlan.hu\0"
-"irc.pl\0"
-"savona.it\0"
-"cr.it\0"
-"test.ru\0cc.tn.us\0"
-"ms.kr\0museumvereniging.museum\0"
-"time.no\0k12.ia.us\0"
-"vladimir.ru\0"
-"correios-e-telecomunica\xc3\xa7\xc3\xb5""es.museum\0"
-"gouv.km\0nationalfirearms.museum\0"
-"m\xc3\xa1latvuopmi.no\0"
-"aero\0yosemite.museum\0"
-"r.bg\0school.na\0"
-"cc.vi.us\0"
-"*.wakayama.jp\0"
-"beauxarts.museum\0averoy.no\0ullensvang.no\0bar.pro\0"
-"!city.hiroshima.jp\0"
-"b\xc3\xa1hccavuotna.no\0"
-"frosta.no\0"
-"gdynia.pl\0"
-"medical.museum\0"
-"embaixada.st\0"
-"balsan.it\0vantaa.museum\0"
-"za.net\0"
-"!city.saitama.jp\0lib.ks.us\0"
-"fnd.br\0"
-"ru.com\0se.com\0hol.no\0modalen.no\0"
-"gouv.ml\0chukotka.ru\0"
-"malopolska.pl\0"
-"mansion.museum\0"
-"iki.fi\0children.museum\0"
-"cyber.museum\0rec.nf\0mo\xc3\xa5reke.no\0"
-"to.it\0"
-"hasvik.no\0"
-"\xc3\xb8yer.no\0"
-"arts.ro\0sc.ug\0"
-"lib.ar.us\0"
-"sc.tz\0cc.ms.us\0cc.nc.us\0"
-"etc.br\0poznan.pl\0"
-"cnt.br\0viking.museum\0"
-"*.miyazaki.jp\0"
-"melhus.no\0"
-"skodje.no\0vevelstad.no\0"
-"sc.us\0"
-"upow.gov.pl\0"
-"!city.fukuoka.jp\0brandywinevalley.museum\0natuurwetenschappen.museum\0tranby.no\0"
-"bahn.museum\0msk.ru\0"
-"delmenhorst.museum\0"
-"russia.museum\0fuoisku.no\0"
-"shell.museum\0"
-"r\xc3\xa1isa.no\0"
-"hs.kr\0udmurtia.ru\0"
-"palermo.it\0"
-"pilot.aero\0"
-"tn.us\0"
-"priv.hu\0"
-"li.it\0"
-"kr\xc3\xa5""anghke.no\0mosreg.ru\0"
-"lib.fl.us\0"
-"plants.museum\0"
-"ulsan.kr\0national.museum\0"
-"mil.ac\0!pref.nara.jp\0surgeonshall.museum\0"
-"mil.ae\0santacruz.museum\0vi.us\0"
-"wlocl.pl\0"
-"mt.it\0napoli.it\0alaska.museum\0arts.nf\0"
-"missoula.museum\0"
-"rec.ro\0"
-"mil.al\0"
-"marburg.museum\0waw.pl\0"
-"pharmaciens.km\0indianapolis.museum\0larsson.museum\0"
-"cc.sd.us\0"
-"mil.ba\0mobi\0"
-"indianmarket.museum\0"
-"recreation.aero\0padova.it\0"
-"varese.it\0parti.se\0"
-"mil.az\0"
-"mil.bo\0!pref.kagoshima.jp\0khmelnitskiy.ua\0"
-"rygge.no\0"
-"os\xc3\xb8yro.no\0"
-"mil.br\0"
-"cs.it\0"
-"austevoll.no\0fjell.no\0"
-"mil.by\0"
-"!pref.tokushima.jp\0org\0"
-"mil.cn\0gs.svalbard.no\0"
-"mil.co\0"
-"pz.it\0lib.va.us\0\xd1\x80\xd1\x84\0"
-"\xe4\xb8\xaa\xe4\xba\xba.hk\0ms.us\0nc.us\0k12.wi.us\0"
-"s.bg\0drangedal.no\0"
-"en.it\0"
-"culturalcenter.museum\0"
-"house.museum\0divttasvuotna.no\0"
-"fhs.no\0"
-"circus.museum\0"
-"priv.at\0"
-"mil.ec\0"
-"ruovat.no\0"
-"midsund.no\0vagan.no\0"
-"casadelamoneda.museum\0"
-"bristol.museum\0"
-"and.museum\0"
-"ascolipiceno.it\0computerhistory.museum\0vyatka.ru\0"
-"uhren.museum\0"
-"lahppi.no\0"
-"*.yokohama.jp\0cody.museum\0lib.al.us\0"
-"colonialwilliamsburg.museum\0indian.museum\0cc.ky.us\0"
-"tp.it\0biev\xc3\xa1t.no\0"
-"can.br\0royken.no\0"
-"id.ir\0"
-"mediocampidano.it\0tromso.no\0"
-"kartuzy.pl\0k12.ok.us\0"
-"*.saitama.jp\0stjohn.museum\0m\xc3\xa1tta-v\xc3\xa1rjjat.no\0"
-"mil.ge\0trani-barletta-andria.it\0"
-"lib.as.us\0"
-"swiebodzin.pl\0cc.mt.us\0cc.nd.us\0"
-"mil.gh\0"
-"science-fiction.museum\0\xd9\x82\xd8\xb7\xd8\xb1\0"
-"airtraffic.aero\0"
-"konskowola.pl\0"
-"scienceandhistory.museum\0nysa.pl\0sd.us\0"
-"balestrand.no\0"
-"oygarden.no\0"
-"her\xc3\xb8y.nordland.no\0"
-"!pref.ishikawa.jp\0strand.no\0"
-"\xe7\xb5\x84\xe7\xbb\x87.hk\0mil.hn\0"
-"gob.bo\0volda.no\0"
-"losangeles.museum\0larvik.no\0"
-"university.museum\0"
-"cc.dc.us\0"
-"mil.id\0"
-"sorfold.no\0"
-"watch-and-clock.museum\0"
-"flor\xc3\xb8.no\0"
-"nittedal.no\0oppeg\xc3\xa5rd.no\0"
-"k12.ri.us\0"
-"gob.cl\0"
-"komi.ru\0"
-"government.aero\0mil.in\0"
-"mil.iq\0id.lv\0"
-"culture.museum\0"
-"id.ly\0"
-"raholt.no\0"
-"lubin.pl\0grozny.ru\0"
-"kchr.ru\0"
-"nikolaev.ua\0"
-"lib.sd.us\0"
-"de.com\0"
-"mil.jo\0"
-"*.kanagawa.jp\0gaular.no\0miasta.pl\0"
-"bi.it\0rnu.tn\0uzhgorod.ua\0"
-"idrett.no\0v\xc3\xa5gs\xc3\xb8y.no\0"
-"wroclaw.pl\0"
-"res.aero\0ne.jp\0mil.kg\0"
-"\xc3\xa5mli.no\0"
-"education.museum\0"
-"dgca.aero\0"
-"mil.km\0"
-"trolley.museum\0"
-"cci.fr\0r.se\0"
-"archaeological.museum\0"
-"monzaedellabrianza.it\0mil.kr\0"
-"gob.es\0kvafjord.no\0ky.us\0"
-"lecco.it\0"
-"ct.it\0"
-"magazine.aero\0"
-"operaunite.com\0ne.kr\0"
-"mil.kz\0skoczow.pl\0"
-"nf.ca\0"
-"western.museum\0"
-"kunst.museum\0gaivuotna.no\0karpacz.pl\0spb.ru\0cc.id.us\0"
-"slask.pl\0"
-"youth.museum\0"
-"adv.br\0campidanomedio.it\0!songfest.om\0"
-"geelvinck.museum\0\xd8\xa7\xd9\x85\xd8\xa7\xd8\xb1\xd8\xa7\xd8\xaa\0"
-"mil.lv\0"
-"fie.ee\0mil.mg\0mt.us\0nd.us\0k12.vt.us\0"
-"t.bg\0ushuaia.museum\0"
-"off.ai\0"
-"irkutsk.ru\0"
-"stor-elvdal.no\0tourism.tn\0"
-"penza.ru\0"
-"bj.cn\0\xe4\xb8\xad\xe5\x9b\xbd\0"
-"civilwar.museum\0mil.mv\0opole.pl\0"
-"nes.akershus.no\0"
-"mil.my\0karelia.ru\0"
-"como.it\0sande.vestfold.no\0"
-"\xe4\xb8\xad\xe5\x9c\x8b\0"
-"gob.hn\0lib.la.us\0"
-"mil.no\0cc.wv.us\0"
-"boleslawiec.pl\0"
-"!pref.niigata.jp\0gs.sf.no\0dc.us\0k12.mi.us\0"
-"museum\0dep.no\0kv\xc3\xa6nangen.no\0l\xc3\xa1hppi.no\0"
-"film.museum\0"
-"frei.no\0"
-"notodden.no\0risor.no\0"
-"messina.it\0"
-"eidsberg.no\0"
-"krakow.pl\0lib.mt.us\0lib.nd.us\0"
-"rauma.no\0"
-"mulhouse.museum\0"
-"sibenik.museum\0grong.no\0mil.pe\0"
-"budejju.no\0k12.nv.us\0"
-"stavanger.no\0mil.ph\0"
-"forli-cesena.it\0"
-"naples.it\0cc.ne.us\0"
-"s\xc3\xb8r-aurdal.no\0"
-"mil.pl\0"
-"vibo-valentia.it\0ski.museum\0siedlce.pl\0"
-"bus.museum\0"
-"tozsde.hu\0"
-"!pref.shizuoka.jp\0santabarbara.museum\0"
-"zhitomir.ua\0"
-"pro.az\0"
-"ne.pw\0"
-"pro.br\0orkanger.no\0b\xc3\xb8.telemark.no\0"
-"roma.it\0cc.ct.us\0"
-"heritage.museum\0giske.no\0"
-"!pref.kumamoto.jp\0prof.pr\0"
-"*.kochi.jp\0"
-"andria-barletta-trani.it\0*.toyama.jp\0sveio.no\0"
-"id.us\0"
-"bolt.hu\0"
-"fetsund.no\0porsgrunn.no\0"
-"iglesias-carbonia.it\0"
-"sf.no\0"
-"mil.ru\0"
-"from.hr\0asnes.no\0mil.rw\0"
-"alesund.no\0sos.pl\0"
-"livorno.it\0"
-"crafts.museum\0"
-"aquila.it\0"
-"vega.no\0"
-"jewelry.museum\0"
-"sk\xc3\xa1nit.no\0chita.ru\0"
-"pro.ec\0"
-"fortmissoula.museum\0j\xc3\xb8lster.no\0"
-"pro\0mil.st\0"
-"busan.kr\0lib.ga.us\0"
-"dellogliastra.it\0"
-"aosta.it\0chungnam.kr\0gob.mx\0"
-"mil.sy\0k12.hi.us\0"
-"mil.tj\0"
-"ulan-ude.ru\0mil.to\0wv.us\0"
-"luster.no\0volgograd.ru\0"
-"pa.it\0kommunalforbund.se\0lib.tx.us\0"
-"s.se\0"
-"qsl.br\0"
-"mil.tw\0"
-"est.pr\0ens.tn\0"
-"lib.id.us\0"
-"mil.tz\0"
-"uscountryestate.museum\0"
-"agents.aero\0"
-"\xc3\xb8vre-eiker.no\0ne.ug\0"
-"pb.ao\0"
-"gob.pa\0ne.tz\0"
-"tur.br\0"
-"mil.vc\0"
-"or.at\0gob.pe\0"
-"s\xc3\xb8r-fron.no\0"
-"or.bi\0ne.us\0"
-"u.bg\0gob.pk\0"
-"stavern.no\0"
-"brindisi.it\0"
-"aknoluokta.no\0"
-"!pref.kyoto.jp\0tydal.no\0"
-"plc.ly\0muos\xc3\xa1t.no\0"
-"or.ci\0hamaroy.no\0priv.pl\0"
-"vestre-slidre.no\0gniezno.pl\0"
-"\xe7\xae\x87\xe4\xba\xba.hk\0"
-"andebu.no\0"
-"nieruchomosci.pl\0\xd8\xa7\xd9\x84\xd8\xb3\xd8\xb9\xd9\x88\xd8\xaf\xd9\x8a\xd8\xa9\0"
-"or.cr\0pro.ht\0bolzano.it\0"
-"ct.us\0k12.md.us\0"
-"za.org\0"
-"!icnet.uk\0"
-"localhistory.museum\0"
-"firm.ht\0"
-"lel.br\0tr.it\0kvanangen.no\0"
-"sondre-land.no\0t\xc3\xb8nsberg.no\0vefsn.no\0"
-"nature.museum\0yamal.ru\0"
-"rv.ua\0"
-"lans.museum\0lib.ne.us\0"
-"lur\xc3\xb8y.no\0"
-"eu.com\0firm.in\0"
-"hjelmeland.no\0"
-"gs.tr.no\0"
-"casino.hu\0essex.museum\0tourism.pl\0"
-"rennesoy.no\0"
-"priv.no\0"
-"baths.museum\0mytis.ru\0"
-"tingvoll.no\0"
-"cc.az.us\0"
-"sh.cn\0"
-"!pref.miyazaki.jp\0s\xc3\xb8rfold.no\0"
-"aurskog-holand.no\0malatvuopmi.no\0"
-"lib.ct.us\0"
-"cc.pa.us\0"
-"pa.gov.pl\0"
-"firm.co\0cc.de.us\0"
-"nrw.museum\0"
-"daejeon.kr\0livinghistory.museum\0"
-"gildeskal.no\0lund.no\0"
-"\xc3\xb8ksnes.no\0stavropol.ru\0"
-"b\xc3\xa6rum.no\0r\xc3\xb8yrvik.no\0"
-"osoyro.no\0"
-"priv.me\0sula.no\0!parliament.uk\0"
-"nationalheritage.museum\0"
-"jaworzno.pl\0"
-"dinosaur.museum\0"
-"garden.museum\0trust.museum\0"
-"turen.tn\0"
-"kautokeino.no\0"
-"pro.na\0"
-"gorizia.it\0"
-"siljan.no\0"
-"or.id\0pro.mv\0"
-"bieszczady.pl\0www.ro\0"
-"lib.ee\0antiques.museum\0brasil.museum\0tr.no\0"
-"aejrie.no\0"
-"!pref.hokkaido.jp\0"
-"schlesisches.museum\0"
-"huissier-justice.fr\0or.it\0"
-"t.se\0"
-"environment.museum\0"
-"vindafjord.no\0"
-"edu.ac\0or.jp\0"
-"tree.museum\0"
-"groundhandling.aero\0edu.af\0"
-"rochester.museum\0sanfrancisco.museum\0"
-"ebiz.tw\0"
-"kirovograd.ua\0"
-"edu.al\0"
-"edu.an\0\xc3\xa1k\xc5\x8boluokta.no\0v\xc3\xa5g\xc3\xa5.no\0"
-"v.bg\0"
-"edu.ba\0"
-"edu.bb\0nesset.no\0"
-"hornindal.no\0pro.pr\0"
-"or.kr\0"
-"az.us\0"
-"edu.bh\0volkenkunde.museum\0"
-"edu.bi\0"
-"edu.az\0"
-"b\xc3\xb8mlo.no\0"
-"edu.bm\0"
-"edu.bo\0tyumen.ru\0"
-"edu.br\0"
-"edu.bs\0pa.us\0"
-"alto-adige.it\0whaling.museum\0"
-"*.iwate.jp\0"
-"edu.ci\0law.pro\0"
-"edu.bz\0de.us\0"
-"lib.ak.us\0"
-"edu.cn\0"
-"edu.co\0"
-"laspezia.it\0"
-"baidar.no\0"
-"ts.it\0"
-"or.na\0"
-"edu.cu\0hotel.lk\0"
-"show.aero\0or.mu\0"
-"sandnes.no\0"
-"museumcenter.museum\0"
-"edu.dm\0kazan.ru\0"
-"biz\0caltanissetta.it\0odessa.ua\0k12.oh.us\0"
-"crimea.ua\0"
-"research.aero\0lom.no\0"
-"edu.ec\0florence.it\0clock.museum\0sshn.se\0"
-"edu.ee\0game.tw\0"
-"!pref.okinawa.jp\0"
-"ilawa.pl\0"
-"edu.dz\0indiana.museum\0"
-"gs.jan-mayen.no\0"
-"publ.pt\0"
-"nom.ad\0"
-"skanit.no\0gdansk.pl\0k12.pa.us\0"
-"nom.ag\0edu.es\0"
-"if.ua\0"
-"pro.tt\0lib.de.us\0"
-"environmentalconservation.museum\0cc.or.us\0"
-"bern.museum\0nat.tn\0"
-"rubtsovsk.ru\0"
-"!educ.ar\0masoy.no\0"
-"bologna.it\0"
-"\xc3\xa5snes.no\0fhv.se\0"
-"*.tottori.jp\0radoy.no\0"
-"romskog.no\0"
-"malbork.pl\0"
-"olbiatempio.it\0"
-"edu.ge\0"
-"edu.gh\0"
-"edu.gi\0"
-"or.pw\0"
-"hob\xc3\xb8l.no\0"
-"nom.br\0edu.gn\0virginia.museum\0mbone.pl\0!nls.uk\0"
-"seljord.no\0pro.vn\0"
-"edu.gp\0"
-"edu.gr\0"
-"!uba.ar\0!pref.saitama.jp\0"
-"greta.fr\0gs.aa.no\0kvinnherad.no\0"
-"lib.sc.us\0"
-"js.cn\0nom.co\0edu.hk\0"
-"lesja.no\0"
-"bl.it\0"
-"edu.hn\0\xc3\xb8ystre-slidre.no\0mari-el.ru\0"
-"hotel.hu\0"
-"rindal.no\0"
-"edu.ht\0"
-"!pref.miyagi.jp\0"
-"midtre-gauldal.no\0"
-"xj.cn\0australia.museum\0"
-"ab.ca\0salvadordali.museum\0olawa.pl\0"
-"pc.it\0"
-"u.se\0"
-"edu.in\0b\xc3\xa1l\xc3\xa1t.no\0"
-"ln.cn\0alta.no\0"
-"chelyabinsk.ru\0"
-"edu.iq\0"
-"ontario.museum\0"
-"edu.is\0"
-"edu.it\0"
-"b\xc3\xa5tsfjord.no\0"
-"trysil.no\0or.th\0"
-"utsira.no\0"
-"nom.es\0edu.jo\0fhsk.se\0"
-"bale.museum\0"
-"w.bg\0"
-"lillesand.no\0"
-"edu.kg\0"
-"amusement.aero\0"
-"edu.ki\0"
-"fauske.no\0or.ug\0"
-"int.az\0askvoll.no\0eidskog.no\0cv.ua\0"
-"algard.no\0"
-"edu.km\0or.tz\0"
-"nom.fr\0edu.kn\0"
-"*.ibaraki.jp\0hoylandet.no\0"
-"int.bo\0edu.kp\0"
-"edu.la\0"
-"si.it\0edu.lb\0travel.pl\0"
-"edu.lc\0mx.na\0n\xc3\xa1vuotna.no\0ovre-eiker.no\0"
-"aa.no\0!siemens.om\0"
-"sciences.museum\0or.us\0"
-"cat\0"
-"edu.ky\0"
-"int.ci\0edu.kz\0firm.ro\0cc.wy.us\0"
-"edu.lk\0vaapste.no\0"
-"!pref.tochigi.jp\0"
-"int.co\0podlasie.pl\0"
-"edu.lr\0"
-"karikatur.museum\0jamal.ru\0"
-"gjovik.no\0krager\xc3\xb8.no\0k12.az.us\0"
-"edu.me\0"
-"ud.it\0edu.lv\0entomology.museum\0"
-"edu.mg\0moskenes.no\0"
-"\xe6\x94\xbf\xe5\xba\x9c.hk\0edu.ly\0"
-"stpetersburg.museum\0"
-"edu.mk\0"
-"edu.ml\0nordreisa.no\0"
-"!pref.fukui.jp\0lib.ms.us\0lib.nc.us\0"
-"edu.mn\0\xd9\x81\xd9\x84\xd8\xb3\xd8\xb7\xd9\x8a\xd9\x86\0"
-"fot.br\0edu.mo\0"
-"iron.museum\0"
-"asti.it\0annefrank.museum\0stv.ru\0cc.nh.us\0"
-"edu.mv\0"
-"lodi.it\0edu.mw\0edu.ng\0"
-"gwangju.kr\0edu.mx\0"
-"edu.my\0"
-"soundandvision.museum\0"
-"lenvik.no\0"
-"ballooning.aero\0"
-"name\0"
-"jogasz.hu\0frogn.no\0"
-"history.museum\0"
-"consultant.aero\0edu.nr\0"
-"manchester.museum\0"
-"*.hiroshima.jp\0"
-"pol.dz\0"
-"*.tochigi.jp\0heimatunduhren.museum\0"
-"!pref.kanagawa.jp\0"
-"firm.nf\0edu.pa\0"
-"coop.ht\0pc.pl\0"
-"chicago.museum\0"
-"vn.ua\0"
-"edu.pe\0"
-"tana.no\0edu.pf\0"
-"edu.ph\0"
-"nom.km\0"
-"travel.tt\0"
-"edu.pk\0"
-"experts-comptables.fr\0edu.pl\0bryansk.ru\0"
-"edu.pn\0"
-"evje-og-hornnes.no\0warszawa.pl\0"
-"ac.ae\0"
-"edu.pr\0"
-"vaksdal.no\0edu.ps\0dni.us\0"
-"po.gov.pl\0edu.pt\0"
-"nordre-land.no\0vadso.no\0"
-"rnrt.tn\0"
-"sport.hu\0!pref.gifu.jp\0voss.no\0targi.pl\0"
-"flesberg.no\0"
-"photography.museum\0"
-"modena.it\0tonsberg.no\0"
-"ac.at\0"
-"ac.be\0coop.br\0"
-"services.aero\0"
-"nom.mg\0"
-"wielun.pl\0"
-"jefferson.museum\0wy.us\0"
-"pd.it\0ot.it\0neues.museum\0slattum.no\0"
-"vdonsk.ru\0"
-"ar.com\0edu.sa\0"
-"\xc3\xa5l.no\0edu.sb\0"
-"edu.rs\0edu.sc\0"
-"ac.ci\0int.is\0edu.sd\0!tsk.tr\0"
-"br\xc3\xb8nn\xc3\xb8ysund.no\0and\xc3\xb8y.no\0edu.ru\0"
-"pol.ht\0"
-"edu.rw\0edu.sg\0"
-"gyeongnam.kr\0olecko.pl\0"
-"ac.cn\0"
-"graz.museum\0"
-"coldwar.museum\0edu.sl\0"
-"ac.cr\0"
-"edu.sn\0"
-"hamar.no\0"
-"histoire.museum\0"
-"!city.shizuoka.jp\0"
-"edu.st\0"
-"oceanographic.museum\0nh.us\0"
-"x.bg\0"
-"surnadal.no\0"
-"fc.it\0costume.museum\0stalowa-wola.pl\0"
-"valer.ostfold.no\0edu.sy\0"
-"edu.tj\0"
-"arq.br\0"
-"aeroclub.aero\0odo.br\0pe.ca\0\xe7\xb6\xb2\xe7\xb5\xa1.cn\0bronnoysund.no\0nom.pa\0"
-"edu.to\0"
-"paleo.museum\0nom.pe\0edu.ua\0"
-"int.la\0trustee.museum\0forsand.no\0krasnoyarsk.ru\0"
-"!pref.hyogo.jp\0"
-"edu.tt\0"
-"zarow.pl\0"
-"edu.tw\0"
-"nom.pl\0"
-"community.museum\0kvitsoy.no\0"
-"int.lk\0tychy.pl\0"
-"k12.me.us\0"
-"jondal.no\0edu.vc\0"
-"illustration.museum\0"
-"clinton.museum\0"
-"tas.au\0es.kr\0"
-"production.aero\0"
-"rodoy.no\0"
-"database.museum\0bodo.no\0"
-"anthro.museum\0landes.museum\0edu.vn\0"
-"nom.re\0"
-"altai.ru\0"
-"filatelia.museum\0"
-"sk.ca\0lezajsk.pl\0"
-"rockart.museum\0int.mv\0"
-"int.mw\0herad.no\0"
-"eti.br\0ac.gn\0"
-"fedje.no\0nom.ro\0"
-"money.museum\0"
-"\xd9\x85\xd8\xb5\xd8\xb1\0"
-"horten.no\0"
-"gangaviika.no\0mielec.pl\0"
-"uw.gov.pl\0"
-"moma.museum\0"
-"edu.ws\0"
-"go.ci\0"
-"tv.bo\0technology.museum\0"
-"s\xc3\xb8ndre-land.no\0"
-"tv.br\0"
-"jor.br\0lib.dc.us\0"
-"arboretum.museum\0"
-"go.cr\0"
-"artsandcrafts.museum\0\xd8\xaa\xd9\x88\xd9\x86\xd8\xb3\0"
-"psc.br\0ac.id\0!city.chiba.jp\0"
-"wa.au\0"
-"rome.it\0"
-"amli.no\0"
-"ac.im\0lo.it\0"
-"ac.in\0"
-"\xe7\xb6\xb2\xe7\xb5\xa1.hk\0durham.museum\0"
-"ac.ir\0"
-"torino.museum\0"
-"loabat.no\0"
-"com\0"
-"nalchik.ru\0"
-"yakutia.ru\0"
-"settlers.museum\0"
-"!promocion.ar\0int.pt\0"
-"union.aero\0"
-"utah.museum\0"
-"giehtavuoatna.no\0"
-"ac.jp\0"
-"air-traffic-control.aero\0"
-"silk.museum\0usantiques.museum\0"
-"bn.it\0"
-"kalisz.pl\0"
-"perm.ru\0"
-"aoste.it\0bindal.no\0"
-"coloradoplateau.museum\0k12.gu.us\0"
-"frosinone.it\0forde.no\0"
-"epilepsy.museum\0"
-"olbia-tempio.it\0"
-"journalist.aero\0ac.kr\0*.sch.uk\0"
-"nic.im\0sciencesnaturelles.museum\0bedzin.pl\0"
-"nic.in\0pe.it\0"
-"w.se\0"
-"!pref.okayama.jp\0"
-"urn.arpa\0"
-"cinema.museum\0"
-"monza.it\0versailles.museum\0int.ru\0"
-"andasuolo.no\0skj\xc3\xa5k.no\0chernovtsy.ua\0"
-"nyc.museum\0int.rw\0paroch.k12.ma.us\0"
-"ringerike.no\0"
-"ac.ma\0"
-"org.ac\0civilaviation.aero\0"
-"rakkestad.no\0"
-"org.ae\0ac.me\0"
-"org.af\0"
-"org.ag\0"
-"org.ai\0stokke.no\0"
-"airport.aero\0"
-"finnoy.no\0"
-"org.al\0"
-"org.an\0y.bg\0habmer.no\0"
-"stadt.museum\0holtalen.no\0"
-"int.tj\0"
-"org.ba\0gjerdrum.no\0"
-"org.bb\0ascoli-piceno.it\0molde.no\0r\xc3\xb8st.no\0tysfjord.no\0"
-"pe.kr\0rybnik.pl\0"
-"go.id\0"
-"ac.mu\0"
-"ac.mw\0ac.ng\0"
-"org.bh\0\xc3\xa5mot.no\0rana.no\0"
-"org.bi\0"
-"org.az\0belgorod.ru\0int.tt\0"
-"ae.org\0"
-"group.aero\0posts-and-telecommunications.museum\0"
-"org.bm\0salerno.it\0"
-"etnedal.no\0"
-"org.bo\0*.hokkaido.jp\0donetsk.ua\0"
-"ostroda.pl\0"
-"org.br\0"
-"org.bs\0"
-"go.it\0h\xc3\xb8ylandet.no\0"
-"zgorzelec.pl\0"
-"org.bw\0"
-"org.ci\0"
-"org.bz\0vicenza.it\0resistance.museum\0"
-"missile.museum\0"
-"org.cn\0"
-"org.co\0assassination.museum\0"
-"go.jp\0"
-"tv.it\0austrheim.no\0ac.pa\0"
-"verbania.it\0"
-"palace.museum\0"
-"tmp.br\0int.vn\0"
-"org.cu\0"
-"paris.museum\0"
-"media.aero\0hokksund.no\0"
-"arts.museum\0gemological.museum\0hammerfest.no\0"
-"k12.ny.us\0"
-"org.dm\0hemsedal.no\0ringsaker.no\0sklep.pl\0"
-"h\xc3\xa5.no\0cc.nj.us\0"
-"rzeszow.pl\0"
-"go.kr\0gjesdal.no\0ac.pr\0"
-"org.ec\0"
-"org.ee\0"
-"media.museum\0"
-"terni.it\0touch.museum\0zakopane.pl\0"
-"journal.aero\0org.dz\0"
-"incheon.kr\0"
-"b\xc3\xa1hcavuotna.no\0"
-"leksvik.no\0ulvik.no\0"
-"plantation.museum\0"
-"org.es\0loyalist.museum\0"
-"gildesk\xc3\xa5l.no\0bytom.pl\0"
-"bo.nordland.no\0"
-"ambulance.aero\0iglesiascarbonia.it\0"
-"tw.cn\0\xe6\x96\xb0\xe5\x8a\xa0\xe5\x9d\xa1\0"
-"chocolate.museum\0"
-"pittsburgh.museum\0"
-"royrvik.no\0sor-odal.no\0ac.rs\0"
-"kaluga.ru\0"
-"org.ge\0erotica.hu\0ac.ru\0ac.se\0"
-"org.gg\0leangaviika.no\0ac.rw\0"
-"org.gh\0v\xc3\xa6r\xc3\xb8y.no\0"
-"org.gi\0"
-"jevnaker.no\0"
-"org.gn\0tv.na\0leikanger.no\0"
-"org.gp\0"
-"ask\xc3\xb8y.no\0"
-"org.gr\0wroc.pl\0"
-"ad.jp\0"
-"powiat.pl\0"
-"tj\xc3\xb8me.no\0"
-"coop.tt\0"
-"ac.th\0"
-"mragowo.pl\0ac.sz\0ac.tj\0"
-"org.hk\0bo.it\0"
-"philately.museum\0"
-"org.hn\0"
-"fet.no\0"
-"axis.museum\0mansions.museum\0"
-"wiki.br\0"
-"org.ht\0"
-"org.hu\0piacenza.it\0scotland.museum\0cpa.pro\0"
-"ac.ug\0"
-"coop.mv\0x.se\0"
-"coop.mw\0ac.tz\0"
-"bmd.br\0"
-"org.im\0ralingen.no\0"
-"org.in\0"
-"cz.it\0lib.ia.us\0"
-"org.iq\0"
-"org.ir\0"
-"org.is\0"
-"nl.ca\0"
-"org.je\0"
-"childrensgarden.museum\0"
-"kvits\xc3\xb8y.no\0go.pw\0"
-"sokndal.no\0"
-"ra.it\0grimstad.no\0"
-"denmark.museum\0"
-"ac.vn\0"
-"ecn.br\0org.jo\0"
-"bialystok.pl\0nj.us\0"
-"z.bg\0bilbao.museum\0stargard.pl\0nic.tj\0"
-"eisenbahn.museum\0"
-"fe.it\0bryne.no\0vrn.ru\0"
-"cc.wa.us\0"
-"sex.hu\0skierva.no\0"
-"org.kg\0"
-"org.ki\0"
-"org.km\0"
-"org.kn\0khakassia.ru\0"
-"org.kp\0"
-"org.la\0"
-"org.lb\0"
-"org.lc\0"
-"francaise.museum\0"
-"panama.museum\0"
-"rotorcraft.aero\0gateway.museum\0olkusz.pl\0"
-"org.ky\0czeladz.pl\0ryazan.ru\0"
-"org.kz\0"
-"org.lk\0dyr\xc3\xb8y.no\0"
-"raisa.no\0"
-"dlugoleka.pl\0"
-"org.ma\0"
-"org.lr\0prochowice.pl\0"
-"org.ls\0"
-"org.me\0sandoy.no\0s\xc3\xb8r-varanger.no\0"
-"org.lv\0"
-"org.mg\0"
-"tel\0go.th\0"
-"org.ly\0"
-"steam.museum\0go.tj\0"
-"org.mk\0pasadena.museum\0jessheim.no\0lib.mn.us\0"
-"org.ml\0"
-"software.aero\0"
-"org.mn\0"
-"org.mo\0"
-"*.fukui.jp\0decorativearts.museum\0"
-"spy.museum\0org.na\0jorpeland.no\0"
-"vads\xc3\xb8.no\0"
-"org.mu\0building.museum\0gausdal.no\0"
-"org.mv\0nannestad.no\0"
-"org.mw\0org.ng\0go.ug\0"
-"vr.it\0org.mx\0"
-"org.my\0"
-"go.tz\0"
-"oppdal.no\0"
-"uk.net\0"
-"coop.km\0"
-"*.kyoto.jp\0"
-"sarpsborg.no\0org.nr\0"
-"chernigov.ua\0"
-"ha.cn\0no.com\0"
-"space.museum\0"
-"org.pa\0"
-"*.ar\0"
-"usgarden.museum\0"
-"*.bd\0org.pe\0"
-"*.au\0org.pf\0um.gov.pl\0"
-"bio.br\0"
-"org.ph\0"
-"org.pk\0"
-"fr\xc3\xa6na.no\0org.pl\0"
-"nord-aurdal.no\0org.pn\0"
-"*.bn\0handson.museum\0agrinet.tn\0"
-"kviteseid.no\0"
-"rel.ht\0virtuel.museum\0atm.pl\0org.pr\0"
-"org.ps\0cherkassy.ua\0"
-"org.pt\0wa.us\0"
-"*.bt\0arendal.no\0magnitka.ru\0"
-"depot.museum\0porsangu.no\0"
-"laakesvuemie.no\0"
-"sor-fron.no\0"
-"heroy.more-og-romsdal.no\0"
-"*.ck\0"
-"!rakpetroleum.om\0"
-"kr\xc3\xb8""dsherad.no\0mail.pl\0"
-"mod.gi\0"
-"gs.nl.no\0"
-"mb.ca\0"
-"pavia.it\0"
-"civilisation.museum\0folldal.no\0"
-"suli.hu\0"
-"brumunddal.no\0"
-"*.cy\0"
-"pg.it\0troms\xc3\xb8.no\0"
-"sex.pl\0y.se\0"
-"org.ro\0"
-"*.do\0"
-"caserta.it\0org.sa\0"
-"za.com\0halloffame.museum\0org.sb\0lviv.ua\0"
-"mill.museum\0org.rs\0org.sc\0"
-"org.sd\0"
-"idv.hk\0!omanmobile.om\0org.ru\0org.se\0"
-"langev\xc3\xa5g.no\0r\xc3\xa5holt.no\0starostwo.gov.pl\0"
-"trani-andria-barletta.it\0org.sg\0"
-"*.eg\0hvaler.no\0"
-"*.ehime.jp\0"
-"gmina.pl\0"
-"bod\xc3\xb8.no\0org.sl\0"
-"edu\0org.sn\0"
-"org.so\0lib.wi.us\0"
-"kommune.no\0"
-"nome.pt\0"
-"*.er\0namdalseid.no\0k12.wa.us\0"
-"nm.cn\0org.st\0"
-"*.et\0d\xc3\xb8nna.no\0"
-"jewish.museum\0preservation.museum\0"
-"slupsk.pl\0org.sy\0"
-"art.br\0org.sz\0org.tj\0"
-"ntr.br\0*.fj\0ski.no\0"
-"*.fk\0rimini.it\0grajewo.pl\0"
-"loppa.no\0"
-"franziskaner.museum\0notteroy.no\0org.tn\0"
-"org.to\0"
-"nesoddtangen.no\0"
-"org.ua\0"
-"discovery.museum\0wloclawek.pl\0"
-"lakas.hu\0org.tt\0"
-"kurgan.ru\0"
-"baltimore.museum\0nkz.ru\0org.tw\0"
-"com.ac\0castle.museum\0"
-"*.fukuoka.jp\0sandefjord.no\0varggat.no\0"
-"com.af\0"
-"com.ag\0"
-"ato.br\0k12.nj.us\0"
-"com.ai\0"
-"city.hu\0oryol.ru\0"
-"com.al\0nl.no\0mielno.pl\0cc.ma.us\0"
-"org.vc\0"
-"com.an\0g12.br\0"
-"*.gt\0"
-"*.gu\0"
-"com.ba\0"
-"com.bb\0americanart.museum\0"
-"org.vi\0"
-"kunstsammlung.museum\0"
-"com.aw\0"
-"flight.aero\0com.bh\0lib.mo.us\0org.vn\0"
-"com.bi\0adygeya.ru\0"
-"com.az\0"
-"art.dz\0"
-"com.bm\0"
-"dr\xc3\xb8""bak.no\0"
-"com.bo\0isla.pr\0"
-"com.br\0"
-"com.bs\0ustka.pl\0kuban.ru\0"
-"press.aero\0"
-"vs.it\0"
-"meloy.no\0"
-"*.il\0ulm.museum\0"
-"com.by\0com.ci\0genoa.it\0"
-"com.bz\0sn.cn\0"
-"lib.or.us\0"
-"santafe.museum\0org.ws\0"
-};
-
-QT_END_NAMESPACE
-
-#endif // QNETWORKCOOKIEJARTLD_P_H
diff --git a/src/network/access/qnetworkcookiejartlds_p.h.INFO b/src/network/access/qnetworkcookiejartlds_p.h.INFO
deleted file mode 100644
index 57a8d0e..0000000
--- a/src/network/access/qnetworkcookiejartlds_p.h.INFO
+++ /dev/null
@@ -1,17 +0,0 @@
-The file qnetworkcookiejartlds_p.h is generated from the Public Suffix
-List (see [1] and [2]), by the program residing at
-util/network/cookiejar-generateTLDs in the Qt source tree.
-
-That program generates a character array and an index array from the
-list to provide fast lookups of elements within C++.
-
-Those arrays in qnetworkcookiejartlds_p.h are derived from the Public
-Suffix List ([2]), which was originally provided by
-Jo Hermans <jo.hermans@gmail.com>.
-
-The file qnetworkcookiejartlds_p.h was last generated Friday,
-November 19th 15:24 2010.
-
-----
-[1] list: http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1
-[2] homepage: http://publicsuffix.org/
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index 3b18fe8..1c515c2 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -50,13 +50,15 @@
#include <qdir.h>
#include <qdatetime.h>
#include <qdiriterator.h>
-#include <qcryptographichash.h>
#include <qurl.h>
-
+#include <qcryptographichash.h>
#include <qdebug.h>
-#define CACHE_PREFIX QLatin1String("cache_")
-#define CACHE_POSTFIX QLatin1String(".cache")
+#define CACHE_POSTFIX QLatin1String(".d")
+#define PREPARED_SLASH QLatin1String("prepared/")
+#define CACHE_VERSION 7
+#define DATA_DIR QLatin1String("data")
+
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
#ifndef QT_NO_NETWORKDISKCACHE
@@ -153,6 +155,9 @@ void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
d->cacheDirectory = dir.absolutePath();
if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
d->cacheDirectory += QLatin1Char('/');
+
+ d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/');
+ d->prepareLayout();
}
/*!
@@ -244,6 +249,26 @@ void QNetworkDiskCache::insert(QIODevice *device)
d->inserting.erase(it);
}
+
+/*!
+ Create subdirectories and other housekeeping on the filesystem.
+ Prevents too many files from being present in any single directory.
+*/
+void QNetworkDiskCachePrivate::prepareLayout()
+{
+ QDir helper;
+ helper.mkpath(cacheDirectory + PREPARED_SLASH);
+
+ //Create directory and subdirectories 0-F
+ helper.mkpath(dataDirectory);
+ for (uint i = 0; i < 16 ; i++) {
+ QString str = QString::number(i, 16);
+ QString subdir = dataDirectory + str;
+ helper.mkdir(subdir);
+ }
+}
+
+
void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
{
Q_Q(QNetworkDiskCache);
@@ -324,7 +349,7 @@ bool QNetworkDiskCachePrivate::removeFile(const QString &file)
return false;
QFileInfo info(file);
QString fileName = info.fileName();
- if (!fileName.endsWith(CACHE_POSTFIX) || !fileName.startsWith(CACHE_PREFIX))
+ if (!fileName.endsWith(CACHE_POSTFIX))
return false;
qint64 size = info.size();
if (QFile::remove(file)) {
@@ -404,7 +429,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) {
@@ -508,6 +533,9 @@ qint64 QNetworkDiskCache::expire()
return 0;
}
+ // close file handle to prevent "in use" error when QFile::remove() is called
+ d->lastItem.reset();
+
QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
@@ -517,7 +545,7 @@ qint64 QNetworkDiskCache::expire()
QString path = it.next();
QFileInfo info = it.fileInfo();
QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX) && fileName.startsWith(CACHE_PREFIX)) {
+ if (fileName.endsWith(CACHE_POSTFIX)) {
cacheItems.insert(info.created(), path);
totalSize += info.size();
}
@@ -544,8 +572,6 @@ qint64 QNetworkDiskCache::expire()
<< "Kept:" << cacheItems.count() - removedFiles;
}
#endif
- if (removedFiles > 0)
- d->lastItem.reset();
return totalSize;
}
@@ -564,7 +590,10 @@ void QNetworkDiskCache::clear()
d->maximumCacheSize = size;
}
-QByteArray QNetworkDiskCachePrivate::generateId(const QUrl &url) const
+/*!
+ Given a URL, generates a unique enough filename (and subdirectory)
+ */
+QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
{
QUrl cleanUrl = url;
cleanUrl.setPassword(QString());
@@ -572,29 +601,32 @@ QByteArray QNetworkDiskCachePrivate::generateId(const QUrl &url) const
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(cleanUrl.toEncoded());
- return hash.result().toHex();
+ // convert sha1 to base36 form and return first 8 bytes for use as string
+ QByteArray id = QByteArray::number(*(qlonglong*)hash.result().data(), 36).left(8);
+ // generates <one-char subdir>/<8-char filname.d>
+ uint code = (uint)id.at(id.length()-1) % 16;
+ QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
+ + QLatin1String(id) + CACHE_POSTFIX;
+
+ return pathFragment;
}
QString QNetworkDiskCachePrivate::tmpCacheFileName() const
{
- QDir dir;
- dir.mkpath(cacheDirectory + QLatin1String("prepared/"));
- return cacheDirectory + QLatin1String("prepared/") + CACHE_PREFIX + QLatin1String("XXXXXX") + CACHE_POSTFIX;
+ //The subdirectory is presumed to be already read for use.
+ return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
}
+/*!
+ Generates fully qualified path of cached resource from a URL.
+ */
QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const
{
if (!url.isValid())
return QString();
- QString directory = cacheDirectory + url.scheme() + QLatin1Char('/');
- if (!QFile::exists(directory)) {
- // ### make a static QDir function for this...
- QDir dir;
- dir.mkpath(directory);
- }
- QString fileName = CACHE_PREFIX + QLatin1String(generateId(url)) + CACHE_POSTFIX;
- return directory + fileName;
+ QString fullpath = dataDirectory + uniqueFileName(url);
+ return fullpath;
}
/*!
@@ -631,7 +663,7 @@ bool QCacheItem::canCompress() const
enum
{
CacheMagic = 0xe8,
- CurrentCacheVersion = 7
+ CurrentCacheVersion = CACHE_VERSION
};
void QCacheItem::writeHeader(QFile *device) const
@@ -682,6 +714,12 @@ bool QCacheItem::read(QFile *device, bool readData)
data.setData(qUncompress(dataBA));
data.open(QBuffer::ReadOnly);
}
+
+ // quick and dirty check if metadata's URL field and the file's name are in synch
+ QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
+ if (!device->fileName().endsWith(expectedFilename))
+ return false;
+
return metaData.isValid();
}
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index e3a7698..ceb300d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -104,14 +104,17 @@ public:
, currentCacheSize(-1)
{}
- QByteArray generateId(const QUrl &url) const;
+ static QString uniqueFileName(const QUrl &url);
QString cacheFileName(const QUrl &url) const;
QString tmpCacheFileName() const;
bool removeFile(const QString &file);
void storeItem(QCacheItem *item);
+ void prepareLayout();
+ static quint32 crc32(const char *data, uint len);
mutable QCacheItem lastItem;
QString cacheDirectory;
+ QString dataDirectory;
qint64 maximumCacheSize;
qint64 currentCacheSize;
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index c74e92a..1ce7e8f 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -49,6 +49,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
: readBufferMaxSize(0),
operation(QNetworkAccessManager::UnknownOperation),
errorCode(QNetworkReply::NoError)
+ , isFinished(false)
{
// set the default attribute values
attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false);
@@ -462,7 +463,7 @@ QNetworkReply::NetworkError QNetworkReply::error() const
*/
bool QNetworkReply::isFinished() const
{
- return d_func()->isFinished();
+ return d_func()->isFinished;
}
/*!
@@ -718,6 +719,21 @@ void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
}
/*!
+ \since 4.8
+ Sets the reply as \a finished.
+
+ After having this set the replies data must not change.
+
+ \sa isFinished()
+*/
+void QNetworkReply::setFinished(bool finished)
+{
+ Q_D(QNetworkReply);
+ d->isFinished = finished;
+}
+
+
+/*!
Sets the URL being processed to be \a url. Normally, the URL
matches that of the request that was posted, but for a variety of
reasons it can be different (for example, a file path being made
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index b02fd4b..f8352a2 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -163,6 +163,7 @@ protected:
void setOperation(QNetworkAccessManager::Operation operation);
void setRequest(const QNetworkRequest &request);
void setError(NetworkError errorCode, const QString &errorString);
+ void setFinished(bool);
void setUrl(const QUrl &url);
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index a761d41..687e652 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -71,12 +71,11 @@ public:
qint64 readBufferMaxSize;
QNetworkAccessManager::Operation operation;
QNetworkReply::NetworkError errorCode;
+ bool isFinished;
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/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
new file mode 100644
index 0000000..0cd10ce
--- /dev/null
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplydataimpl_p.h"
+#include "private/qdataurl_p.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMetaObject>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkReplyDataImplPrivate::QNetworkReplyDataImplPrivate()
+ : QNetworkReplyPrivate()
+{
+}
+
+QNetworkReplyDataImplPrivate::~QNetworkReplyDataImplPrivate()
+{
+}
+
+QNetworkReplyDataImpl::~QNetworkReplyDataImpl()
+{
+}
+
+QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
+ : QNetworkReply(*new QNetworkReplyDataImplPrivate(), parent)
+{
+ Q_D(QNetworkReplyDataImpl);
+ setRequest(req);
+ setUrl(req.url());
+ setOperation(op);
+ setFinished(true);
+ QNetworkReply::open(QIODevice::ReadOnly);
+
+ QUrl url = req.url();
+
+ // FIXME qDecodeDataUrl should instead be rewritten to have the QByteArray
+ // and the mime type as an output parameter and return a bool instead
+ d->decodeDataUrlResult = qDecodeDataUrl(url);
+
+ if (! d->decodeDataUrlResult.first.isNull()) {
+ QString &mimeType = d->decodeDataUrlResult.first;
+ qint64 size = d->decodeDataUrlResult.second.size();
+ setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
+ setHeader(QNetworkRequest::ContentLengthHeader, size);
+ QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
+
+ d->decodedData.setBuffer(&d->decodeDataUrlResult.second);
+ d->decodedData.open(QIODevice::ReadOnly);
+
+ QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
+ Q_ARG(qint64,size), Q_ARG(qint64, size));
+ QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ } else {
+ // something wrong with this URI
+ const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
+ "Invalid URI: %1").arg(QString::fromLatin1(url.toEncoded()));
+ setError(QNetworkReply::ProtocolFailure, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolFailure));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ }
+}
+
+void QNetworkReplyDataImpl::close()
+{
+ QNetworkReply::close();
+}
+
+void QNetworkReplyDataImpl::abort()
+{
+ QNetworkReply::close();
+}
+
+qint64 QNetworkReplyDataImpl::bytesAvailable() const
+{
+ Q_D(const QNetworkReplyDataImpl);
+ return QNetworkReply::bytesAvailable() + d->decodedData.bytesAvailable();
+}
+
+bool QNetworkReplyDataImpl::isSequential () const
+{
+ return true;
+}
+
+qint64 QNetworkReplyDataImpl::size() const
+{
+ Q_D(const QNetworkReplyDataImpl);
+ return d->decodedData.size();
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReplyDataImpl::readData(char *data, qint64 maxlen)
+{
+ Q_D(QNetworkReplyDataImpl);
+
+ // TODO idea:
+ // Instead of decoding the whole data into new memory, we could decode on demand.
+ // Note that this might be tricky to do.
+
+ return d->decodedData.read(data, maxlen);
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qnetworkreplydataimpl_p.cpp"
+
diff --git a/src/network/access/qnetworkaccessdatabackend_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 57dfdef..a63c4b1 100644
--- a/src/network/access/qnetworkaccessdatabackend_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QNETWORKACCESSDATABACKEND_P_H
-#define QNETWORKACCESSDATABACKEND_P_H
+#ifndef QNETWORKREPLYDATAIMPL_H
+#define QNETWORKREPLYDATAIMPL_H
//
// W A R N I N G
@@ -53,32 +53,46 @@
// We mean it.
//
-#include "qnetworkaccessbackend_p.h"
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+#include <QBuffer>
QT_BEGIN_NAMESPACE
-class QNetworkAccessDataBackend: public QNetworkAccessBackend
+
+class QNetworkReplyDataImplPrivate;
+class QNetworkReplyDataImpl: public QNetworkReply
{
+ Q_OBJECT
public:
- QNetworkAccessDataBackend();
- virtual ~QNetworkAccessDataBackend();
+ QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ ~QNetworkReplyDataImpl();
+ virtual void abort();
+
+ // reimplemented from QNetworkReply
+ virtual void close();
+ virtual qint64 bytesAvailable() const;
+ virtual bool isSequential () const;
+ qint64 size() const;
- virtual void open();
- virtual void closeDownstreamChannel();
- virtual void closeUpstreamChannel();
- virtual bool waitForDownstreamReadyRead(int msecs);
- virtual bool waitForUpstreamBytesWritten(int msecs);
+ virtual qint64 readData(char *data, qint64 maxlen);
- virtual bool processRequestSynchronously();
+ Q_DECLARE_PRIVATE(QNetworkReplyDataImpl)
};
-class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory
+class QNetworkReplyDataImplPrivate: public QNetworkReplyPrivate
{
public:
- virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
- const QNetworkRequest &request) const;
+ QNetworkReplyDataImplPrivate();
+ ~QNetworkReplyDataImplPrivate();
+
+ QPair<QString, QByteArray> decodeDataUrlResult;
+ QBuffer decodedData;
+
+ Q_DECLARE_PUBLIC(QNetworkReplyDataImpl)
};
QT_END_NAMESPACE
-#endif
+#endif // QNETWORKREPLYDATAIMPL_H
diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index 37e3578..7a0c5c7 100644
--- a/src/network/access/qfilenetworkreply.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -39,7 +39,7 @@
**
****************************************************************************/
-#include "qfilenetworkreply_p.h"
+#include "qnetworkreplyfileimpl_p.h"
#include "QtCore/qdatetime.h"
#include <QtCore/QCoreApplication>
@@ -48,31 +48,25 @@
QT_BEGIN_NAMESPACE
-QFileNetworkReplyPrivate::QFileNetworkReplyPrivate()
- : QNetworkReplyPrivate(), fileEngine(0), fileSize(0), filePos(0)
+QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
+ : QNetworkReplyPrivate(), realFileSize(0)
{
}
-QFileNetworkReplyPrivate::~QFileNetworkReplyPrivate()
+QNetworkReplyFileImpl::~QNetworkReplyFileImpl()
{
- delete fileEngine;
}
-QFileNetworkReply::~QFileNetworkReply()
-{
-}
-
-QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
- : QNetworkReply(*new QFileNetworkReplyPrivate(), parent)
+QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
+ : QNetworkReply(*new QNetworkReplyFileImplPrivate(), parent)
{
setRequest(req);
setUrl(req.url());
setOperation(op);
+ setFinished(true);
QNetworkReply::open(QIODevice::ReadOnly);
- qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
-
- QFileNetworkReplyPrivate *d = (QFileNetworkReplyPrivate*) d_func();
+ QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
QUrl url = req.url();
if (url.host() == QLatin1String("localhost"))
@@ -113,15 +107,15 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
return;
}
- d->fileEngine = QAbstractFileEngine::create(fileName);
- bool opened = d->fileEngine->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+ d->realFile.setFileName(fileName);
+ bool opened = d->realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
// could we open the file?
if (!opened) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
- .arg(fileName, d->fileEngine->errorString());
+ .arg(d->realFile.fileName(), d->realFile.errorString());
- if (fi.exists()) {
+ if (d->realFile.exists()) {
setError(QNetworkReply::ContentAccessDenied, msg);
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
@@ -134,79 +128,62 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
return;
}
- d->fileSize = fi.size();
setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
- setHeader(QNetworkRequest::ContentLengthHeader, d->fileSize);
+ d->realFileSize = fi.size();
+ setHeader(QNetworkRequest::ContentLengthHeader, d->realFileSize);
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
- Q_ARG(qint64, d->fileSize), Q_ARG(qint64, d->fileSize));
+ Q_ARG(qint64, d->realFileSize), Q_ARG(qint64, d->realFileSize));
QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
-
-bool QFileNetworkReplyPrivate::isFinished() const
+void QNetworkReplyFileImpl::close()
{
- return true;
-}
-
-void QFileNetworkReply::close()
-{
- Q_D(QFileNetworkReply);
+ Q_D(QNetworkReplyFileImpl);
QNetworkReply::close();
- if (d->fileEngine)
- d->fileEngine->close();
+ d->realFile.close();
}
-void QFileNetworkReply::abort()
+void QNetworkReplyFileImpl::abort()
{
- Q_D(QFileNetworkReply);
+ Q_D(QNetworkReplyFileImpl);
QNetworkReply::close();
- if (d->fileEngine)
- d->fileEngine->close();
+ d->realFile.close();
}
-qint64 QFileNetworkReply::bytesAvailable() const
+qint64 QNetworkReplyFileImpl::bytesAvailable() const
{
- Q_D(const QFileNetworkReply);
- if (!d->fileEngine)
- return 0;
-
- return QNetworkReply::bytesAvailable() + d->fileSize - d->filePos;
+ Q_D(const QNetworkReplyFileImpl);
+ return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable();
}
-bool QFileNetworkReply::isSequential () const
+bool QNetworkReplyFileImpl::isSequential () const
{
return true;
}
-qint64 QFileNetworkReply::size() const
+qint64 QNetworkReplyFileImpl::size() const
{
- Q_D(const QFileNetworkReply);
- return d->fileSize;
+ Q_D(const QNetworkReplyFileImpl);
+ return d->realFileSize;
}
/*!
\internal
*/
-qint64 QFileNetworkReply::readData(char *data, qint64 maxlen)
+qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
{
- Q_D(QFileNetworkReply);
- if (!d->fileEngine)
+ Q_D(QNetworkReplyFileImpl);
+ qint64 ret = d->realFile.read(data, maxlen);
+ if (ret == 0 && bytesAvailable() == 0)
return -1;
-
- qint64 ret = d->fileEngine->read(data, maxlen);
- if (ret == 0 && bytesAvailable() == 0) {
- return -1; // everything had been read
- } else if (ret > 0) {
- d->filePos += ret;
- }
-
- return ret;
+ else
+ return ret;
}
QT_END_NAMESPACE
-#include "moc_qfilenetworkreply_p.cpp"
+#include "moc_qnetworkreplyfileimpl_p.cpp"
diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index ec8aa4e..c9f5def 100644
--- a/src/network/access/qfilenetworkreply_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QFILENETWORKREPLY_P_H
-#define QFILENETWORKREPLY_P_H
+#ifndef QNETWORKREPLYFILEIMPL_H
+#define QNETWORKREPLYFILEIMPL_H
//
// W A R N I N G
@@ -62,13 +62,13 @@
QT_BEGIN_NAMESPACE
-class QFileNetworkReplyPrivate;
-class QFileNetworkReply: public QNetworkReply
+class QNetworkReplyFileImplPrivate;
+class QNetworkReplyFileImpl: public QNetworkReply
{
Q_OBJECT
public:
- QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
- ~QFileNetworkReply();
+ QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ ~QNetworkReplyFileImpl();
virtual void abort();
// reimplemented from QNetworkReply
@@ -79,24 +79,20 @@ public:
virtual qint64 readData(char *data, qint64 maxlen);
- Q_DECLARE_PRIVATE(QFileNetworkReply)
+ Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
};
-class QFileNetworkReplyPrivate: public QNetworkReplyPrivate
+class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate
{
public:
- QFileNetworkReplyPrivate();
- ~QFileNetworkReplyPrivate();
+ QNetworkReplyFileImplPrivate();
- QAbstractFileEngine *fileEngine;
- qint64 fileSize;
- qint64 filePos;
+ QFile realFile;
+ qint64 realFileSize;
- virtual bool isFinished() const;
-
- Q_DECLARE_PUBLIC(QFileNetworkReply)
+ Q_DECLARE_PUBLIC(QNetworkReplyFileImpl)
};
QT_END_NAMESPACE
-#endif // QFILENETWORKREPLY_P_H
+#endif // QNETWORKREPLYFILEIMPL_H
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index a7a6287..0b568d4 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -52,23 +52,29 @@
#include <QtCore/QCoreApplication>
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+
QT_BEGIN_NAMESPACE
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
- : backend(0), outgoingData(0), outgoingDataBuffer(0),
+ : backend(0), outgoingData(0),
copyDevice(0),
cacheEnabled(false), cacheSaveDevice(0),
notificationHandlingPaused(false),
bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
httpStatusCode(0),
state(Idle)
+ , downloadBufferReadPosition(0)
+ , downloadBufferCurrentSize(0)
+ , downloadBufferMaximumSize(0)
+ , downloadBuffer(0)
{
}
void QNetworkReplyImplPrivate::_q_startOperation()
{
// ensure this function is only being called once
- if (state == Working) {
+ if (state == Working || state == Finished) {
qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
return;
}
@@ -84,10 +90,10 @@ void QNetworkReplyImplPrivate::_q_startOperation()
return;
}
+ if (!backend->start()) {
#ifndef QT_NO_BEARERMANAGEMENT
- if (!backend->start()) { // ### we should call that method even if bearer is not used
// backend failed to start because the session state is not Connected.
- // QNetworkAccessManager will call reply->backend->start() again for us when the session
+ // QNetworkAccessManager will call _q_startOperation again for us when the session
// state changes.
state = WaitingForSession;
@@ -103,14 +109,24 @@ void QNetworkReplyImplPrivate::_q_startOperation()
session->open();
} else {
qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
+ state = Working;
+ error(QNetworkReplyImpl::UnknownNetworkError,
+ QCoreApplication::translate("QNetworkReply", "Network session error."));
+ finished();
}
-
+#else
+ qWarning("Backend start failed");
+ state = Working;
+ error(QNetworkReplyImpl::UnknownNetworkError,
+ QCoreApplication::translate("QNetworkReply", "backend start error."));
+ finished();
+#endif
return;
}
-#endif
if (backend && backend->isSynchronous()) {
state = Finished;
+ q_func()->setFinished(true);
} else {
if (state != Finished) {
if (operation == QNetworkAccessManager::GetOperation)
@@ -129,6 +145,10 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (!copyDevice || !q->isOpen())
return;
+ // FIXME Optimize to use download buffer if it is a QBuffer.
+ // Needs to be done where sendCacheContents() (?) of HTTP is emitting
+ // metaDataChanged ?
+
forever {
qint64 bytesToRead = nextDownstreamBlockSize();
if (bytesToRead == 0)
@@ -167,9 +187,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();
}
@@ -201,7 +223,7 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
if (!outgoingDataBuffer) {
// first call, create our buffer
- outgoingDataBuffer = new QRingBuffer();
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
@@ -300,20 +322,22 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
// Internal code that does a HTTP reply for the synchronous Ajax
// in QtWebKit.
QVariant synchronousHttpAttribute = req.attribute(
- static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1));
- if (backend && synchronousHttpAttribute.toBool()) {
- backend->setSynchronous(true);
- if (outgoingData && outgoingData->isSequential()) {
- outgoingDataBuffer = new QRingBuffer();
- QByteArray data;
- do {
- data = outgoingData->readAll();
- if (data.isEmpty())
- break;
- outgoingDataBuffer->append(data);
- } while (1);
- }
+ static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
+ // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
+ // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
+ if (synchronousHttpAttribute.toBool() && outgoingData) {
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
+ qint64 previousDataSize = 0;
+ do {
+ previousDataSize = outgoingDataBuffer->size();
+ outgoingDataBuffer->append(outgoingData->readAll());
+ } while (outgoingDataBuffer->size() != previousDataSize);
}
+
+ if (backend)
+ backend->setSynchronous(synchronousHttpAttribute.toBool());
+
+
if (outgoingData && backend && !backend->isSynchronous()) {
// there is data to be uploaded, e.g. HTTP POST.
@@ -343,10 +367,6 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
}
}
} else {
- // No outgoing data (e.g. HTTP GET request)
- // or no backend
- // if no backend, _q_startOperation will handle the error of this
-
// for HTTP, we want to send out the request as fast as possible to the network, without
// invoking methods in a QueuedConnection
#ifndef QT_NO_HTTP
@@ -578,25 +598,22 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
{
Q_Q(QNetworkReplyImpl);
- QPointer<QNetworkReplyImpl> qq = q;
-
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
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());
- // hopefully we haven't been deleted here
- if (!qq.isNull()) {
- resumeNotificationHandling();
- // do we still have room in the buffer?
- if (nextDownstreamBlockSize() > 0)
- backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
- }
+ resumeNotificationHandling();
+ // do we still have room in the buffer?
+ if (nextDownstreamBlockSize() > 0)
+ backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
}
// this is used when it was fetched from the cache, right?
@@ -631,6 +648,75 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
}
+static void downloadBufferDeleter(char *ptr)
+{
+ delete[] ptr;
+}
+
+char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ if (!downloadBuffer) {
+ // We are requested to create it
+ // Check attribute() if allocating a buffer of that size can be allowed
+ QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
+ if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
+ downloadBufferCurrentSize = 0;
+ downloadBufferMaximumSize = size;
+ downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
+ downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
+
+ q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+ }
+ }
+
+ return downloadBuffer;
+}
+
+void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ downloadBufferPointer = sp;
+ downloadBuffer = downloadBufferPointer.data();
+ downloadBufferCurrentSize = 0;
+ downloadBufferMaximumSize = size;
+ q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+}
+
+
+void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!q->isOpen())
+ return;
+
+ if (cacheEnabled && !cacheSaveDevice)
+ initCacheSaveDevice();
+
+ if (cacheSaveDevice && bytesReceived == bytesTotal) {
+// if (lastBytesDownloaded == -1)
+// lastBytesDownloaded = 0;
+// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
+
+ // Write everything in one go if we use a download buffer. might be more performant.
+ cacheSaveDevice->write(downloadBuffer, bytesTotal);
+ }
+
+ bytesDownloaded = bytesReceived;
+ lastBytesDownloaded = bytesReceived;
+
+ downloadBufferCurrentSize = bytesReceived;
+
+ // 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()
{
Q_Q(QNetworkReplyImpl);
@@ -669,6 +755,8 @@ void QNetworkReplyImplPrivate::finished()
resumeNotificationHandling();
state = Finished;
+ q->setFinished(true);
+
pendingNotifications.clear();
pauseNotificationHandling();
@@ -696,6 +784,11 @@ void QNetworkReplyImplPrivate::finished()
void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
{
Q_Q(QNetworkReplyImpl);
+ // Can't set and emit multiple errors.
+ if (errorCode != QNetworkReply::NoError) {
+ qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
+ return;
+ }
errorCode = code;
q->setErrorString(errorMessage);
@@ -739,11 +832,6 @@ 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)
{
@@ -758,9 +846,6 @@ QNetworkReplyImpl::~QNetworkReplyImpl()
// save had been properly finished. So if it is still enabled it means we got deleted/aborted.
if (d->isCachingEnabled())
d->networkCache()->remove(url());
-
- if (d->outgoingDataBuffer)
- delete d->outgoingDataBuffer;
}
void QNetworkReplyImpl::abort()
@@ -778,7 +863,7 @@ void QNetworkReplyImpl::abort()
QNetworkReply::close();
if (d->state != QNetworkReplyImplPrivate::Finished) {
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
d->state = QNetworkReplyImplPrivate::Working;
@@ -808,7 +893,7 @@ void QNetworkReplyImpl::close()
QNetworkReply::close();
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
d->finished();
}
@@ -827,6 +912,13 @@ bool QNetworkReplyImpl::canReadLine () const
*/
qint64 QNetworkReplyImpl::bytesAvailable() const
{
+ // Special case for the "zero copy" download buffer
+ Q_D(const QNetworkReplyImpl);
+ if (d->downloadBuffer) {
+ qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
+ return QNetworkReply::bytesAvailable() + maxAvail;
+ }
+
return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
}
@@ -881,8 +973,22 @@ void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &er
qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
{
Q_D(QNetworkReplyImpl);
+
+ // Special case code if we have the "zero copy" download buffer
+ if (d->downloadBuffer) {
+ qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
+ if (maxAvail == 0)
+ return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
+ qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
+ d->downloadBufferReadPosition += maxAvail;
+ return maxAvail;
+ }
+
+
if (d->readBuffer.isEmpty())
return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
if (maxlen == 1) {
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index 31da297..089c87e 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -62,6 +62,7 @@
#include "QtCore/qbuffer.h"
#include "private/qringbuffer_p.h"
#include "private/qbytedata_p.h"
+#include <QSharedPointer>
QT_BEGIN_NAMESPACE
@@ -163,17 +164,19 @@ public:
void appendDownstreamData(QIODevice *data);
void appendDownstreamData(const QByteArray &data);
+ void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
+ char* getDownloadBuffer(qint64 size);
+ void appendDownstreamDataDownloadBuffer(qint64, qint64);
+
void finished();
void error(QNetworkReply::NetworkError code, const QString &errorString);
void metaDataChanged();
void redirectionRequested(const QUrl &target);
void sslErrors(const QList<QSslError> &errors);
- bool isFinished() const;
-
QNetworkAccessBackend *backend;
QIODevice *outgoingData;
- QRingBuffer *outgoingDataBuffer;
+ QSharedPointer<QRingBuffer> outgoingDataBuffer;
QIODevice *copyDevice;
QAbstractNetworkCache *networkCache() const;
@@ -191,6 +194,7 @@ public:
QList<QNetworkProxy> proxyList;
#endif
+ // Used for normal downloading. For "zero copy" the downloadBuffer is used
QByteDataBuffer readBuffer;
qint64 bytesDownloaded;
qint64 lastBytesDownloaded;
@@ -202,6 +206,14 @@ public:
State state;
+ // only used when the "zero copy" style is used. Else readBuffer is used.
+ // Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
+ qint64 downloadBufferReadPosition;
+ qint64 downloadBufferCurrentSize;
+ qint64 downloadBufferMaximumSize;
+ QSharedPointer<char> downloadBufferPointer;
+ char* downloadBuffer;
+
Q_DECLARE_PUBLIC(QNetworkReplyImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index cad8ef6..8b3b359 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -226,6 +226,8 @@ QT_BEGIN_NAMESPACE
\omitvalue DownloadBufferAttribute
+ \omitvalue SynchronousRequestAttribute
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -522,7 +524,7 @@ void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
QSslConfiguration QNetworkRequest::sslConfiguration() const
{
if (!d->sslConfiguration)
- d->sslConfiguration = new QSslConfiguration;
+ d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
return *d->sslConfiguration;
}
@@ -637,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
}
@@ -649,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:
@@ -757,7 +763,7 @@ static QVariant parseCookieHeader(const QByteArray &raw)
result += parsed;
}
- return qVariantFromValue(result);
+ return QVariant::fromValue(result);
}
static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
@@ -790,7 +796,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
return parseCookieHeader(value);
case QNetworkRequest::SetCookieHeader:
- return qVariantFromValue(QNetworkCookie::parseCookies(value));
+ return QVariant::fromValue(QNetworkCookie::parseCookies(value));
default:
Q_ASSERT(0);
@@ -810,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 6d0e02c..eedb0f6 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,
@@ -84,9 +85,7 @@ public:
CookieSaveControlAttribute,
MaximumDownloadBufferSizeAttribute, // internal
DownloadBufferAttribute, // internal
-
- // (DownloadBufferAttribute + 1) is reserved internal for QSynchronousHttpNetworkReply
- // add the enum in 4.8
+ SynchronousRequestAttribute, // internal
User = 1000,
UserMax = 32767
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 80b4bdc..54251c8 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);