diff options
author | Thiago Macieira <thiago.macieira@nokia.com> | 2009-11-25 18:20:45 (GMT) |
---|---|---|
committer | Thiago Macieira <thiago.macieira@nokia.com> | 2009-11-25 18:20:45 (GMT) |
commit | e58b7f0cc139c5fda3d472c38161be8fb718db7d (patch) | |
tree | 3c20cccfe050d125a5f4736ba11b6383e8734be4 /src/network | |
parent | c2a4a1b1e06f8b0d02e7b56c9eb0c19fd2fae80a (diff) | |
parent | 4c3539dfbc65d5decdd842d4181f9aa3d38d213c (diff) | |
download | Qt-e58b7f0cc139c5fda3d472c38161be8fb718db7d.zip Qt-e58b7f0cc139c5fda3d472c38161be8fb718db7d.tar.gz Qt-e58b7f0cc139c5fda3d472c38161be8fb718db7d.tar.bz2 |
Merge branch '4.6'
Conflicts:
doc/src/modules.qdoc
examples/assistant/simpletextviewer/findfiledialog.cpp
examples/webkit/fancybrowser/mainwindow.cpp
src/gui/widgets/qtabbar.cpp
src/gui/widgets/qtabbar_p.h
tests/auto/qpixmap/tst_qpixmap.cpp
tools/assistant/compat/helpdialog.cpp
tools/assistant/compat/tabbedbrowser.cpp
translations/translations.pri
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/access/access.pri | 2 | ||||
-rw-r--r-- | src/network/access/qfilenetworkreply.cpp | 205 | ||||
-rw-r--r-- | src/network/access/qfilenetworkreply_p.h | 107 | ||||
-rw-r--r-- | src/network/access/qhttp.cpp | 8 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 17 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel.cpp | 10 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkheader.cpp | 6 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkreply.cpp | 6 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessbackend.cpp | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend.cpp | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.cpp | 12 | ||||
-rw-r--r-- | src/network/access/qnetworkcookiejar.cpp | 7 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.cpp | 105 | ||||
-rw-r--r-- | src/network/kernel/qnetworkproxy_win.cpp | 5 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine.cpp | 40 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine_win.cpp | 34 |
16 files changed, 516 insertions, 52 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri index edc1b63..aa36890 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -7,6 +7,7 @@ HEADERS += access/qftp.h \ access/qhttpnetworkreply_p.h \ access/qhttpnetworkconnection_p.h \ access/qhttpnetworkconnectionchannel_p.h \ + access/qfilenetworkreply_p.h \ access/qnetworkaccessmanager.h \ access/qnetworkaccessmanager_p.h \ access/qnetworkaccesscache_p.h \ @@ -38,6 +39,7 @@ SOURCES += access/qftp.cpp \ access/qhttpnetworkreply.cpp \ access/qhttpnetworkconnection.cpp \ access/qhttpnetworkconnectionchannel.cpp \ + access/qfilenetworkreply.cpp \ access/qnetworkaccessmanager.cpp \ access/qnetworkaccesscache.cpp \ access/qnetworkaccessbackend.cpp \ diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qfilenetworkreply.cpp new file mode 100644 index 0000000..497519f --- /dev/null +++ b/src/network/access/qfilenetworkreply.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilenetworkreply_p.h" + +#include "QtCore/qdatetime.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> + +QT_BEGIN_NAMESPACE + +QFileNetworkReplyPrivate::QFileNetworkReplyPrivate() + : QNetworkReplyPrivate(), realFileSize(0), finished(false) +{ +} + +QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req) + : QNetworkReply(*new QFileNetworkReplyPrivate(), parent) +{ + setRequest(req); + setUrl(req.url()); + setOperation(QNetworkAccessManager::GetOperation); + QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection); + QNetworkReply::open(QIODevice::ReadOnly); +} + +QFileNetworkReply::~QFileNetworkReply() +{ +} + +// This code is mostly inspired by QNetworkAccessFileBackend +// We also use its translation context for error messages +void QFileNetworkReplyPrivate::_q_startOperation() +{ + Q_Q(QFileNetworkReply); + + QUrl url = q->url(); + if (url.host() == QLatin1String("localhost")) + url.setHost(QString()); + +#if !defined(Q_OS_WIN) + // do not allow UNC paths on Unix + if (!url.host().isEmpty()) { + // we handle only local files + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()); + q->setError(QNetworkReply::ProtocolInvalidOperationError, msg); + emit q->error(QNetworkReply::ProtocolInvalidOperationError); + doFinished(); + return; + } +#endif + if (url.path().isEmpty()) + url.setPath(QLatin1String("/")); + q->setUrl(url); + + + QString fileName = url.toLocalFile(); + if (fileName.isEmpty()) { + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } + realFile.setFileName(fileName); + + QFileInfo fi(realFile); + if (fi.isDir()) { + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString()); + q->setError(QNetworkReply::ContentOperationNotPermittedError, msg); + emit q->error(QNetworkReply::ContentOperationNotPermittedError); + doFinished(); + return; + } + + bool opened = realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered); + + // could we open the file? + if (!opened) { + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2") + .arg(realFile.fileName(), realFile.errorString()); + + if (realFile.exists()) { + q->setError(QNetworkReply::ContentAccessDenied, msg); + emit q->error(QNetworkReply::ContentAccessDenied); + } else { + q->setError(QNetworkReply::ContentNotFoundError, msg); + emit q->error(QNetworkReply::ContentNotFoundError); + } + doFinished(); + return; + } + + realFileSize = fi.size(); + q->setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified()); + q->setHeader(QNetworkRequest::ContentLengthHeader, realFileSize); + + emit q->metaDataChanged(); + emit q->downloadProgress(realFileSize, realFileSize); + emit q->readyRead(); + doFinished(); +} + +bool QFileNetworkReplyPrivate::isFinished() const +{ + return finished; +} + +void QFileNetworkReplyPrivate::doFinished() +{ + Q_Q(QFileNetworkReply); + finished = true; + emit q->finished(); +} + + +void QFileNetworkReply::close() +{ + Q_D(QFileNetworkReply); + QNetworkReply::close(); + d->realFile.close(); + + if (!d->finished) + d->doFinished(); +} + +void QFileNetworkReply::abort() +{ + Q_D(QFileNetworkReply); + QNetworkReply::close(); + d->realFile.close(); + + if (!d->finished) + d->doFinished(); +} + +qint64 QFileNetworkReply::bytesAvailable() const +{ + Q_D(const QFileNetworkReply); + return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable(); +} + +bool QFileNetworkReply::isSequential () const +{ + return true; +} + +qint64 QFileNetworkReply::size() const +{ + Q_D(const QFileNetworkReply); + return d->realFileSize; +} + +/*! + \internal +*/ +qint64 QFileNetworkReply::readData(char *data, qint64 maxlen) +{ + Q_D(QFileNetworkReply); + qint64 ret = d->realFile.read(data, maxlen); + if (ret == 0 && bytesAvailable() == 0) + return -1; // everything had been read + else + return ret; +} + + +QT_END_NAMESPACE + +#include "moc_qfilenetworkreply_p.cpp" + diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qfilenetworkreply_p.h new file mode 100644 index 0000000..831f50a --- /dev/null +++ b/src/network/access/qfilenetworkreply_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILENETWORKREPLY_P_H +#define QFILENETWORKREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkreply.h" +#include "qnetworkreply_p.h" +#include "qnetworkaccessmanager.h" +#include <QFile> + +QT_BEGIN_NAMESPACE + + +class QFileNetworkReplyPrivate; +class QFileNetworkReply: public QNetworkReply +{ + Q_OBJECT +public: + QFileNetworkReply(QObject *parent, const QNetworkRequest &req); + ~QFileNetworkReply(); + virtual void abort(); + + // reimplemented from QNetworkReply + virtual void close(); + virtual qint64 bytesAvailable() const; + virtual bool isSequential () const; + qint64 size() const; + + + virtual qint64 readData(char *data, qint64 maxlen); + + Q_DECLARE_PRIVATE(QFileNetworkReply) + Q_PRIVATE_SLOT(d_func(), void _q_startOperation()) + +}; + +class QFileNetworkReplyPrivate: public QNetworkReplyPrivate +{ +public: + QFileNetworkReplyPrivate(); + + QFile realFile; + qint64 realFileSize; + + void _q_startOperation(); + + virtual bool isFinished() const; + void doFinished(); + bool finished; + + + Q_DECLARE_PUBLIC(QFileNetworkReply) +}; + +QT_END_NAMESPACE + +#endif // QFILENETWORKREPLY_P_H diff --git a/src/network/access/qhttp.cpp b/src/network/access/qhttp.cpp index f006fba..7365435 100644 --- a/src/network/access/qhttp.cpp +++ b/src/network/access/qhttp.cpp @@ -3115,14 +3115,14 @@ void QHttpPrivate::setSock(QTcpSocket *sock) QObject::connect(socket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_slotBytesWritten(qint64))); #ifndef QT_NO_NETWORKPROXY - QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), - q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); + QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); #endif #ifndef QT_NO_OPENSSL if (qobject_cast<QSslSocket *>(socket)) { - QObject::connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), - q, SIGNAL(sslErrors(const QList<QSslError> &))); + QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)), + q, SIGNAL(sslErrors(QList<QSslError>))); QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)), q, SLOT(_q_slotEncryptedBytesWritten(qint64))); } diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 8dd7a00..2dbd512 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -194,11 +194,20 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) // some websites mandate an accept-language header and fail // if it is not sent. This is a problem with the website and - // not with us, but we work around this by setting a - // universal one always. + // not with us, but we work around this by setting + // one always. value = request.headerField("accept-language"); - if (value.isEmpty()) - request.setHeaderField("accept-language", "en,*"); + if (value.isEmpty()) { + QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-')); + QString acceptLanguage; + if (systemLocale == QLatin1String("C")) + acceptLanguage = QString::fromAscii("en,*"); + else if (systemLocale.startsWith(QLatin1String("en-"))) + acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale); + else + acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale); + request.setHeaderField("Accept-Language", acceptLanguage.toAscii()); + } // set the User Agent value = request.headerField("user-agent"); diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 6962ab3..fa89fcc 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -90,8 +90,8 @@ void QHttpNetworkConnectionChannel::init() this, SLOT(_q_error(QAbstractSocket::SocketError)), Qt::DirectConnection); #ifndef QT_NO_NETWORKPROXY - QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), - this, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::DirectConnection); #endif @@ -102,8 +102,8 @@ void QHttpNetworkConnectionChannel::init() QObject::connect(sslSocket, SIGNAL(encrypted()), this, SLOT(_q_encrypted()), Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), - this, SLOT(_q_sslErrors(const QList<QSslError>&)), + QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(_q_sslErrors(QList<QSslError>)), Qt::DirectConnection); QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(_q_encryptedBytesWritten(qint64)), @@ -774,6 +774,8 @@ void QHttpNetworkConnectionChannel::_q_connected() { // improve performance since we get the request sent by the kernel ASAP socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + // not sure yet if it helps, but it makes sense + socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp index 68ed3e5..e9866ca 100644 --- a/src/network/access/qhttpnetworkheader.cpp +++ b/src/network/access/qhttpnetworkheader.cpp @@ -92,11 +92,10 @@ QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const 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()) + if (qstricmp(name.constData(), it->first) == 0) result += it->second; return result; @@ -104,10 +103,9 @@ QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray 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()) + if (qstricmp(name.constData(), it->first) == 0) it = fields.erase(it); else ++it; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index e990704..2b0c252 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -239,7 +239,7 @@ qint64 QHttpNetworkReplyPrivate::bytesAvailable() const bool QHttpNetworkReplyPrivate::isGzipped() { QByteArray encoding = headerField("content-encoding"); - return encoding.toLower() == "gzip"; + return qstricmp(encoding.constData(), "gzip") == 0; } void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() @@ -247,11 +247,10 @@ 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()) { + if (qstricmp(name.constData(), it->first.constData()) == 0) { fields.erase(it); break; } @@ -269,6 +268,7 @@ bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challeng QList<QByteArray> challenges = headerFieldValues(header); for (int i = 0; i<challenges.size(); i++) { QByteArray line = challenges.at(i); + // todo use qstrincmp if (!line.toLower().startsWith("negotiate")) challenge = line; } diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 1a92868..3e2db7a 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -141,6 +141,8 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() // and the special backends need to access this. void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal) { + if (reply->isFinished()) + return; reply->emitUploadProgress(bytesSent, bytesTotal); } diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index bfcc299..91f9189 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -732,7 +732,7 @@ void QNetworkAccessHttpBackend::replyHeaderChanged() for (; it != end; ++it) { QByteArray value = rawHeader(it->first); if (!value.isEmpty()) { - if (it->first.toLower() == "set-cookie") + if (qstricmp(it->first.constData(), "set-cookie") == 0) value += "\n"; else value += ", "; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index b1160aa..754633d 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -52,6 +52,7 @@ #include "qnetworkaccessfilebackend_p.h" #include "qnetworkaccessdatabackend_p.h" #include "qnetworkaccessdebugpipebackend_p.h" +#include "qfilenetworkreply_p.h" #include "QtCore/qbuffer.h" #include "QtCore/qurl.h" @@ -681,6 +682,17 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera QIODevice *outgoingData) { Q_D(QNetworkAccessManager); + + // fast path for GET on file:// URLs + // Also if the scheme is empty we consider it a file. + // The QNetworkAccessFileBackend will right now only be used + // for PUT or qrc:// + if (op == QNetworkAccessManager::GetOperation + && (req.url().scheme() == QLatin1String("file") + || req.url().scheme().isEmpty())) { + return new QFileNetworkReply(this, req); + } + QNetworkRequest request = req; if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && outgoingData && !outgoingData->isSequential()) { diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp index 19f7217..f826115 100644 --- a/src/network/access/qnetworkcookiejar.cpp +++ b/src/network/access/qnetworkcookiejar.cpp @@ -192,9 +192,10 @@ bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieLis // validate the cookie & set the defaults if unset if (cookie.path().isEmpty()) cookie.setPath(defaultPath); - else if (!isParentPath(pathAndFileName, cookie.path())) - continue; // not accepted - + // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815 +// else if (!isParentPath(pathAndFileName, cookie.path())) { +// continue; // not accepted +// } if (cookie.domain().isEmpty()) { cookie.setDomain(defaultDomain); } else { diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 86195c6..c91c608 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -48,6 +48,8 @@ #include "QtCore/qlocale.h" #include "QtCore/qdatetime.h" +#include <ctype.h> + QT_BEGIN_NAMESPACE /*! @@ -606,26 +608,25 @@ static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerNam { // headerName is not empty here - QByteArray lower = headerName.toLower(); - switch (lower.at(0)) { + switch (tolower(headerName.at(0))) { case 'c': - if (lower == "content-type") + if (qstricmp(headerName.constData(), "content-type") == 0) return QNetworkRequest::ContentTypeHeader; - else if (lower == "content-length") + else if (qstricmp(headerName.constData(), "content-length") == 0) return QNetworkRequest::ContentLengthHeader; - else if (lower == "cookie") + else if (qstricmp(headerName.constData(), "cookie") == 0) return QNetworkRequest::CookieHeader; break; case 'l': - if (lower == "location") + if (qstricmp(headerName.constData(), "location") == 0) return QNetworkRequest::LocationHeader; - else if (lower == "last-modified") + else if (qstricmp(headerName.constData(), "last-modified") == 0) return QNetworkRequest::LastModifiedHeader; break; case 's': - if (lower == "set-cookie") + if (qstricmp(headerName.constData(), "set-cookie") == 0) return QNetworkRequest::SetCookieHeader; break; } @@ -697,11 +698,10 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy QNetworkHeadersPrivate::RawHeadersList::ConstIterator QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const { - QByteArray lowerKey = key.toLower(); RawHeadersList::ConstIterator it = rawHeaders.constBegin(); RawHeadersList::ConstIterator end = rawHeaders.constEnd(); for ( ; it != end; ++it) - if (it->first.toLower() == lowerKey) + if (qstricmp(it->first.constData(), key.constData()) == 0) return it; return end; // not found @@ -775,10 +775,9 @@ void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders heade void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value) { - QByteArray lowerKey = key.toLower(); RawHeadersList::Iterator it = rawHeaders.begin(); while (it != rawHeaders.end()) { - if (it->first.toLower() == lowerKey) + if (qstricmp(it->first.constData(), key.constData()) == 0) it = rawHeaders.erase(it); else ++it; @@ -805,6 +804,68 @@ void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByt } } +// Fast month string to int conversion. This code +// assumes that the Month name is correct and that +// the string is at least three chars long. +static int name_to_month(const char* month_str) +{ + switch (month_str[0]) { + case 'J': + switch (month_str[1]) { + case 'a': + return 1; + break; + case 'u': + switch (month_str[2] ) { + case 'n': + return 6; + break; + case 'l': + return 7; + break; + } + } + break; + case 'F': + return 2; + break; + case 'M': + switch (month_str[2] ) { + case 'r': + return 3; + break; + case 'y': + return 5; + break; + } + break; + case 'A': + switch (month_str[1]) { + case 'p': + return 4; + break; + case 'u': + return 8; + break; + } + break; + case 'O': + return 10; + break; + case 'S': + return 9; + break; + case 'N': + return 11; + break; + case 'D': + return 12; + break; + } + + return 0; +} + QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) { // HTTP dates have three possible formats: @@ -820,16 +881,20 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) // no comma -> asctime(3) format dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate); } else { - // eat the weekday, the comma and the space following it - QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); - - QLocale c = QLocale::c(); - if (pos == 3) - // must be RFC 1123 date - dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT")); - else + // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the + // QtWebKit performance benchmarks to get an idea. + if (pos == 3) { + char month_name[4]; + int day, year, hour, minute, second; + if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6) + dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second)); + } else { + QLocale c = QLocale::c(); + // eat the weekday, the comma and the space following it + QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); // must be RFC 850 date dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'")); + } } #endif // QT_NO_DATESTRING diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp index 6f92424..0e2dd2b 100644 --- a/src/network/kernel/qnetworkproxy_win.cpp +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -291,7 +291,10 @@ void QWindowsSystemProxy::init() GlobalFree(ieProxyConfig.lpszAutoConfigUrl); } if (ieProxyConfig.lpszProxy) { - proxyServerList << QString::fromWCharArray(ieProxyConfig.lpszProxy); + // http://msdn.microsoft.com/en-us/library/aa384250%28VS.85%29.aspx speaks only about a "proxy URL", + // not multiple URLs. However we tested this and it can return multiple URLs. So we use splitSpaceSemicolon + // on it. + proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxy)); GlobalFree(ieProxyConfig.lpszProxy); } if (ieProxyConfig.lpszProxyBypass) { diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index ecf5ad9..5d17022 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -876,7 +876,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut) */ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) { - Q_D(const QNativeSocketEngine); + Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(), QAbstractSocket::UnconnectedState, false); @@ -893,6 +893,24 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) setState(QAbstractSocket::ConnectedState); d_func()->fetchConnectionParameters(); return true; + } else { + int value = 0; + int valueSize = sizeof(value); + if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) { + if (value == WSAECONNREFUSED) { + d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } else if (value == WSAETIMEDOUT) { + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } else if (value == WSAEHOSTUNREACH) { + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } + } } #endif @@ -913,7 +931,7 @@ bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWri bool checkRead, bool checkWrite, int msecs, bool *timedOut) { - Q_D(const QNativeSocketEngine); + Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false); Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(), QAbstractSocket::UnconnectedState, false); @@ -927,6 +945,24 @@ bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWri setState(QAbstractSocket::ConnectedState); d_func()->fetchConnectionParameters(); return true; + } else { + int value = 0; + int valueSize = sizeof(value); + if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) { + if (value == WSAECONNREFUSED) { + d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } else if (value == WSAETIMEDOUT) { + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } else if (value == WSAEHOSTUNREACH) { + d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString); + d->socketState = QAbstractSocket::UnconnectedState; + return false; + } + } } #endif if (ret == 0) { diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 63fe78e..91f930a 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -1107,10 +1107,22 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; - if (selectForRead) + if (selectForRead) { ret = select(0, &fds, 0, 0, timeout < 0 ? 0 : &tv); - else - ret = select(0, 0, &fds, 0, timeout < 0 ? 0 : &tv); + } else { + // select for write + + // Windows needs this to report errors when connecting a socket ... + fd_set fdexception; + FD_ZERO(&fdexception); + FD_SET(socketDescriptor, &fdexception); + + ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv); + + // ... but if it is actually set, pretend it did not happen + if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + ret--; + } if (readEnabled) readNotifier->setEnabled(true); @@ -1125,9 +1137,10 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool readEnabled = checkRead && readNotifier && readNotifier->isEnabled(); if (readEnabled) readNotifier->setEnabled(false); - + fd_set fdread; fd_set fdwrite; + fd_set fdexception; int ret = 0; @@ -1137,9 +1150,13 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, fdread.fd_array[0] = socketDescriptor; } memset(&fdwrite, 0, sizeof(fd_set)); + FD_ZERO(&fdexception); if (checkWrite) { fdwrite.fd_count = 1; fdwrite.fd_array[0] = socketDescriptor; + + // Windows needs this to report errors when connecting a socket + FD_SET(socketDescriptor, &fdexception); } struct timeval tv; @@ -1147,10 +1164,15 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, tv.tv_usec = (timeout % 1000) * 1000; #if !defined(Q_OS_WINCE) - ret = select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv); #else - ret = select(1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + ret = select(1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv); #endif + + //... but if it is actually set, pretend it did not happen + if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + ret--; + if (readEnabled) readNotifier->setEnabled(true); |