summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authoraxis <qt-info@nokia.com>2009-04-24 14:03:55 (GMT)
committeraxis <qt-info@nokia.com>2009-04-27 07:09:01 (GMT)
commite74c8dc65e2feffb9a55d00aee5ca634fba41df8 (patch)
tree3a131f9235fb6a455793178d8313655e4fd0036e /src/network
parent8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (diff)
parent211bea9838bcc2acd7f54b65468fe1be2d81b1e0 (diff)
downloadQt-e74c8dc65e2feffb9a55d00aee5ca634fba41df8.zip
Qt-e74c8dc65e2feffb9a55d00aee5ca634fba41df8.tar.gz
Qt-e74c8dc65e2feffb9a55d00aee5ca634fba41df8.tar.bz2
Merge branch '4.5' of git@scm.dev.nokia.troll.no:qt/qt
Configure.exe recompiled with MSVC6. Conflicts: configure.exe examples/network/network.pro src/gui/dialogs/qfiledialog_p.h src/gui/dialogs/qfilesystemmodel_p.h src/gui/kernel/qapplication.cpp tests/auto/_Categories/qmake.txt tests/auto/qfile/test/test.pro tests/auto/qfile/tst_qfile.cpp tests/auto/qlibrary/tst_qlibrary.cpp tests/auto/qline/tst_qline.cpp tests/auto/qstyle/tst_qstyle.cpp tests/auto/qtextstream/tst_qtextstream.cpp tests/auto/qtranslator/qtranslator.pro tests/auto/qwaitcondition/tst_qwaitcondition.cpp translations/qt_ja_JP.ts
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri6
-rw-r--r--src/network/access/qhttp.cpp6
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp1127
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h243
-rw-r--r--src/network/access/qhttpnetworkheader.cpp123
-rw-r--r--src/network/access/qhttpnetworkheader_p.h111
-rw-r--r--src/network/access/qhttpnetworkreply.cpp663
-rw-r--r--src/network/access/qhttpnetworkreply_p.h224
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp261
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h144
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp36
-rw-r--r--src/network/access/qnetworkcookie.cpp466
-rw-r--r--src/network/access/qnetworkdiskcache.cpp3
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp44
-rw-r--r--src/network/kernel/qauthenticator.cpp2
-rw-r--r--src/network/kernel/qhostaddress.cpp2
-rw-r--r--src/network/kernel/qnetworkinterface_win.cpp6
-rw-r--r--src/network/socket/qabstractsocket.cpp4
-rw-r--r--src/network/socket/qlocalserver_unix.cpp6
-rw-r--r--src/network/socket/qlocalsocket_p.h1
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp28
-rw-r--r--src/network/socket/qlocalsocket_win.cpp2
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp39
23 files changed, 2176 insertions, 1371 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 6bcca0c..3269b2e 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -2,6 +2,9 @@
HEADERS += access/qftp.h \
access/qhttp.h \
+ access/qhttpnetworkheader_p.h \
+ access/qhttpnetworkrequest_p.h \
+ access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
@@ -27,6 +30,9 @@ HEADERS += access/qftp.h \
SOURCES += access/qftp.cpp \
access/qhttp.cpp \
+ access/qhttpnetworkheader.cpp \
+ access/qhttpnetworkrequest.cpp \
+ access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
diff --git a/src/network/access/qhttp.cpp b/src/network/access/qhttp.cpp
index 0141ae2..96ccc91 100644
--- a/src/network/access/qhttp.cpp
+++ b/src/network/access/qhttp.cpp
@@ -2120,6 +2120,10 @@ int QHttp::setUser(const QString &userName, const QString &password)
Web proxy cache server (from \l http://www.squid.org/). For transparent
proxying, such as SOCKS5, use QNetworkProxy instead.
+ \note setProxy() has to be called before setHost() for it to take effect.
+ If setProxy() is called after setHost(), then it will not apply until after
+ setHost() is called again.
+
\sa QFtp::setProxy()
*/
int QHttp::setProxy(const QString &host, int port,
@@ -2139,7 +2143,7 @@ int QHttp::setProxy(const QString &host, int port,
is QNetworkProxy::HttpCachingProxy, QHttp will behave like the
previous function.
- Note: for compatibility with Qt 4.3, if the proxy type is
+ \note for compatibility with Qt 4.3, if the proxy type is
QNetworkProxy::HttpProxy and the request type is unencrypted (that
is, ConnectionModeHttp), QHttp will treat the proxy as a caching
proxy.
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index edb2988..5940fba 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -45,7 +45,7 @@
#include <private/qauthenticator_p.h>
#include <qnetworkproxy.h>
#include <qauthenticator.h>
-#include <qbytearraymatcher.h>
+
#include <qbuffer.h>
#include <qpair.h>
#include <qhttp.h>
@@ -59,875 +59,9 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
-#ifndef QT_NO_COMPRESS
-# include <zlib.h>
-static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
-// gzip flag byte
-#define HEAD_CRC 0x02 // bit 1 set: header CRC present
-#define EXTRA_FIELD 0x04 // bit 2 set: extra field present
-#define ORIG_NAME 0x08 // bit 3 set: original file name present
-#define COMMENT 0x10 // bit 4 set: file comment present
-#define RESERVED 0xE0 // bits 5..7: reserved
-#define CHUNK 16384
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QHttpNetworkHeaderPrivate : public QSharedData
-{
-public:
- QUrl url;
- QList<QPair<QByteArray, QByteArray> > fields;
-
- QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
- QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
- inline qint64 contentLength() const;
- inline void setContentLength(qint64 length);
-
- inline QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- inline QList<QByteArray> headerFieldValues(const QByteArray &name) const;
- inline void setHeaderField(const QByteArray &name, const QByteArray &data);
- bool operator==(const QHttpNetworkHeaderPrivate &other) const;
-
-};
-
-QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
- :url(newUrl)
-{
-}
-
-QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
- :QSharedData(other)
-{
- url = other.url;
- fields = other.fields;
-}
-
-qint64 QHttpNetworkHeaderPrivate::contentLength() const
-{
- bool ok = false;
- QByteArray value = headerField("content-length");
- qint64 length = value.toULongLong(&ok);
- if (ok)
- return length;
- return -1; // the header field is not set
-}
-
-void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
-{
- setHeaderField("Content-Length", QByteArray::number(length));
-}
-
-QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- QList<QByteArray> allValues = headerFieldValues(name);
- if (allValues.isEmpty())
- return defaultValue;
-
- QByteArray result;
- bool first = true;
- foreach (QByteArray value, allValues) {
- if (!first)
- result += ", ";
- first = false;
- result += value;
- }
- return result;
-}
-
-QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
-{
- QList<QByteArray> result;
- QByteArray lowerName = name.toLower();
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
- end = fields.constEnd();
- for ( ; it != end; ++it)
- if (lowerName == it->first.toLower())
- result += it->second;
-
- return result;
-}
-
-void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
-{
- QByteArray lowerName = name.toLower();
- QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin();
- while (it != fields.end()) {
- if (lowerName == it->first.toLower())
- it = fields.erase(it);
- else
- ++it;
- }
- fields.append(qMakePair(name, data));
-}
-
-bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
-{
- return (url == other.url);
-}
-
-// QHttpNetworkRequestPrivate
-class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
-{
-public:
- QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
- QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
- QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
- ~QHttpNetworkRequestPrivate();
- bool operator==(const QHttpNetworkRequestPrivate &other) const;
- QByteArray methodName() const;
- QByteArray uri(bool throughProxy) const;
-
- static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
-
- QHttpNetworkRequest::Operation operation;
- QHttpNetworkRequest::Priority priority;
- mutable QIODevice *data;
- bool autoDecompress;
-};
-
-QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
- QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0),
- autoDecompress(false)
-{
-}
-
-QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
- : QHttpNetworkHeaderPrivate(other)
-{
- operation = other.operation;
- priority = other.priority;
- data = other.data;
- autoDecompress = other.autoDecompress;
-}
-
-QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
-{
-}
-
-bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
-{
- return QHttpNetworkHeaderPrivate::operator==(other)
- && (operation == other.operation)
- && (data == other.data);
-}
-
-QByteArray QHttpNetworkRequestPrivate::methodName() const
-{
- QByteArray ba;
- switch (operation) {
- case QHttpNetworkRequest::Options:
- ba += "OPTIONS";
- break;
- case QHttpNetworkRequest::Get:
- ba += "GET";
- break;
- case QHttpNetworkRequest::Head:
- ba += "HEAD";
- break;
- case QHttpNetworkRequest::Post:
- ba += "POST";
- break;
- case QHttpNetworkRequest::Put:
- ba += "PUT";
- break;
- case QHttpNetworkRequest::Delete:
- ba += "DELETE";
- break;
- case QHttpNetworkRequest::Trace:
- ba += "TRACE";
- break;
- case QHttpNetworkRequest::Connect:
- ba += "CONNECT";
- break;
- default:
- break;
- }
- return ba;
-}
-
-QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
-{
- QUrl::FormattingOptions format(QUrl::RemoveFragment);
-
- // for POST, query data is send as content
- if (operation == QHttpNetworkRequest::Post && !data)
- format |= QUrl::RemoveQuery;
- // for requests through proxy, the Request-URI contains full url
- if (throughProxy)
- format |= QUrl::RemoveUserInfo;
- else
- format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
- QByteArray uri = url.toEncoded(format);
- if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
- uri += '/';
- return uri;
-}
-
-QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
-{
- QByteArray ba = request.d->methodName();
- QByteArray uri = request.d->uri(throughProxy);
- ba += " " + uri;
-
- QString majorVersion = QString::number(request.majorVersion());
- QString minorVersion = QString::number(request.minorVersion());
- ba += " HTTP/" + majorVersion.toLatin1() + "." + minorVersion.toLatin1() + "\r\n";
-
- QList<QPair<QByteArray, QByteArray> > fields = request.header();
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- for (; it != fields.constEnd(); ++it)
- ba += it->first + ": " + it->second + "\r\n";
- 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.d->data && request.d->url.hasQuery()) {
- QByteArray query = request.d->url.encodedQuery();
- ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n";
- ba += "\r\n";
- ba += query;
- } else {
- ba += "\r\n";
- }
- } else {
- ba += "\r\n";
- }
- return ba;
-}
-
-class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
-{
-public:
- QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
- ~QHttpNetworkReplyPrivate();
- qint64 readStatus(QAbstractSocket *socket);
- void parseStatus(const QByteArray &status);
- qint64 readHeader(QAbstractSocket *socket);
- void parseHeader(const QByteArray &header);
- qint64 readBody(QAbstractSocket *socket, QIODevice *out);
- bool findChallenge(bool forProxy, QByteArray &challenge) const;
- QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
- void clear();
-
- qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size);
- qint64 transferChunked(QIODevice *in, QIODevice *out);
- qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
-
- qint64 bytesAvailable() const;
- bool isChunked();
- bool connectionCloseEnabled();
- bool isGzipped();
-#ifndef QT_NO_COMPRESS
- bool gzipCheckHeader(QByteArray &content, int &pos);
- int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated);
-#endif
- void removeAutoDecompressHeader();
-
- enum ReplyState {
- NothingDoneState,
- ReadingStatusState,
- ReadingHeaderState,
- ReadingDataState,
- AllDoneState
- } state;
-
- QHttpNetworkRequest request;
- int statusCode;
- int majorVersion;
- int minorVersion;
- QString errorString;
- QString reasonPhrase;
- qint64 bodyLength;
- qint64 contentRead;
- qint64 totalProgress;
- QByteArray fragment;
- qint64 currentChunkSize;
- qint64 currentChunkRead;
- QPointer<QHttpNetworkConnection> connection;
- bool initInflate;
- bool streamEnd;
-#ifndef QT_NO_COMPRESS
- z_stream inflateStrm;
-#endif
- bool autoDecompress;
-
- QByteArray responseData; // uncompressed body
- QByteArray compressedData; // compressed body (temporary)
- QBuffer requestDataBuffer;
- bool requestIsBuffering;
- bool requestIsPrepared;
-};
-
-QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
- majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
- currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
- autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false)
-{
-}
-
-QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
-{
-}
-
-void QHttpNetworkReplyPrivate::clear()
-{
- state = NothingDoneState;
- statusCode = 100;
- bodyLength = 0;
- contentRead = 0;
- totalProgress = 0;
- currentChunkSize = 0;
- currentChunkRead = 0;
- connection = 0;
-#ifndef QT_NO_COMPRESS
- if (initInflate)
- inflateEnd(&inflateStrm);
-#endif
- initInflate = false;
- streamEnd = false;
- autoDecompress = false;
- fields.clear();
-}
-
-// QHttpNetworkReplyPrivate
-qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
-{
- return (state != ReadingDataState ? 0 : fragment.size());
-}
-bool QHttpNetworkReplyPrivate::isGzipped()
-{
- QByteArray encoding = headerField("content-encoding");
- return encoding.toLower() == "gzip";
-}
-void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
-{
- // The header "Content-Encoding = gzip" is retained.
- // Content-Length is removed since the actual one send by the server is for compressed data
- QByteArray name("content-length");
- QByteArray lowerName = name.toLower();
- QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
- end = fields.end();
- while (it != end) {
- if (name == it->first.toLower()) {
- fields.erase(it);
- break;
- }
- ++it;
- }
-
-}
-
-bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
-{
- challenge.clear();
- // find out the type of authentication protocol requested.
- QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
- // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i);
- if (!line.toLower().startsWith("negotiate"))
- challenge = line;
- }
- return !challenge.isEmpty();
-}
-
-QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
-{
- // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
- QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
- QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i).trimmed().toLower();
- if (method < QAuthenticatorPrivate::Basic
- && line.startsWith("basic")) {
- method = QAuthenticatorPrivate::Basic;
- } else if (method < QAuthenticatorPrivate::Ntlm
- && line.startsWith("ntlm")) {
- method = QAuthenticatorPrivate::Ntlm;
- } else if (method < QAuthenticatorPrivate::DigestMd5
- && line.startsWith("digest")) {
- method = QAuthenticatorPrivate::DigestMd5;
- }
- }
- return method;
-}
-
-#ifndef QT_NO_COMPRESS
-bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
-{
- int method = 0; // method byte
- int flags = 0; // flags byte
- bool ret = false;
-
- // Assure two bytes in the buffer so we can peek ahead -- handle case
- // where first byte of header is at the end of the buffer after the last
- // gzip segment
- pos = -1;
- QByteArray &body = content;
- int maxPos = body.size()-1;
- if (maxPos < 1) {
- return ret;
- }
-
- // Peek ahead to check the gzip magic header
- if (body[0] != char(gz_magic[0]) ||
- body[1] != char(gz_magic[1])) {
- return ret;
- }
- pos += 2;
- // Check the rest of the gzip header
- if (++pos <= maxPos)
- method = body[pos];
- if (pos++ <= maxPos)
- flags = body[pos];
- if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
- return ret;
- }
-
- // Discard time, xflags and OS code:
- pos += 6;
- if (pos > maxPos)
- return ret;
- if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
- unsigned len = (unsigned)body[++pos];
- len += ((unsigned)body[++pos])<<8;
- pos += len;
- if (pos > maxPos)
- return ret;
- }
- if ((flags & ORIG_NAME) != 0) { // skip the original file name
- while(++pos <= maxPos && body[pos]) {}
- }
- if ((flags & COMMENT) != 0) { // skip the .gz file comment
- while(++pos <= maxPos && body[pos]) {}
- }
- if ((flags & HEAD_CRC) != 0) { // skip the header crc
- pos += 2;
- if (pos > maxPos)
- return ret;
- }
- ret = (pos < maxPos); // return failed, if no more bytes left
- return ret;
-}
-
-int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
-{
- int ret = Z_DATA_ERROR;
- unsigned have;
- unsigned char out[CHUNK];
- int pos = -1;
-
- if (!initInflate) {
- // check the header
- if (!gzipCheckHeader(compressed, pos))
- return ret;
- // allocate inflate state
- inflateStrm.zalloc = Z_NULL;
- inflateStrm.zfree = Z_NULL;
- inflateStrm.opaque = Z_NULL;
- inflateStrm.avail_in = 0;
- inflateStrm.next_in = Z_NULL;
- ret = inflateInit2(&inflateStrm, -MAX_WBITS);
- if (ret != Z_OK)
- return ret;
- initInflate = true;
- streamEnd = false;
- }
-
- //remove the header.
- compressed.remove(0, pos+1);
- // expand until deflate stream ends
- inflateStrm.next_in = (unsigned char *)compressed.data();
- inflateStrm.avail_in = compressed.size();
- do {
- inflateStrm.avail_out = sizeof(out);
- inflateStrm.next_out = out;
- ret = inflate(&inflateStrm, Z_NO_FLUSH);
- switch (ret) {
- case Z_NEED_DICT:
- ret = Z_DATA_ERROR;
- // and fall through
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- inflateEnd(&inflateStrm);
- initInflate = false;
- return ret;
- }
- have = sizeof(out) - inflateStrm.avail_out;
- inflated.append(QByteArray((const char *)out, have));
- } while (inflateStrm.avail_out == 0);
- // clean up and return
- if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
- inflateEnd(&inflateStrm);
- initInflate = false;
- }
- streamEnd = (ret == Z_STREAM_END);
- return ret;
-}
-#endif
-
-qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
-{
- qint64 bytes = 0;
- char c;
-
- while (socket->bytesAvailable()) {
- // allow both CRLF & LF (only) line endings
- if (socket->peek(&c, 1) == 1 && c == '\n') {
- bytes += socket->read(&c, 1); // read the "n"
- // remove the CR at the end
- if (fragment.endsWith('\r')) {
- fragment.truncate(fragment.length()-1);
- }
- parseStatus(fragment);
- state = ReadingHeaderState;
- fragment.clear(); // next fragment
- break;
- } else {
- c = 0;
- bytes += socket->read(&c, 1);
- fragment.append(c);
- }
- }
- return bytes;
-}
-
-void QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
-{
- const QByteArrayMatcher sp(" ");
- int i = sp.indexIn(status);
- const QByteArray version = status.mid(0, i);
- int j = sp.indexIn(status, i + 1);
- const QByteArray code = status.mid(i + 1, j - i - 1);
- const QByteArray reason = status.mid(j + 1, status.count() - j);
-
- const QByteArrayMatcher slash("/");
- int k = slash.indexIn(version);
- const QByteArrayMatcher dot(".");
- int l = dot.indexIn(version, k);
- const QByteArray major = version.mid(k + 1, l - k - 1);
- const QByteArray minor = version.mid(l + 1, version.count() - l);
-
- majorVersion = QString::fromAscii(major.constData()).toInt();
- minorVersion = QString::fromAscii(minor.constData()).toInt();
- statusCode = QString::fromAscii(code.constData()).toInt();
- reasonPhrase = QString::fromAscii(reason.constData());
-}
-
-qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
-{
- qint64 bytes = 0;
- char crlfcrlf[5];
- crlfcrlf[4] = '\0';
- char c = 0;
- bool allHeaders = false;
- while (!allHeaders && socket->bytesAvailable()) {
- if (socket->peek(&c, 1) == 1 && c == '\n') {
- // check for possible header endings. As per HTTP rfc,
- // the header endings will be marked by CRLFCRLF. But
- // we will allow CRLFLF, LFLF & CRLFCRLF
- if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
- allHeaders = true;
- }
- bytes += socket->read(&c, 1);
- fragment.append(c);
- }
- // we received all headers now parse them
- if (allHeaders) {
- parseHeader(fragment);
- state = ReadingDataState;
- fragment.clear(); // next fragment
- bodyLength = contentLength(); // cache the length
- }
- return bytes;
-}
-
-void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
-{
- // see rfc2616, sec 4 for information about HTTP/1.1 headers.
- // allows relaxed parsing here, accepts both CRLF & LF line endings
- const QByteArrayMatcher lf("\n");
- const QByteArrayMatcher colon(":");
- int i = 0;
- while (i < header.count()) {
- int j = colon.indexIn(header, i); // field-name
- if (j == -1)
- break;
- const QByteArray field = header.mid(i, j - i).trimmed();
- j++;
- // any number of LWS is allowed before and after the value
- QByteArray value;
- do {
- i = lf.indexIn(header, j);
- if (i == -1)
- break;
- if (!value.isEmpty())
- value += ' ';
- // check if we have CRLF or only LF
- bool hasCR = (i && header[i-1] == '\r');
- int length = i -(hasCR ? 1: 0) - j;
- value += header.mid(j, length).trimmed();
- j = ++i;
- } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
- if (i == -1)
- break; // something is wrong
-
- fields.append(qMakePair(field, value));
- }
-}
-
-bool QHttpNetworkReplyPrivate::isChunked()
-{
- return headerField("transfer-encoding").toLower().contains("chunked");
-}
-
-bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
-{
- return (headerField("connection").toLower().contains("close") ||
- headerField("proxy-connection").toLower().contains("close"));
-}
-
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
-{
- qint64 bytes = 0;
- if (isChunked()) {
- bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
- } else if (bodyLength > 0) { // we have a Content-Length
- bytes += transferRaw(socket, out, bodyLength - contentRead);
- if (contentRead + bytes == bodyLength)
- state = AllDoneState;
- } else {
- bytes += transferRaw(socket, out, socket->bytesAvailable());
- }
- if (state == AllDoneState)
- socket->readAll(); // Read the rest to clean (CRLF)
- contentRead += bytes;
- return bytes;
-}
-
-qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size)
-{
- qint64 bytes = 0;
- Q_ASSERT(in);
- Q_ASSERT(out);
-
- int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
- QByteArray raw(toBeRead, 0);
- while (size > 0) {
- qint64 read = in->read(raw.data(), raw.size());
- if (read == 0)
- return bytes;
- // ### error checking here
- qint64 written = out->write(raw.data(), read);
- if (written == 0)
- return bytes;
- if (read != written)
- qDebug() << "### read" << read << "written" << written;
- bytes += read;
- size -= read;
- out->waitForBytesWritten(-1); // throttle
- }
- return bytes;
-
-}
-
-qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *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
- if (currentChunkRead >= currentChunkSize) {
- currentChunkSize = 0;
- currentChunkRead = 0;
- if (bytes) {
- char crlf[2];
- bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
- }
- bytes += getChunkSize(in, &currentChunkSize);
- if (currentChunkSize == -1)
- break;
- }
- // if the chunk size is 0, end of the stream
- if (currentChunkSize == 0) {
- state = AllDoneState;
- break;
- }
- // otherwise, read data
- qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
- QByteArray buffer(readSize, 0);
- qint64 read = in->read(buffer.data(), readSize);
- bytes += read;
- currentChunkRead += read;
- qint64 written = out->write(buffer);
- Q_UNUSED(written); // Avoid compile warning when building release
- Q_ASSERT(read == written);
- // ### error checking here
- out->waitForBytesWritten(-1);
- }
- return bytes;
-}
-
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
-{
- qint64 bytes = 0;
- char crlf[2];
- *chunkSize = -1;
- int bytesAvailable = in->bytesAvailable();
- while (bytesAvailable > bytes) {
- qint64 sniffedBytes = in->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
- if (crlf[0] == '\r')
- bytes += in->read(crlf, 1); // read the \n
- bool ok = false;
- // ignore the chunk-extension
- fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
- *chunkSize = fragment.toLong(&ok, 16);
- fragment.clear();
- break; // size done
- } else {
- // read the fragment to the buffer
- char c = 0;
- bytes += in->read(&c, 1);
- fragment.append(c);
- }
- }
- return bytes;
-}
-
-// QHttpNetworkConnectionPrivate
-
-typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
-
-class QHttpNetworkConnectionPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QHttpNetworkConnection)
-public:
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
- ~QHttpNetworkConnectionPrivate();
- void init();
- void connectSignals(QAbstractSocket *socket);
-
- enum SocketState {
- IdleState = 0, // ready to send request
- ConnectingState = 1, // connecting to host
- WritingState = 2, // writing the data
- WaitingState = 4, // waiting for reply
- ReadingState = 8, // reading the reply
- Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done
- BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
- };
-
- enum { ChunkSize = 4096 };
-
- int indexOf(QAbstractSocket *socket) const;
- bool isSocketBusy(QAbstractSocket *socket) const;
- bool isSocketWriting(QAbstractSocket *socket) const;
- bool isSocketWaiting(QAbstractSocket *socket) const;
- bool isSocketReading(QAbstractSocket *socket) const;
-
- QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
- void unqueueRequest(QAbstractSocket *socket);
- void prepareRequest(HttpMessagePair &request);
- bool sendRequest(QAbstractSocket *socket);
- void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply);
- void resendCurrentRequest(QAbstractSocket *socket);
- void closeChannel(int channel);
- void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
-
- // private slots
- void _q_bytesWritten(qint64 bytes); // proceed sending
- void _q_readyRead(); // pending data to read
- void _q_disconnected(); // disconnected from host
- void _q_startNextRequest(); // send the next request from the queue
- void _q_restartPendingRequest(); // send the currently blocked request
- void _q_connected(); // start sending request
- void _q_error(QAbstractSocket::SocketError); // error from socket
-#ifndef QT_NO_NETWORKPROXY
- void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
-#endif
- void _q_dataReadyReadNoBuffer();
- void _q_dataReadyReadBuffer();
-
- void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
- bool ensureConnection(QAbstractSocket *socket);
- QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket);
- void eraseData(QHttpNetworkReply *reply);
-#ifndef QT_NO_COMPRESS
- bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
-#endif
- void bufferData(HttpMessagePair &request);
- void removeReply(QHttpNetworkReply *reply);
-
- QString hostName;
- quint16 port;
- bool encrypt;
-
- struct Channel {
- QAbstractSocket *socket;
- SocketState state;
- QHttpNetworkRequest request; // current request
- QHttpNetworkReply *reply; // current reply for this request
- qint64 written;
- qint64 bytesTotal;
- bool resendCurrent;
- int lastStatus; // last status received on this channel
- bool pendingEncrypt; // for https (send after encrypted)
- int reconnectAttempts; // maximum 2 reconnection attempts
- QAuthenticatorPrivate::Method authMehtod;
- QAuthenticatorPrivate::Method proxyAuthMehtod;
- QAuthenticator authenticator;
- QAuthenticator proxyAuthenticator;
-#ifndef QT_NO_OPENSSL
- bool ignoreSSLErrors;
-#endif
- Channel() :state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), reconnectAttempts(2),
- authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
-#ifndef QT_NO_OPENSSL
- , ignoreSSLErrors(false)
-#endif
- {}
- };
- static const int channelCount;
- Channel channels[2]; // maximum of 2 socket connections to the server
- bool pendingAuthSignal; // there is an incomplete authentication signal
- bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
-
- void appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed);
- qint64 bytesAvailable(const QHttpNetworkReply &reply, bool compressed = false) const;
- qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed);
- void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
- bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
- void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply);
- void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply);
- inline bool emitSignals(QHttpNetworkReply *reply);
- inline bool expectContent(QHttpNetworkReply *reply);
-
-#ifndef QT_NO_OPENSSL
- void _q_encrypted(); // start sending request (https)
- void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
- QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
-#endif
-
-#ifndef QT_NO_NETWORKPROXY
- QNetworkProxy networkProxy;
-#endif
-
- //The request queues
- QList<HttpMessagePair> highPriorityQueue;
- QList<HttpMessagePair> lowPriorityQueue;
-};
+QT_BEGIN_NAMESPACE
const int QHttpNetworkConnectionPrivate::channelCount = 2;
@@ -1131,6 +265,11 @@ bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket)
if (socket->state() != QAbstractSocket::ConnectedState) {
// connect to the host if not already connected.
int index = indexOf(socket);
+ // resend this request after we receive the disconnected signal
+ if (socket->state() == QAbstractSocket::ClosingState) {
+ channels[index].resendCurrent = true;
+ return false;
+ }
channels[index].state = ConnectingState;
channels[index].pendingEncrypt = encrypt;
@@ -1848,6 +987,9 @@ void QHttpNetworkConnectionPrivate::_q_disconnected()
channels[i].state = ReadingState;
if (channels[i].reply)
receiveReply(socket, channels[i].reply);
+ } else if (channels[i].state == IdleState && channels[i].resendCurrent) {
+ // re-sending request because the socket was in ClosingState
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
channels[i].state = IdleState;
}
@@ -2150,235 +1292,6 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
-// QHttpNetworkRequest
-
-QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
- : d(new QHttpNetworkRequestPrivate(operation, priority, url))
-{
-}
-
-QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
- : QHttpNetworkHeader(other), d(other.d)
-{
-}
-
-QHttpNetworkRequest::~QHttpNetworkRequest()
-{
-}
-
-QUrl QHttpNetworkRequest::url() const
-{
- return d->url;
-}
-void QHttpNetworkRequest::setUrl(const QUrl &url)
-{
- d->url = url;
-}
-
-qint64 QHttpNetworkRequest::contentLength() const
-{
- return d->contentLength();
-}
-
-void QHttpNetworkRequest::setContentLength(qint64 length)
-{
- d->setContentLength(length);
-}
-
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
-{
- return d->fields;
-}
-
-QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- return d->headerField(name, defaultValue);
-}
-
-void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
-{
- d->setHeaderField(name, data);
-}
-
-QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
-{
- d = other.d;
- return *this;
-}
-
-bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
-{
- return d->operator==(*other.d);
-}
-
-QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
-{
- return d->operation;
-}
-
-void QHttpNetworkRequest::setOperation(Operation operation)
-{
- d->operation = operation;
-}
-
-QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
-{
- return d->priority;
-}
-
-void QHttpNetworkRequest::setPriority(Priority priority)
-{
- d->priority = priority;
-}
-
-QIODevice *QHttpNetworkRequest::data() const
-{
- return d->data;
-}
-
-void QHttpNetworkRequest::setData(QIODevice *data)
-{
- d->data = data;
-}
-
-int QHttpNetworkRequest::majorVersion() const
-{
- return 1;
-}
-
-int QHttpNetworkRequest::minorVersion() const
-{
- return 1;
-}
-
-// QHttpNetworkReply
-
-QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
- : QObject(*new QHttpNetworkReplyPrivate(url), parent)
-{
-}
-
-QHttpNetworkReply::~QHttpNetworkReply()
-{
- Q_D(QHttpNetworkReply);
- if (d->connection) {
- d->connection->d_func()->removeReply(this);
- }
-}
-
-QUrl QHttpNetworkReply::url() const
-{
- return d_func()->url;
-}
-void QHttpNetworkReply::setUrl(const QUrl &url)
-{
- Q_D(QHttpNetworkReply);
- d->url = url;
-}
-
-qint64 QHttpNetworkReply::contentLength() const
-{
- return d_func()->contentLength();
-}
-
-void QHttpNetworkReply::setContentLength(qint64 length)
-{
- Q_D(QHttpNetworkReply);
- d->setContentLength(length);
-}
-
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
-{
- return d_func()->fields;
-}
-
-QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- return d_func()->headerField(name, defaultValue);
-}
-
-void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
-{
- Q_D(QHttpNetworkReply);
- d->setHeaderField(name, data);
-}
-
-void QHttpNetworkReply::parseHeader(const QByteArray &header)
-{
- Q_D(QHttpNetworkReply);
- d->parseHeader(header);
-}
-
-QHttpNetworkRequest QHttpNetworkReply::request() const
-{
- return d_func()->request;
-}
-
-void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
-{
- Q_D(QHttpNetworkReply);
- d->request = request;
-}
-
-int QHttpNetworkReply::statusCode() const
-{
- return d_func()->statusCode;
-}
-
-void QHttpNetworkReply::setStatusCode(int code)
-{
- Q_D(QHttpNetworkReply);
- d->statusCode = code;
-}
-
-QString QHttpNetworkReply::errorString() const
-{
- return d_func()->errorString;
-}
-
-QString QHttpNetworkReply::reasonPhrase() const
-{
- return d_func()->reasonPhrase;
-}
-
-void QHttpNetworkReply::setErrorString(const QString &error)
-{
- Q_D(QHttpNetworkReply);
- d->errorString = error;
-}
-
-int QHttpNetworkReply::majorVersion() const
-{
- return d_func()->majorVersion;
-}
-
-int QHttpNetworkReply::minorVersion() const
-{
- return d_func()->minorVersion;
-}
-
-qint64 QHttpNetworkReply::bytesAvailable() const
-{
- Q_D(const QHttpNetworkReply);
- if (d->connection)
- return d->connection->d_func()->bytesAvailable(*this);
- else
- return -1;
-}
-
-QByteArray QHttpNetworkReply::read(qint64 maxSize)
-{
- Q_D(QHttpNetworkReply);
- QByteArray data;
- if (d->connection)
- d->connection->d_func()->read(*this, data, maxSize, false);
- return data;
-}
-
-bool QHttpNetworkReply::isFinished() const
-{
- return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
-}
// SSL support below
#ifndef QT_NO_OPENSSL
@@ -2433,27 +1346,7 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel)
}
}
-QSslConfiguration QHttpNetworkReply::sslConfiguration() const
-{
- Q_D(const QHttpNetworkReply);
- if (d->connection)
- return d->connection->d_func()->sslConfiguration(*this);
- return QSslConfiguration();
-}
-void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
-{
- Q_D(QHttpNetworkReply);
- if (d->connection)
- d->connection->setSslConfiguration(config);
-}
-
-void QHttpNetworkReply::ignoreSslErrors()
-{
- Q_D(QHttpNetworkReply);
- if (d->connection)
- d->connection->ignoreSslErrors();
-}
#endif //QT_NO_OPENSSL
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 8dbcb3d..09bd459 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -56,6 +56,16 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
+#include <private/qobject_p.h>
+#include <qauthenticator.h>
+#include <qnetworkproxy.h>
+#include <qbuffer.h>
+
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qhttpnetworkreply_p.h>
+
+
#ifndef QT_NO_HTTP
#ifndef QT_NO_OPENSSL
@@ -145,144 +155,137 @@ private:
#endif
};
-class Q_AUTOTEST_EXPORT QHttpNetworkHeader
-{
-public:
- virtual ~QHttpNetworkHeader() {};
- virtual QUrl url() const = 0;
- virtual void setUrl(const QUrl &url) = 0;
- virtual int majorVersion() const = 0;
- virtual int minorVersion() const = 0;
- virtual qint64 contentLength() const = 0;
- virtual void setContentLength(qint64 length) = 0;
- virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
- virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
- virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
-};
+// private classes
+typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
+
-class QHttpNetworkRequestPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader
+class QHttpNetworkConnectionPrivate : public QObjectPrivate
{
+ Q_DECLARE_PUBLIC(QHttpNetworkConnection)
public:
- enum Operation {
- Options,
- Get,
- Head,
- Post,
- Put,
- Delete,
- Trace,
- Connect
+ QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
+ ~QHttpNetworkConnectionPrivate();
+ void init();
+ void connectSignals(QAbstractSocket *socket);
+
+ enum SocketState {
+ IdleState = 0, // ready to send request
+ ConnectingState = 1, // connecting to host
+ WritingState = 2, // writing the data
+ WaitingState = 4, // waiting for reply
+ ReadingState = 8, // reading the reply
+ Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done
+ BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
};
- enum Priority {
- HighPriority,
- NormalPriority,
- LowPriority
+ enum { ChunkSize = 4096 };
+
+ int indexOf(QAbstractSocket *socket) const;
+ bool isSocketBusy(QAbstractSocket *socket) const;
+ bool isSocketWriting(QAbstractSocket *socket) const;
+ bool isSocketWaiting(QAbstractSocket *socket) const;
+ bool isSocketReading(QAbstractSocket *socket) const;
+
+ QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
+ void unqueueRequest(QAbstractSocket *socket);
+ void prepareRequest(HttpMessagePair &request);
+ bool sendRequest(QAbstractSocket *socket);
+ void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply);
+ void resendCurrentRequest(QAbstractSocket *socket);
+ void closeChannel(int channel);
+ void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
+
+ // private slots
+ void _q_bytesWritten(qint64 bytes); // proceed sending
+ void _q_readyRead(); // pending data to read
+ void _q_disconnected(); // disconnected from host
+ void _q_startNextRequest(); // send the next request from the queue
+ void _q_restartPendingRequest(); // send the currently blocked request
+ void _q_connected(); // start sending request
+ void _q_error(QAbstractSocket::SocketError); // error from socket
+#ifndef QT_NO_NETWORKPROXY
+ void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
+#endif
+ void _q_dataReadyReadNoBuffer();
+ void _q_dataReadyReadBuffer();
+
+ void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
+ bool ensureConnection(QAbstractSocket *socket);
+ QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket);
+ void eraseData(QHttpNetworkReply *reply);
+#ifndef QT_NO_COMPRESS
+ bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
+#endif
+ void bufferData(HttpMessagePair &request);
+ void removeReply(QHttpNetworkReply *reply);
+
+ QString hostName;
+ quint16 port;
+ bool encrypt;
+
+ struct Channel {
+ QAbstractSocket *socket;
+ SocketState state;
+ QHttpNetworkRequest request; // current request
+ QHttpNetworkReply *reply; // current reply for this request
+ qint64 written;
+ qint64 bytesTotal;
+ bool resendCurrent;
+ int lastStatus; // last status received on this channel
+ bool pendingEncrypt; // for https (send after encrypted)
+ int reconnectAttempts; // maximum 2 reconnection attempts
+ QAuthenticatorPrivate::Method authMehtod;
+ QAuthenticatorPrivate::Method proxyAuthMehtod;
+ QAuthenticator authenticator;
+ QAuthenticator proxyAuthenticator;
+#ifndef QT_NO_OPENSSL
+ bool ignoreSSLErrors;
+#endif
+ Channel() :state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), reconnectAttempts(2),
+ authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
+#ifndef QT_NO_OPENSSL
+ , ignoreSSLErrors(false)
+#endif
+ {}
};
-
- QHttpNetworkRequest(const QUrl &url = QUrl(), Operation operation = Get, Priority priority = NormalPriority);
- QHttpNetworkRequest(const QHttpNetworkRequest &other);
- virtual ~QHttpNetworkRequest();
- QHttpNetworkRequest &operator=(const QHttpNetworkRequest &other);
- bool operator==(const QHttpNetworkRequest &other) const;
-
- QUrl url() const;
- void setUrl(const QUrl &url);
-
- int majorVersion() const;
- int minorVersion() const;
-
- qint64 contentLength() const;
- void setContentLength(qint64 length);
-
- QList<QPair<QByteArray, QByteArray> > header() const;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- void setHeaderField(const QByteArray &name, const QByteArray &data);
-
- Operation operation() const;
- void setOperation(Operation operation);
-
- Priority priority() const;
- void setPriority(Priority priority);
-
- QIODevice *data() const;
- void setData(QIODevice *data);
-
-private:
- QSharedDataPointer<QHttpNetworkRequestPrivate> d;
- friend class QHttpNetworkRequestPrivate;
- friend class QHttpNetworkConnectionPrivate;
-};
-
-class QHttpNetworkReplyPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
-{
- Q_OBJECT
-public:
-
- explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0);
- virtual ~QHttpNetworkReply();
-
- QUrl url() const;
- void setUrl(const QUrl &url);
-
- int majorVersion() const;
- int minorVersion() const;
-
- qint64 contentLength() const;
- void setContentLength(qint64 length);
-
- QList<QPair<QByteArray, QByteArray> > header() const;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- void setHeaderField(const QByteArray &name, const QByteArray &data);
- void parseHeader(const QByteArray &header); // mainly for testing
-
- QHttpNetworkRequest request() const;
- void setRequest(const QHttpNetworkRequest &request);
-
- int statusCode() const;
- void setStatusCode(int code);
-
- QString errorString() const;
- void setErrorString(const QString &error);
-
- QString reasonPhrase() const;
-
- qint64 bytesAvailable() const;
- QByteArray read(qint64 maxSize = -1);
-
- bool isFinished() const;
+ static const int channelCount;
+ Channel channels[2]; // maximum of 2 socket connections to the server
+ bool pendingAuthSignal; // there is an incomplete authentication signal
+ bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
+
+ void appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed);
+ qint64 bytesAvailable(const QHttpNetworkReply &reply, bool compressed = false) const;
+ qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed);
+ void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
+ bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+ void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply);
+ void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply);
+ inline bool emitSignals(QHttpNetworkReply *reply);
+ inline bool expectContent(QHttpNetworkReply *reply);
#ifndef QT_NO_OPENSSL
- QSslConfiguration sslConfiguration() const;
- void setSslConfiguration(const QSslConfiguration &config);
- void ignoreSslErrors();
-
-Q_SIGNALS:
- void sslErrors(const QList<QSslError> &errors);
+ void _q_encrypted(); // start sending request (https)
+ void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
+ QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
#endif
-Q_SIGNALS:
- void readyRead();
- void finished();
- void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
- void headerChanged();
- void dataReadProgress(int done, int total);
- void dataSendProgress(int done, int total);
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy networkProxy;
+#endif
-private:
- Q_DECLARE_PRIVATE(QHttpNetworkReply)
- friend class QHttpNetworkConnection;
- friend class QHttpNetworkConnectionPrivate;
+ //The request queues
+ QList<HttpMessagePair> highPriorityQueue;
+ QList<HttpMessagePair> lowPriorityQueue;
};
+
+
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QHttpNetworkRequest)
+//Q_DECLARE_METATYPE(QHttpNetworkRequest)
//Q_DECLARE_METATYPE(QHttpNetworkReply)
#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp
new file mode 100644
index 0000000..6f7f6f7
--- /dev/null
+++ b/src/network/access/qhttpnetworkheader.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkheader_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
+ :url(newUrl)
+{
+}
+
+QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
+ :QSharedData(other)
+{
+ url = other.url;
+ fields = other.fields;
+}
+
+qint64 QHttpNetworkHeaderPrivate::contentLength() const
+{
+ bool ok = false;
+ QByteArray value = headerField("content-length");
+ qint64 length = value.toULongLong(&ok);
+ if (ok)
+ return length;
+ return -1; // the header field is not set
+}
+
+void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
+{
+ setHeaderField("Content-Length", QByteArray::number(length));
+}
+
+QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ QList<QByteArray> allValues = headerFieldValues(name);
+ if (allValues.isEmpty())
+ return defaultValue;
+
+ QByteArray result;
+ bool first = true;
+ foreach (QByteArray value, allValues) {
+ if (!first)
+ result += ", ";
+ first = false;
+ result += value;
+ }
+ return result;
+}
+
+QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
+{
+ QList<QByteArray> result;
+ QByteArray lowerName = name.toLower();
+ QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
+ end = fields.constEnd();
+ for ( ; it != end; ++it)
+ if (lowerName == it->first.toLower())
+ result += it->second;
+
+ return result;
+}
+
+void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ QByteArray lowerName = name.toLower();
+ QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin();
+ while (it != fields.end()) {
+ if (lowerName == it->first.toLower())
+ it = fields.erase(it);
+ else
+ ++it;
+ }
+ fields.append(qMakePair(name, data));
+}
+
+bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
+{
+ return (url == other.url);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
new file mode 100644
index 0000000..4e62352
--- /dev/null
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKHEADER_H
+#define QHTTPNETWORKHEADER_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.
+//
+#ifndef QT_NO_HTTP
+
+#include <qshareddata.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QHttpNetworkHeader
+{
+public:
+ virtual ~QHttpNetworkHeader() {};
+ virtual QUrl url() const = 0;
+ virtual void setUrl(const QUrl &url) = 0;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+ virtual qint64 contentLength() const = 0;
+ virtual void setContentLength(qint64 length) = 0;
+
+ virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
+ virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
+ virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
+};
+
+class QHttpNetworkHeaderPrivate : public QSharedData
+{
+public:
+ QUrl url;
+ QList<QPair<QByteArray, QByteArray> > fields;
+
+ QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
+ QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+ bool operator==(const QHttpNetworkHeaderPrivate &other) const;
+
+};
+
+
+QT_END_NAMESPACE
+
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKHEADER_H
+
+
+
+
+
+
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
new file mode 100644
index 0000000..fe3f6af
--- /dev/null
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -0,0 +1,663 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkreply_p.h"
+#include "qhttpnetworkconnection_p.h"
+
+#include <qbytearraymatcher.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslkey.h>
+# include <QtNetwork/qsslcipher.h>
+# include <QtNetwork/qsslconfiguration.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
+ : QObject(*new QHttpNetworkReplyPrivate(url), parent)
+{
+}
+
+QHttpNetworkReply::~QHttpNetworkReply()
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection) {
+ d->connection->d_func()->removeReply(this);
+ }
+}
+
+QUrl QHttpNetworkReply::url() const
+{
+ return d_func()->url;
+}
+void QHttpNetworkReply::setUrl(const QUrl &url)
+{
+ Q_D(QHttpNetworkReply);
+ d->url = url;
+}
+
+qint64 QHttpNetworkReply::contentLength() const
+{
+ return d_func()->contentLength();
+}
+
+void QHttpNetworkReply::setContentLength(qint64 length)
+{
+ Q_D(QHttpNetworkReply);
+ d->setContentLength(length);
+}
+
+QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
+{
+ return d_func()->fields;
+}
+
+QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ return d_func()->headerField(name, defaultValue);
+}
+
+void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ Q_D(QHttpNetworkReply);
+ d->setHeaderField(name, data);
+}
+
+void QHttpNetworkReply::parseHeader(const QByteArray &header)
+{
+ Q_D(QHttpNetworkReply);
+ d->parseHeader(header);
+}
+
+QHttpNetworkRequest QHttpNetworkReply::request() const
+{
+ return d_func()->request;
+}
+
+void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
+{
+ Q_D(QHttpNetworkReply);
+ d->request = request;
+}
+
+int QHttpNetworkReply::statusCode() const
+{
+ return d_func()->statusCode;
+}
+
+void QHttpNetworkReply::setStatusCode(int code)
+{
+ Q_D(QHttpNetworkReply);
+ d->statusCode = code;
+}
+
+QString QHttpNetworkReply::errorString() const
+{
+ return d_func()->errorString;
+}
+
+QString QHttpNetworkReply::reasonPhrase() const
+{
+ return d_func()->reasonPhrase;
+}
+
+void QHttpNetworkReply::setErrorString(const QString &error)
+{
+ Q_D(QHttpNetworkReply);
+ d->errorString = error;
+}
+
+int QHttpNetworkReply::majorVersion() const
+{
+ return d_func()->majorVersion;
+}
+
+int QHttpNetworkReply::minorVersion() const
+{
+ return d_func()->minorVersion;
+}
+
+qint64 QHttpNetworkReply::bytesAvailable() const
+{
+ Q_D(const QHttpNetworkReply);
+ if (d->connection)
+ return d->connection->d_func()->bytesAvailable(*this);
+ else
+ return -1;
+}
+
+QByteArray QHttpNetworkReply::read(qint64 maxSize)
+{
+ Q_D(QHttpNetworkReply);
+ QByteArray data;
+ if (d->connection)
+ d->connection->d_func()->read(*this, data, maxSize, false);
+ return data;
+}
+
+bool QHttpNetworkReply::isFinished() const
+{
+ return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
+}
+
+
+
+QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
+ : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
+ majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
+ currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
+ autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false)
+{
+}
+
+QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
+{
+}
+
+void QHttpNetworkReplyPrivate::clear()
+{
+ state = NothingDoneState;
+ statusCode = 100;
+ bodyLength = 0;
+ contentRead = 0;
+ totalProgress = 0;
+ currentChunkSize = 0;
+ currentChunkRead = 0;
+ connection = 0;
+#ifndef QT_NO_COMPRESS
+ if (initInflate)
+ inflateEnd(&inflateStrm);
+#endif
+ initInflate = false;
+ streamEnd = false;
+ autoDecompress = false;
+ fields.clear();
+}
+
+// QHttpNetworkReplyPrivate
+qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
+{
+ return (state != ReadingDataState ? 0 : fragment.size());
+}
+
+bool QHttpNetworkReplyPrivate::isGzipped()
+{
+ QByteArray encoding = headerField("content-encoding");
+ return encoding.toLower() == "gzip";
+}
+
+void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
+{
+ // The header "Content-Encoding = gzip" is retained.
+ // Content-Length is removed since the actual one send by the server is for compressed data
+ QByteArray name("content-length");
+ QByteArray lowerName = name.toLower();
+ QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
+ end = fields.end();
+ while (it != end) {
+ if (name == it->first.toLower()) {
+ fields.erase(it);
+ break;
+ }
+ ++it;
+ }
+
+}
+
+bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
+{
+ challenge.clear();
+ // find out the type of authentication protocol requested.
+ QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
+ // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
+ QList<QByteArray> challenges = headerFieldValues(header);
+ for (int i = 0; i<challenges.size(); i++) {
+ QByteArray line = challenges.at(i);
+ if (!line.toLower().startsWith("negotiate"))
+ challenge = line;
+ }
+ return !challenge.isEmpty();
+}
+
+QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
+{
+ // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
+ QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
+ QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
+ QList<QByteArray> challenges = headerFieldValues(header);
+ for (int i = 0; i<challenges.size(); i++) {
+ QByteArray line = challenges.at(i).trimmed().toLower();
+ if (method < QAuthenticatorPrivate::Basic
+ && line.startsWith("basic")) {
+ method = QAuthenticatorPrivate::Basic;
+ } else if (method < QAuthenticatorPrivate::Ntlm
+ && line.startsWith("ntlm")) {
+ method = QAuthenticatorPrivate::Ntlm;
+ } else if (method < QAuthenticatorPrivate::DigestMd5
+ && line.startsWith("digest")) {
+ method = QAuthenticatorPrivate::DigestMd5;
+ }
+ }
+ return method;
+}
+
+#ifndef QT_NO_COMPRESS
+bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
+{
+ int method = 0; // method byte
+ int flags = 0; // flags byte
+ bool ret = false;
+
+ // Assure two bytes in the buffer so we can peek ahead -- handle case
+ // where first byte of header is at the end of the buffer after the last
+ // gzip segment
+ pos = -1;
+ QByteArray &body = content;
+ int maxPos = body.size()-1;
+ if (maxPos < 1) {
+ return ret;
+ }
+
+ // Peek ahead to check the gzip magic header
+ if (body[0] != char(gz_magic[0]) ||
+ body[1] != char(gz_magic[1])) {
+ return ret;
+ }
+ pos += 2;
+ // Check the rest of the gzip header
+ if (++pos <= maxPos)
+ method = body[pos];
+ if (pos++ <= maxPos)
+ flags = body[pos];
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ return ret;
+ }
+
+ // Discard time, xflags and OS code:
+ pos += 6;
+ if (pos > maxPos)
+ return ret;
+ if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
+ unsigned len = (unsigned)body[++pos];
+ len += ((unsigned)body[++pos])<<8;
+ pos += len;
+ if (pos > maxPos)
+ return ret;
+ }
+ if ((flags & ORIG_NAME) != 0) { // skip the original file name
+ while(++pos <= maxPos && body[pos]) {}
+ }
+ if ((flags & COMMENT) != 0) { // skip the .gz file comment
+ while(++pos <= maxPos && body[pos]) {}
+ }
+ if ((flags & HEAD_CRC) != 0) { // skip the header crc
+ pos += 2;
+ if (pos > maxPos)
+ return ret;
+ }
+ ret = (pos < maxPos); // return failed, if no more bytes left
+ return ret;
+}
+
+int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
+{
+ int ret = Z_DATA_ERROR;
+ unsigned have;
+ unsigned char out[CHUNK];
+ int pos = -1;
+
+ if (!initInflate) {
+ // check the header
+ if (!gzipCheckHeader(compressed, pos))
+ return ret;
+ // allocate inflate state
+ inflateStrm.zalloc = Z_NULL;
+ inflateStrm.zfree = Z_NULL;
+ inflateStrm.opaque = Z_NULL;
+ inflateStrm.avail_in = 0;
+ inflateStrm.next_in = Z_NULL;
+ ret = inflateInit2(&inflateStrm, -MAX_WBITS);
+ if (ret != Z_OK)
+ return ret;
+ initInflate = true;
+ streamEnd = false;
+ }
+
+ //remove the header.
+ compressed.remove(0, pos+1);
+ // expand until deflate stream ends
+ inflateStrm.next_in = (unsigned char *)compressed.data();
+ inflateStrm.avail_in = compressed.size();
+ do {
+ inflateStrm.avail_out = sizeof(out);
+ inflateStrm.next_out = out;
+ ret = inflate(&inflateStrm, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ // and fall through
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&inflateStrm);
+ initInflate = false;
+ return ret;
+ }
+ have = sizeof(out) - inflateStrm.avail_out;
+ inflated.append(QByteArray((const char *)out, have));
+ } while (inflateStrm.avail_out == 0);
+ // clean up and return
+ if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
+ inflateEnd(&inflateStrm);
+ initInflate = false;
+ }
+ streamEnd = (ret == Z_STREAM_END);
+ return ret;
+}
+#endif
+
+qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
+{
+ qint64 bytes = 0;
+ char c;
+
+ while (socket->bytesAvailable()) {
+ // allow both CRLF & LF (only) line endings
+ if (socket->peek(&c, 1) == 1 && c == '\n') {
+ bytes += socket->read(&c, 1); // read the "n"
+ // remove the CR at the end
+ if (fragment.endsWith('\r')) {
+ fragment.truncate(fragment.length()-1);
+ }
+ parseStatus(fragment);
+ state = ReadingHeaderState;
+ fragment.clear(); // next fragment
+ break;
+ } else {
+ c = 0;
+ bytes += socket->read(&c, 1);
+ fragment.append(c);
+ }
+ }
+ return bytes;
+}
+
+void QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
+{
+ const QByteArrayMatcher sp(" ");
+ int i = sp.indexIn(status);
+ const QByteArray version = status.mid(0, i);
+ int j = sp.indexIn(status, i + 1);
+ const QByteArray code = status.mid(i + 1, j - i - 1);
+ const QByteArray reason = status.mid(j + 1, status.count() - j);
+
+ const QByteArrayMatcher slash("/");
+ int k = slash.indexIn(version);
+ const QByteArrayMatcher dot(".");
+ int l = dot.indexIn(version, k);
+ const QByteArray major = version.mid(k + 1, l - k - 1);
+ const QByteArray minor = version.mid(l + 1, version.count() - l);
+
+ majorVersion = QString::fromAscii(major.constData()).toInt();
+ minorVersion = QString::fromAscii(minor.constData()).toInt();
+ statusCode = QString::fromAscii(code.constData()).toInt();
+ reasonPhrase = QString::fromAscii(reason.constData());
+}
+
+qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
+{
+ qint64 bytes = 0;
+ char crlfcrlf[5];
+ crlfcrlf[4] = '\0';
+ char c = 0;
+ bool allHeaders = false;
+ while (!allHeaders && socket->bytesAvailable()) {
+ if (socket->peek(&c, 1) == 1 && c == '\n') {
+ // check for possible header endings. As per HTTP rfc,
+ // the header endings will be marked by CRLFCRLF. But
+ // we will allow CRLFLF, LFLF & CRLFCRLF
+ if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
+ allHeaders = true;
+ }
+ bytes += socket->read(&c, 1);
+ fragment.append(c);
+ }
+ // we received all headers now parse them
+ if (allHeaders) {
+ parseHeader(fragment);
+ state = ReadingDataState;
+ fragment.clear(); // next fragment
+ bodyLength = contentLength(); // cache the length
+ }
+ return bytes;
+}
+
+void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
+{
+ // see rfc2616, sec 4 for information about HTTP/1.1 headers.
+ // allows relaxed parsing here, accepts both CRLF & LF line endings
+ const QByteArrayMatcher lf("\n");
+ const QByteArrayMatcher colon(":");
+ int i = 0;
+ while (i < header.count()) {
+ int j = colon.indexIn(header, i); // field-name
+ if (j == -1)
+ break;
+ const QByteArray field = header.mid(i, j - i).trimmed();
+ j++;
+ // any number of LWS is allowed before and after the value
+ QByteArray value;
+ do {
+ i = lf.indexIn(header, j);
+ if (i == -1)
+ break;
+ if (!value.isEmpty())
+ value += ' ';
+ // check if we have CRLF or only LF
+ bool hasCR = (i && header[i-1] == '\r');
+ int length = i -(hasCR ? 1: 0) - j;
+ value += header.mid(j, length).trimmed();
+ j = ++i;
+ } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
+ if (i == -1)
+ break; // something is wrong
+
+ fields.append(qMakePair(field, value));
+ }
+}
+
+bool QHttpNetworkReplyPrivate::isChunked()
+{
+ return headerField("transfer-encoding").toLower().contains("chunked");
+}
+
+bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
+{
+ return (headerField("connection").toLower().contains("close") ||
+ headerField("proxy-connection").toLower().contains("close"));
+}
+
+qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
+{
+ qint64 bytes = 0;
+ if (isChunked()) {
+ bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
+ } else if (bodyLength > 0) { // we have a Content-Length
+ bytes += transferRaw(socket, out, bodyLength - contentRead);
+ if (contentRead + bytes == bodyLength)
+ state = AllDoneState;
+ } else {
+ bytes += transferRaw(socket, out, socket->bytesAvailable());
+ }
+ if (state == AllDoneState)
+ socket->readAll(); // Read the rest to clean (CRLF)
+ contentRead += bytes;
+ return bytes;
+}
+
+qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size)
+{
+ qint64 bytes = 0;
+ Q_ASSERT(in);
+ Q_ASSERT(out);
+
+ int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ QByteArray raw(toBeRead, 0);
+ while (size > 0) {
+ qint64 read = in->read(raw.data(), raw.size());
+ if (read == 0)
+ return bytes;
+ // ### error checking here
+ qint64 written = out->write(raw.data(), read);
+ if (written == 0)
+ return bytes;
+ if (read != written)
+ qDebug() << "### read" << read << "written" << written;
+ bytes += read;
+ size -= read;
+ out->waitForBytesWritten(-1); // throttle
+ }
+ return bytes;
+
+}
+
+qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *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
+ if (currentChunkRead >= currentChunkSize) {
+ currentChunkSize = 0;
+ currentChunkRead = 0;
+ if (bytes) {
+ char crlf[2];
+ bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
+ }
+ bytes += getChunkSize(in, &currentChunkSize);
+ if (currentChunkSize == -1)
+ break;
+ }
+ // if the chunk size is 0, end of the stream
+ if (currentChunkSize == 0) {
+ state = AllDoneState;
+ break;
+ }
+ // otherwise, read data
+ qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
+ QByteArray buffer(readSize, 0);
+ qint64 read = in->read(buffer.data(), readSize);
+ bytes += read;
+ currentChunkRead += read;
+ qint64 written = out->write(buffer);
+ Q_UNUSED(written); // Avoid compile warning when building release
+ Q_ASSERT(read == written);
+ // ### error checking here
+ out->waitForBytesWritten(-1);
+ }
+ return bytes;
+}
+
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
+{
+ qint64 bytes = 0;
+ char crlf[2];
+ *chunkSize = -1;
+ int bytesAvailable = in->bytesAvailable();
+ while (bytesAvailable > bytes) {
+ qint64 sniffedBytes = in->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
+ if (crlf[0] == '\r')
+ bytes += in->read(crlf, 1); // read the \n
+ bool ok = false;
+ // ignore the chunk-extension
+ fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
+ *chunkSize = fragment.toLong(&ok, 16);
+ fragment.clear();
+ break; // size done
+ } else {
+ // read the fragment to the buffer
+ char c = 0;
+ bytes += in->read(&c, 1);
+ fragment.append(c);
+ }
+ }
+ return bytes;
+}
+
+// SSL support below
+#ifndef QT_NO_OPENSSL
+
+QSslConfiguration QHttpNetworkReply::sslConfiguration() const
+{
+ Q_D(const QHttpNetworkReply);
+ if (d->connection)
+ return d->connection->d_func()->sslConfiguration(*this);
+ return QSslConfiguration();
+}
+
+void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->setSslConfiguration(config);
+}
+
+void QHttpNetworkReply::ignoreSslErrors()
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->ignoreSslErrors();
+}
+
+
+#endif //QT_NO_OPENSSL
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP \ No newline at end of file
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
new file mode 100644
index 0000000..c17c65c
--- /dev/null
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKREPLY_H
+#define QHTTPNETWORKREPLY_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.
+//
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_COMPRESS
+# include <zlib.h>
+static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
+// gzip flag byte
+#define HEAD_CRC 0x02 // bit 1 set: header CRC present
+#define EXTRA_FIELD 0x04 // bit 2 set: extra field present
+#define ORIG_NAME 0x08 // bit 3 set: original file name present
+#define COMMENT 0x10 // bit 4 set: file comment present
+#define RESERVED 0xE0 // bits 5..7: reserved
+#define CHUNK 16384
+#endif
+
+#include <QtNetwork/qtcpsocket.h>
+// it's safe to include these even if SSL support is not enabled
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qsslerror.h>
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <qbuffer.h>
+
+#include <private/qobject_p.h>
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qauthenticator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkConnection;
+class QHttpNetworkRequest;
+class QHttpNetworkConnectionPrivate;
+class QHttpNetworkReplyPrivate;
+class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
+{
+ Q_OBJECT
+public:
+
+ explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0);
+ virtual ~QHttpNetworkReply();
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QList<QPair<QByteArray, QByteArray> > header() const;
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+ void parseHeader(const QByteArray &header); // mainly for testing
+
+ QHttpNetworkRequest request() const;
+ void setRequest(const QHttpNetworkRequest &request);
+
+ int statusCode() const;
+ void setStatusCode(int code);
+
+ QString errorString() const;
+ void setErrorString(const QString &error);
+
+ QString reasonPhrase() const;
+
+ qint64 bytesAvailable() const;
+ QByteArray read(qint64 maxSize = -1);
+
+ bool isFinished() const;
+
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration sslConfiguration() const;
+ void setSslConfiguration(const QSslConfiguration &config);
+ void ignoreSslErrors();
+
+Q_SIGNALS:
+ void sslErrors(const QList<QSslError> &errors);
+#endif
+
+Q_SIGNALS:
+ void readyRead();
+ void finished();
+ void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
+ void headerChanged();
+ void dataReadProgress(int done, int total);
+ void dataSendProgress(int done, int total);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpNetworkReply)
+ friend class QHttpNetworkConnection;
+ friend class QHttpNetworkConnectionPrivate;
+};
+
+
+class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
+{
+public:
+ QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
+ ~QHttpNetworkReplyPrivate();
+ qint64 readStatus(QAbstractSocket *socket);
+ void parseStatus(const QByteArray &status);
+ qint64 readHeader(QAbstractSocket *socket);
+ void parseHeader(const QByteArray &header);
+ qint64 readBody(QAbstractSocket *socket, QIODevice *out);
+ bool findChallenge(bool forProxy, QByteArray &challenge) const;
+ QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
+ void clear();
+
+ qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size);
+ qint64 transferChunked(QIODevice *in, QIODevice *out);
+ qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
+
+ qint64 bytesAvailable() const;
+ bool isChunked();
+ bool connectionCloseEnabled();
+ bool isGzipped();
+#ifndef QT_NO_COMPRESS
+ bool gzipCheckHeader(QByteArray &content, int &pos);
+ int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated);
+#endif
+ void removeAutoDecompressHeader();
+
+ enum ReplyState {
+ NothingDoneState,
+ ReadingStatusState,
+ ReadingHeaderState,
+ ReadingDataState,
+ AllDoneState
+ } state;
+
+ QHttpNetworkRequest request;
+ int statusCode;
+ int majorVersion;
+ int minorVersion;
+ QString errorString;
+ QString reasonPhrase;
+ qint64 bodyLength;
+ qint64 contentRead;
+ qint64 totalProgress;
+ QByteArray fragment;
+ qint64 currentChunkSize;
+ qint64 currentChunkRead;
+ QPointer<QHttpNetworkConnection> connection;
+ bool initInflate;
+ bool streamEnd;
+#ifndef QT_NO_COMPRESS
+ z_stream inflateStrm;
+#endif
+ bool autoDecompress;
+
+ QByteArray responseData; // uncompressed body
+ QByteArray compressedData; // compressed body (temporary)
+ QBuffer requestDataBuffer;
+ bool requestIsBuffering;
+ bool requestIsPrepared;
+};
+
+
+
+
+QT_END_NAMESPACE
+
+//Q_DECLARE_METATYPE(QHttpNetworkReply)
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKREPLY_H
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
new file mode 100644
index 0000000..420cb69
--- /dev/null
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkrequest_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
+ QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
+ : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0),
+ autoDecompress(false)
+{
+}
+
+QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
+ : QHttpNetworkHeaderPrivate(other)
+{
+ operation = other.operation;
+ priority = other.priority;
+ data = other.data;
+ autoDecompress = other.autoDecompress;
+}
+
+QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
+{
+}
+
+bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
+{
+ return QHttpNetworkHeaderPrivate::operator==(other)
+ && (operation == other.operation)
+ && (data == other.data);
+}
+
+QByteArray QHttpNetworkRequestPrivate::methodName() const
+{
+ QByteArray ba;
+ switch (operation) {
+ case QHttpNetworkRequest::Options:
+ ba += "OPTIONS";
+ break;
+ case QHttpNetworkRequest::Get:
+ ba += "GET";
+ break;
+ case QHttpNetworkRequest::Head:
+ ba += "HEAD";
+ break;
+ case QHttpNetworkRequest::Post:
+ ba += "POST";
+ break;
+ case QHttpNetworkRequest::Put:
+ ba += "PUT";
+ break;
+ case QHttpNetworkRequest::Delete:
+ ba += "DELETE";
+ break;
+ case QHttpNetworkRequest::Trace:
+ ba += "TRACE";
+ break;
+ case QHttpNetworkRequest::Connect:
+ ba += "CONNECT";
+ break;
+ default:
+ break;
+ }
+ return ba;
+}
+
+QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
+{
+ QUrl::FormattingOptions format(QUrl::RemoveFragment);
+
+ // for POST, query data is send as content
+ if (operation == QHttpNetworkRequest::Post && !data)
+ format |= QUrl::RemoveQuery;
+ // for requests through proxy, the Request-URI contains full url
+ if (throughProxy)
+ format |= QUrl::RemoveUserInfo;
+ else
+ format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
+ QByteArray uri = url.toEncoded(format);
+ if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
+ uri += '/';
+ return uri;
+}
+
+QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
+{
+ QByteArray ba = request.d->methodName();
+ QByteArray uri = request.d->uri(throughProxy);
+ ba += " " + uri;
+
+ QString majorVersion = QString::number(request.majorVersion());
+ QString minorVersion = QString::number(request.minorVersion());
+ ba += " HTTP/" + majorVersion.toLatin1() + "." + minorVersion.toLatin1() + "\r\n";
+
+ QList<QPair<QByteArray, QByteArray> > fields = request.header();
+ QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
+ for (; it != fields.constEnd(); ++it)
+ ba += it->first + ": " + it->second + "\r\n";
+ 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.d->data && request.d->url.hasQuery()) {
+ QByteArray query = request.d->url.encodedQuery();
+ ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n";
+ ba += "\r\n";
+ ba += query;
+ } else {
+ ba += "\r\n";
+ }
+ } else {
+ ba += "\r\n";
+ }
+ return ba;
+}
+
+
+// QHttpNetworkRequest
+
+QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
+ : d(new QHttpNetworkRequestPrivate(operation, priority, url))
+{
+}
+
+QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
+ : QHttpNetworkHeader(other), d(other.d)
+{
+}
+
+QHttpNetworkRequest::~QHttpNetworkRequest()
+{
+}
+
+QUrl QHttpNetworkRequest::url() const
+{
+ return d->url;
+}
+void QHttpNetworkRequest::setUrl(const QUrl &url)
+{
+ d->url = url;
+}
+
+qint64 QHttpNetworkRequest::contentLength() const
+{
+ return d->contentLength();
+}
+
+void QHttpNetworkRequest::setContentLength(qint64 length)
+{
+ d->setContentLength(length);
+}
+
+QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
+{
+ return d->fields;
+}
+
+QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ return d->headerField(name, defaultValue);
+}
+
+void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ d->setHeaderField(name, data);
+}
+
+QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
+{
+ d = other.d;
+ return *this;
+}
+
+bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
+{
+ return d->operator==(*other.d);
+}
+
+QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
+{
+ return d->operation;
+}
+
+void QHttpNetworkRequest::setOperation(Operation operation)
+{
+ d->operation = operation;
+}
+
+QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
+{
+ return d->priority;
+}
+
+void QHttpNetworkRequest::setPriority(Priority priority)
+{
+ d->priority = priority;
+}
+
+QIODevice *QHttpNetworkRequest::data() const
+{
+ return d->data;
+}
+
+void QHttpNetworkRequest::setData(QIODevice *data)
+{
+ d->data = data;
+}
+
+int QHttpNetworkRequest::majorVersion() const
+{
+ return 1;
+}
+
+int QHttpNetworkRequest::minorVersion() const
+{
+ return 1;
+}
+
+
+QT_END_NAMESPACE
+
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
new file mode 100644
index 0000000..d18e116
--- /dev/null
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKREQUEST_H
+#define QHTTPNETWORKREQUEST_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.
+//
+#ifndef QT_NO_HTTP
+
+#include <private/qhttpnetworkheader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkRequestPrivate;
+class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader
+{
+public:
+ enum Operation {
+ Options,
+ Get,
+ Head,
+ Post,
+ Put,
+ Delete,
+ Trace,
+ Connect
+ };
+
+ enum Priority {
+ HighPriority,
+ NormalPriority,
+ LowPriority
+ };
+
+ QHttpNetworkRequest(const QUrl &url = QUrl(), Operation operation = Get, Priority priority = NormalPriority);
+ QHttpNetworkRequest(const QHttpNetworkRequest &other);
+ virtual ~QHttpNetworkRequest();
+ QHttpNetworkRequest &operator=(const QHttpNetworkRequest &other);
+ bool operator==(const QHttpNetworkRequest &other) const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QList<QPair<QByteArray, QByteArray> > header() const;
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+
+ Operation operation() const;
+ void setOperation(Operation operation);
+
+ Priority priority() const;
+ void setPriority(Priority priority);
+
+ QIODevice *data() const;
+ void setData(QIODevice *data);
+
+private:
+ QSharedDataPointer<QHttpNetworkRequestPrivate> d;
+ friend class QHttpNetworkRequestPrivate;
+ friend class QHttpNetworkConnectionPrivate;
+};
+
+
+class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
+{
+public:
+ QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
+ QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
+ QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
+ ~QHttpNetworkRequestPrivate();
+ bool operator==(const QHttpNetworkRequestPrivate &other) const;
+ QByteArray methodName() const;
+ QByteArray uri(bool throughProxy) const;
+
+ static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
+
+ QHttpNetworkRequest::Operation operation;
+ QHttpNetworkRequest::Priority priority;
+ mutable QIODevice *data;
+ bool autoDecompress;
+};
+
+
+QT_END_NAMESPACE
+
+//Q_DECLARE_METATYPE(QHttpNetworkRequest)
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKREQUEST_H
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index 4b41aa7..a52b5a0 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -527,8 +527,10 @@ void QNetworkAccessHttpBackend::postRequest()
foreach (const QByteArray &header, headers)
httpRequest.setHeaderField(header, request().rawHeader(header));
- if (loadedFromCache)
+ if (loadedFromCache) {
+ QNetworkAccessBackend::finished();
return; // no need to send the request! :)
+ }
httpReply = http->sendRequest(httpRequest);
httpReply->setParent(this);
@@ -767,8 +769,12 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
for (; it != end; ++it) {
QByteArray value = rawHeader(it->first);
- if (!value.isEmpty())
- value += ", ";
+ if (!value.isEmpty()) {
+ if (it->first.toLower() == "set-cookie")
+ value += "\n";
+ else
+ value += ", ";
+ }
value += it->second;
setRawHeader(it->first, value);
}
@@ -886,8 +892,6 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
if (status < 100)
status = 200; // fake it
- checkForRedirect(status);
-
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
@@ -898,6 +902,8 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
for ( ; it != end; ++it)
setRawHeader(it->first, it->second);
+ checkForRedirect(status);
+
writeDownstreamData(contents);
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes";
@@ -951,6 +957,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
QList<QByteArray> newHeaders = rawHeaderList();
foreach (QByteArray header, newHeaders) {
+ QByteArray originalHeader = header;
header = header.toLower();
bool hop_by_hop =
(header == "connection"
@@ -974,19 +981,32 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
continue;
}
+ it = cacheHeaders.findRawHeader(header);
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ // Match the behavior of Firefox and assume Cache-Control: "no-transform"
+ if (header == "content-encoding"
+ || header == "content-range"
+ || header == "content-type")
+ continue;
+
+ // For MS servers that send "Content-Length: 0" on 304 responses
+ // ignore this too
+ if (header == "content-length")
+ continue;
+ }
+
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
QByteArray n = rawHeader(header);
QByteArray o;
- it = cacheHeaders.findRawHeader(header);
if (it != cacheHeaders.rawHeaders.constEnd())
o = (*it).second;
- if (n != o && header != "Date") {
+ if (n != o && header != "date") {
qDebug() << "replacing" << header;
qDebug() << "new" << n;
qDebug() << "old" << o;
}
#endif
- cacheHeaders.setRawHeader(header, rawHeader(header));
+ cacheHeaders.setRawHeader(originalHeader, rawHeader(header));
}
metaData.setRawHeaders(cacheHeaders.rawHeaders);
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 54497c3..c8d6097 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -49,6 +49,7 @@
#include "QtCore/qlist.h"
#include "QtCore/qlocale.h"
#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
#include "QtCore/qurl.h"
#include "private/qobject_p.h"
@@ -200,7 +201,7 @@ bool QNetworkCookie::operator==(const QNetworkCookie &other) const
return true;
return d->name == other.d->name &&
d->value == other.d->value &&
- d->expirationDate == other.d->expirationDate &&
+ d->expirationDate.toUTC() == other.d->expirationDate.toUTC() &&
d->domain == other.d->domain &&
d->path == other.d->path &&
d->secure == other.d->secure &&
@@ -550,6 +551,392 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
return result;
}
+static const char zones[] =
+ "pst\0" // -8
+ "pdt\0"
+ "mst\0" // -7
+ "mdt\0"
+ "cst\0" // -6
+ "cdt\0"
+ "est\0" // -5
+ "edt\0"
+ "ast\0" // -4
+ "nst\0" // -3
+ "gmt\0" // 0
+ "utc\0"
+ "bst\0"
+ "met\0" // 1
+ "eet\0" // 2
+ "jst\0" // 9
+ "\0";
+static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
+
+static const char months[] =
+ "jan\0"
+ "feb\0"
+ "mar\0"
+ "apr\0"
+ "may\0"
+ "jun\0"
+ "jul\0"
+ "aug\0"
+ "sep\0"
+ "oct\0"
+ "nov\0"
+ "dec\0"
+ "\0";
+
+static inline bool isNumber(char s)
+{ return s >= '0' && s <= '9'; }
+
+static inline bool isTerminator(char c)
+{ return c == '\n' || c == '\r'; }
+
+static inline bool isValueSeparator(char c)
+{ return isTerminator(c) || c == ';'; }
+
+static inline bool isWhitespace(char c)
+{ return c == ' ' || c == '\t'; }
+
+static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
+{
+ if (dateString[at] < 'a' || dateString[at] > 'z')
+ return false;
+ if (val == -1 && dateString.length() >= at + 3) {
+ int j = 0;
+ int i = 0;
+ while (i <= size) {
+ const char *str = array + i;
+ if (str[0] == dateString[at]
+ && str[1] == dateString[at + 1]
+ && str[2] == dateString[at + 2]) {
+ val = j;
+ return true;
+ }
+ i += strlen(str) + 1;
+ ++j;
+ }
+ }
+ return false;
+}
+
+//#define PARSEDATESTRINGDEBUG
+
+#define ADAY 1
+#define AMONTH 2
+#define AYEAR 4
+
+/*
+ Parse all the date formats that Firefox can.
+
+ The official format is:
+ expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
+
+ But browsers have been supporting a very wide range of date
+ strings. To work on many sites we need to support more then
+ just the official date format.
+
+ For reference see Firefox's PR_ParseTimeStringToExplodedTime in
+ prtime.c. The Firefox date parser is coded in a very complex way
+ and is slightly over ~700 lines long. While this implementation
+ will be slightly slower for the non standard dates it is smaller,
+ more readable, and maintainable.
+
+ Or in their own words:
+ "} // else what the hell is this."
+*/
+static QDateTime parseDateString(const QByteArray &dateString)
+{
+ QTime time;
+ // placeholders for values when we are not sure it is a year, month or day
+ int unknown[3] = {-1, -1, -1};
+ int month = -1;
+ int day = -1;
+ int year = -1;
+ int zoneOffset = -1;
+
+ // hour:minute:second.ms pm
+ QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
+
+ int at = 0;
+ while (at < dateString.length()) {
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << dateString.mid(at);
+#endif
+ bool isNum = isNumber(dateString[at]);
+
+ // Month
+ if (!isNum
+ && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
+ ++month;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Month:" << month;
+#endif
+ at += 3;
+ continue;
+ }
+ // Zone
+ if (!isNum
+ && zoneOffset == -1
+ && checkStaticArray(zoneOffset, dateString, at, zones, sizeof(zones)- 1)) {
+ int sign = (at >= 0 && dateString[at - 1] == '-') ? -1 : 1;
+ zoneOffset = sign * zoneOffsets[zoneOffset] * 60 * 60;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Zone:" << month;
+#endif
+ at += 3;
+ continue;
+ }
+ // Zone offset
+ if (!isNum
+ && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
+ && (dateString[at] == '+' || dateString[at] == '-')
+ && (at == 0
+ || isWhitespace(dateString[at - 1])
+ || dateString[at - 1] == ','
+ || (at >= 3
+ && (dateString[at - 3] == 'g')
+ && (dateString[at - 2] == 'm')
+ && (dateString[at - 1] == 't')))) {
+
+ int end = 1;
+ while (end < 5 && dateString.length() > at+end
+ && dateString[at + end] >= '0' && dateString[at + end] <= '9')
+ ++end;
+ int minutes = 0;
+ int hours = 0;
+ switch (end - 1) {
+ case 4:
+ minutes = atoi(dateString.mid(at + 3, 2).constData());
+ // fall through
+ case 2:
+ hours = atoi(dateString.mid(at + 1, 2).constData());
+ break;
+ case 1:
+ hours = atoi(dateString.mid(at + 1, 1).constData());
+ break;
+ default:
+ at += end;
+ continue;
+ }
+ if (end != 1) {
+ int sign = dateString[at] == '-' ? -1 : 1;
+ zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Zone offset:" << zoneOffset << hours << minutes;
+#endif
+ at += end;
+ continue;
+ }
+ }
+
+ // Time
+ if (isNum && time.isNull()
+ && dateString.length() >= at + 3
+ && (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
+ // While the date can be found all over the string the format
+ // for the time is set and a nice regexp can be used.
+ int pos = timeRx.indexIn(QLatin1String(dateString), at);
+ if (pos != -1) {
+ QStringList list = timeRx.capturedTexts();
+ int h = atoi(list.at(1).toLatin1().constData());
+ int m = atoi(list.at(2).toLatin1().constData());
+ int s = atoi(list.at(4).toLatin1().constData());
+ int ms = atoi(list.at(6).toLatin1().constData());
+ if (h < 12 && !list.at(9).isEmpty())
+ if (list.at(9) == QLatin1String("pm"))
+ h += 12;
+ time = QTime(h, m, s, ms);
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Time:" << list << timeRx.matchedLength();
+#endif
+ at += timeRx.matchedLength();
+ continue;
+ }
+ }
+
+ // 4 digit Year
+ if (isNum
+ && year == -1
+ && dateString.length() >= at + 3) {
+ if (isNumber(dateString[at + 1])
+ && isNumber(dateString[at + 2])
+ && isNumber(dateString[at + 3])) {
+ year = atoi(dateString.mid(at, 4).constData());
+ at += 4;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Year:" << year;
+#endif
+ continue;
+ }
+ }
+
+ // a one or two digit number
+ // Could be month, day or year
+ if (isNum) {
+ int length = 1;
+ if (dateString.length() > at + 1
+ && isNumber(dateString[at + 1]))
+ ++length;
+ int x = atoi(dateString.mid(at, length).constData());
+ if (year == -1 && (x > 31 || x == 0)) {
+ year = x;
+ } else {
+ if (unknown[0] == -1) unknown[0] = x;
+ else if (unknown[1] == -1) unknown[1] = x;
+ else if (unknown[2] == -1) unknown[2] = x;
+ }
+ at += length;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Saving" << x;
+#endif
+ continue;
+ }
+
+ // Unknown character, typically a weekday such as 'Mon'
+ ++at;
+ }
+
+ // Once we are done parsing the string take the digits in unknown
+ // and determine which is the unknown year/month/day
+
+ int couldBe[3] = { 0, 0, 0 };
+ int unknownCount = 3;
+ for (int i = 0; i < unknownCount; ++i) {
+ if (unknown[i] == -1) {
+ couldBe[i] = ADAY | AYEAR | AMONTH;
+ unknownCount = i;
+ continue;
+ }
+
+ if (unknown[i] >= 1)
+ couldBe[i] = ADAY;
+
+ if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
+ couldBe[i] |= AMONTH;
+
+ if (year == -1)
+ couldBe[i] |= AYEAR;
+ }
+
+ // For any possible day make sure one of the values that could be a month
+ // can contain that day.
+ // For any possible month make sure one of the values that can be a
+ // day that month can have.
+ // Example: 31 11 06
+ // 31 can't be a day because 11 and 6 don't have 31 days
+ for (int i = 0; i < unknownCount; ++i) {
+ int currentValue = unknown[i];
+ bool findMatchingMonth = couldBe[i] & ADAY && currentValue >= 29;
+ bool findMatchingDay = couldBe[i] & AMONTH;
+ if (!findMatchingMonth || !findMatchingDay)
+ continue;
+ for (int j = 0; j < 3; ++j) {
+ if (j == i)
+ continue;
+ for (int k = 0; k < 2; ++k) {
+ if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
+ continue;
+ else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
+ continue;
+ int m = currentValue;
+ int d = unknown[j];
+ if (k == 0)
+ qSwap(m, d);
+ if (m == -1) m = month;
+ bool found = true;
+ switch(m) {
+ case 2:
+ // When we get 29 and the year ends up having only 28
+ // See date.isValid below
+ // Example: 29 23 Feb
+ if (d <= 29)
+ found = false;
+ break;
+ case 4: case 6: case 9: case 11:
+ if (d <= 30)
+ found = false;
+ break;
+ default:
+ if (d > 0 && d <= 31)
+ found = false;
+ }
+ if (k == 0) findMatchingMonth = found;
+ else if (k == 1) findMatchingDay = found;
+ }
+ }
+ if (findMatchingMonth)
+ couldBe[i] &= ~ADAY;
+ if (findMatchingDay)
+ couldBe[i] &= ~AMONTH;
+ }
+
+ // First set the year/month/day that have been deduced
+ // and reduce the set as we go along to deduce more
+ for (int i = 0; i < unknownCount; ++i) {
+ int unset = 0;
+ for (int j = 0; j < 3; ++j) {
+ if (couldBe[j] == ADAY && day == -1) {
+ day = unknown[j];
+ unset |= ADAY;
+ } else if (couldBe[j] == AMONTH && month == -1) {
+ month = unknown[j];
+ unset |= AMONTH;
+ } else if (couldBe[j] == AYEAR && year == -1) {
+ year = unknown[j];
+ unset |= AYEAR;
+ } else {
+ // common case
+ break;
+ }
+ couldBe[j] &= ~unset;
+ }
+ }
+
+ // Now fallback to a standardized order to fill in the rest with
+ for (int i = 0; i < unknownCount; ++i) {
+ if (couldBe[i] & AMONTH && month == -1) month = unknown[i];
+ else if (couldBe[i] & ADAY && day == -1) day = unknown[i];
+ else if (couldBe[i] & AYEAR && year == -1) year = unknown[i];
+ }
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Final set" << year << month << day;
+#endif
+
+ if (year == -1 || month == -1 || day == -1) {
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Parser failure" << year << month << day;
+#endif
+ return QDateTime();
+ }
+
+ // Y2k behavior
+ int y2k = 0;
+ if (year < 70)
+ y2k = 2000;
+ else if (year < 100)
+ y2k = 1900;
+
+ QDate date(year + y2k, month, day);
+
+ // When we were given a bad cookie that when parsed
+ // set the day to 29 and the year to one that doesn't
+ // have the 29th of Feb rather then adding the extra
+ // complicated checking earlier just swap here.
+ // Example: 29 23 Feb
+ if (!date.isValid())
+ date = QDate(day + y2k, month, year);
+
+ QDateTime dateTime(date, time, Qt::UTC);
+
+ if (zoneOffset != -1) {
+ dateTime = dateTime.addSecs(zoneOffset);
+ }
+ if (!dateTime.isValid())
+ return QDateTime();
+ return dateTime;
+}
+
/*!
Parses the cookie string \a cookieString as received from a server
response in the "Set-Cookie:" header. If there's a parsing error,
@@ -580,17 +967,23 @@ QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieStrin
while (position < length) {
QNetworkCookie cookie;
+ // When there are multiple SetCookie headers they are join with a new line
+ // \n will always be the start of a new cookie
+ int endOfSetCookie = cookieString.indexOf('\n', position);
+ if (endOfSetCookie == -1)
+ endOfSetCookie = length;
+
// The first part is always the "NAME=VALUE" part
QPair<QByteArray,QByteArray> field = nextField(cookieString, position);
if (field.first.isEmpty() || field.second.isNull())
// parsing error
- return QList<QNetworkCookie>();
+ break;
cookie.setName(field.first);
cookie.setValue(field.second);
position = nextNonWhitespace(cookieString, position);
bool endOfCookie = false;
- while (!endOfCookie && position < length)
+ while (!endOfCookie && position < endOfSetCookie)
switch (cookieString.at(position++)) {
case ',':
// end of the cookie
@@ -603,64 +996,20 @@ QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieStrin
field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
if (field.first == "expires") {
- static const char dateFormats[] =
- "d-MMM-yyyy hh:mm:ss\0"
- "d MMM yyyy hh:mm:ss\0"
- "d-MMM-yy hh:mm:ss\0"
- "\0";
-
- // expires is a special case because it contains a naked comma
- // and naked spaces. The format is:
- // expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
- // but we also accept standard HTTP dates
-
- // make sure we're at the comma
- if (position >= length || cookieString.at(position) != ',')
- // invalid cookie string
- return QList<QNetworkCookie>();
-
- ++position;
+ position -= field.second.length();
int end;
for (end = position; end < length; ++end)
- if (cookieString.at(end) == ',' || cookieString.at(end) == ';')
+ if (isValueSeparator(cookieString.at(end)))
break;
- QByteArray datestring = cookieString.mid(position, end - position).trimmed();
+ QByteArray dateString = cookieString.mid(position, end - position).trimmed();
position = end;
- if (datestring.endsWith(" GMT") || datestring.endsWith(" UTC"))
- datestring.chop(4);
- else if (datestring.endsWith(" +0000"))
- datestring.chop(6);
-
- size_t i = 0;
- int j = 0;
- QLocale cLocale = QLocale::c();
- QDateTime dt;
- do {
- QLatin1String df(dateFormats + i);
- i += strlen(dateFormats + i) + 1;
-
-#ifndef QT_NO_DATESTRING
- dt = cLocale.toDateTime(QString::fromLatin1(datestring), df);
-
- // some cookies are set with a two-digit year
- // (although this is not allowed); this is interpreted as a year
- // in the 20th century by QDateTime.
- // Work around this case here (assuming 00-69 is 21st century,
- // 70-99 is 20th century)
- QDate date = dt.date();
- if (j == 2 && date.year() >= 1900 && date.year() < 1970)
- dt = dt.addYears(100);
- if (date.year() >= 0 && date.year() < 100)
- dt = dt.addYears(1900);
-#endif
- j++;
- } while (!dt.isValid() && i <= sizeof dateFormats - 1);
- if (!dt.isValid())
- // invalid cookie string
- return QList<QNetworkCookie>();
-
- dt.setTimeSpec(Qt::UTC);
+ QDateTime dt = parseDateString(dateString.toLower());
+ if (!dt.isValid()) {
+ cookie = QNetworkCookie();
+ endOfCookie = true;
+ continue;
+ }
cookie.setExpirationDate(dt);
} else if (field.first == "domain") {
QByteArray rawDomain = field.second;
@@ -701,9 +1050,12 @@ QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieStrin
}
position = nextNonWhitespace(cookieString, position);
+ if (position > endOfSetCookie)
+ endOfCookie = true;
}
- result += cookie;
+ if (!cookie.name().isEmpty())
+ result += cookie;
}
return result;
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index fa0fccb..93360c8 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -253,7 +253,8 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
// ### use atomic rename rather then remove & rename
if (cacheItem->file->rename(fileName))
currentCacheSize += cacheItem->file->size();
- cacheItem->file->setAutoRemove(true);
+ else
+ cacheItem->file->setAutoRemove(true);
}
if (cacheItem->metaData.url() == lastItem.metaData.url())
lastItem.reset();
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index eaa572f..79c3d1a 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -131,27 +131,37 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (!copyDevice && !q->isOpen())
return;
- qint64 bytesToRead = nextDownstreamBlockSize();
- if (bytesToRead == 0)
- // we'll be called again, eventually
- return;
+ forever {
+ qint64 bytesToRead = nextDownstreamBlockSize();
+ if (bytesToRead == 0)
+ // we'll be called again, eventually
+ break;
- bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
- char *ptr = readBuffer.reserve(bytesToRead);
- qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead);
- if (bytesActuallyRead == -1) {
- readBuffer.chop(bytesToRead);
- backendNotify(NotifyCopyFinished);
- return;
- }
+ bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
+ char *ptr = readBuffer.reserve(bytesToRead);
+ qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead);
+ if (bytesActuallyRead == -1) {
+ readBuffer.chop(bytesToRead);
+ backendNotify(NotifyCopyFinished);
+ return;
+ }
- if (bytesActuallyRead != bytesToRead)
- readBuffer.chop(bytesToRead - bytesActuallyRead);
+ if (bytesActuallyRead != bytesToRead)
+ readBuffer.chop(bytesToRead - bytesActuallyRead);
- if (!copyDevice->isSequential() && copyDevice->atEnd())
- backendNotify(NotifyCopyFinished);
+ if (!copyDevice->isSequential() && copyDevice->atEnd()) {
+ backendNotify(NotifyCopyFinished);
+ break;
+ }
+
+ bytesDownloaded += bytesActuallyRead;
+ }
+
+ if (bytesDownloaded == lastBytesDownloaded) {
+ // we didn't read anything
+ return;
+ }
- bytesDownloaded += bytesActuallyRead;
lastBytesDownloaded = bytesDownloaded;
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
emit q->downloadProgress(bytesDownloaded,
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 4f477bd..c9161f8 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -52,7 +52,7 @@
QT_BEGIN_NAMESPACE
-#include <../3rdparty/des/des.cpp>
+#include "../../3rdparty/des/des.cpp"
static QByteArray qNtlmPhase1();
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index fc43369..b225c17 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -435,7 +435,7 @@ void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol prot
The class also supports common predefined addresses: \l Null, \l
LocalHost, \l LocalHostIPv6, \l Broadcast, and \l Any.
- \sa QTcpSocket, QTcpServer, QUdpSocket
+ \sa QHostInfo, QTcpSocket, QTcpServer, QUdpSocket
*/
/*! \enum QHostAddress::SpecialAddress
diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp
index 9540c18..de97629 100644
--- a/src/network/kernel/qnetworkinterface_win.cpp
+++ b/src/network/kernel/qnetworkinterface_win.cpp
@@ -173,14 +173,14 @@ static QList<QNetworkInterfacePrivate *> interfaceListingWinXP()
interfaces << iface;
iface->index = 0;
- if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex))
+ if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex) && ptr->Ipv6IfIndex != 0)
iface->index = ptr->Ipv6IfIndex;
else if (ptr->IfIndex != 0)
iface->index = ptr->IfIndex;
iface->flags = QNetworkInterface::CanBroadcast;
- if (ptr->OperStatus == IfOperStatusUp)
- iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+ if (ptr->OperStatus == IfOperStatusUp)
+ iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
if ((ptr->Flags & IP_ADAPTER_NO_MULTICAST) == 0)
iface->flags |= QNetworkInterface::CanMulticast;
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index bf243cf..8ae247c 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -1266,7 +1266,7 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint
(int) openMode);
#endif
- if (d->state == ConnectedState || d->state == ConnectingState) {
+ if (d->state == ConnectedState || d->state == ConnectingState || d->state == ClosingState) {
qWarning("QAbstractSocket::connectToHost() called when already connecting/connected to \"%s\"", qPrintable(hostName));
return;
}
@@ -2282,7 +2282,7 @@ void QAbstractSocket::disconnectFromHostImplementation()
emit delayedCloseFinished(); // compat signal
#endif
// only emit disconnected if we were connected before
- if (previousState == ConnectedState || ClosingState)
+ if (previousState == ConnectedState || previousState == ClosingState)
emit disconnected();
d->localPort = 0;
diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp
index 50bcaaa..d3ccb69 100644
--- a/src/network/socket/qlocalserver_unix.cpp
+++ b/src/network/socket/qlocalserver_unix.cpp
@@ -166,9 +166,11 @@ void QLocalServerPrivate::closeServer()
QT_CLOSE(listenSocket);
listenSocket = -1;
- if (socketNotifier)
+ if (socketNotifier) {
+ socketNotifier->setEnabled(false); // Otherwise, closed socket is checked before deleter runs
socketNotifier->deleteLater();
- socketNotifier = 0;
+ socketNotifier = 0;
+ }
if (!fullServerName.isEmpty())
QFile::remove(fullServerName);
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index dd48d0a..781d3da 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -192,6 +192,7 @@ public:
void _q_error(QAbstractSocket::SocketError newError);
void _q_connectToSocket();
void _q_abortConnectionAttempt();
+ void cancelDelayedConnect();
QSocketNotifier *delayConnect;
QTimer *connectTimer;
int connectingSocket;
diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp
index 4748b76..2471043 100644
--- a/src/network/socket/qlocalsocket_unix.cpp
+++ b/src/network/socket/qlocalsocket_unix.cpp
@@ -323,11 +323,8 @@ void QLocalSocketPrivate::_q_connectToSocket()
}
// connected!
- if (delayConnect) {
- delayConnect->setEnabled(false);
- delete delayConnect;
- delayConnect = 0;
- }
+ cancelDelayedConnect();
+
serverName = connectingName;
fullServerName = connectingPathName;
if (unixSocket.setSocketDescriptor(connectingSocket,
@@ -374,6 +371,18 @@ void QLocalSocketPrivate::_q_abortConnectionAttempt()
q->close();
}
+void QLocalSocketPrivate::cancelDelayedConnect()
+{
+ if (delayConnect) {
+ delayConnect->setEnabled(false);
+ delete delayConnect;
+ delayConnect = 0;
+ connectTimer->stop();
+ delete connectTimer;
+ connectTimer = 0;
+ }
+}
+
quintptr QLocalSocket::socketDescriptor() const
{
Q_D(const QLocalSocket);
@@ -420,14 +429,7 @@ void QLocalSocket::close()
{
Q_D(QLocalSocket);
d->unixSocket.close();
- if (d->delayConnect) {
- d->delayConnect->setEnabled(false);
- delete d->delayConnect;
- d->delayConnect = 0;
- d->connectTimer->stop();
- delete d->connectTimer;
- d->connectTimer = 0;
- }
+ d->cancelDelayedConnect();
if (d->connectingSocket != -1)
::close(d->connectingSocket);
d->connectingSocket = -1;
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index e759d0b..39c9284 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -298,7 +298,7 @@ qint64 QLocalSocketPrivate::bytesAvailable()
if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) {
return bytes;
} else {
- if (ERROR_BROKEN_PIPE == GetLastError() && !pipeClosed) {
+ if (!pipeClosed) {
pipeClosed = true;
QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
}
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index 4de844e..5584c6a 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -526,26 +526,11 @@ int QNativeSocketEnginePrivate::nativeAccept()
qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const
{
- /*
- Apparently, there is not consistency among different operating
- systems on how to use FIONREAD.
-
- FreeBSD, Linux and Solaris all expect the 3rd argument to
- ioctl() to be an int, which is normally 32-bit even on 64-bit
- machines.
-
- IRIX, on the other hand, expects a size_t, which is 64-bit on
- 64-bit machines.
-
- So, the solution is to use size_t initialized to zero to make
- sure all bits are set to zero, preventing underflow with the
- FreeBSD/Linux/Solaris ioctls.
- */
- size_t nbytes = 0;
+ int nbytes = 0;
// gives shorter than true amounts on Unix domain sockets.
qint64 available = 0;
if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)
- available = (qint64) *((int *) &nbytes);
+ available = (qint64) nbytes;
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativeBytesAvailable() == %lli", available);
@@ -918,15 +903,15 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
if (timeout > 0) {
// recalculate the timeout
- timeout -= timer.elapsed();
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
-
- if (timeout < 0) {
+ int t = timeout - timer.elapsed();
+ if (t < 0) {
// oops, timeout turned negative?
retval = -1;
break;
}
+
+ tv.tv_sec = t / 1000;
+ tv.tv_usec = (t % 1000) * 1000;
}
} while (true);
@@ -965,15 +950,15 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
if (timeout > 0) {
// recalculate the timeout
- timeout -= timer.elapsed();
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
-
- if (timeout < 0) {
+ int t = timeout - timer.elapsed();
+ if (t < 0) {
// oops, timeout turned negative?
ret = -1;
break;
}
+
+ tv.tv_sec = t / 1000;
+ tv.tv_usec = (t % 1000) * 1000;
}
} while (true);
if (ret <= 0)