diff options
Diffstat (limited to 'src/network')
37 files changed, 726 insertions, 131 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri index edc1b63c57..aa368902a1 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 0000000000..497519f776 --- /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 0000000000..831f50a20e --- /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 69faee3177..f006fbadce 100644 --- a/src/network/access/qhttp.cpp +++ b/src/network/access/qhttp.cpp @@ -121,6 +121,9 @@ public: void _q_slotError(QAbstractSocket::SocketError); void _q_slotClosed(); void _q_slotBytesWritten(qint64 numBytes); +#ifndef QT_NO_OPENSSL + void _q_slotEncryptedBytesWritten(qint64 numBytes); +#endif void _q_slotDoFinished(); void _q_slotSendRequest(); void _q_continuePost(); @@ -135,6 +138,8 @@ public: void closeConn(); void setSock(QTcpSocket *sock); + void postMoreData(); + QTcpSocket *socket; int reconnectAttempts; bool deleteSocket; @@ -2659,19 +2664,40 @@ void QHttpPrivate::_q_slotError(QAbstractSocket::SocketError err) closeConn(); } +#ifndef QT_NO_OPENSSL +void QHttpPrivate::_q_slotEncryptedBytesWritten(qint64 written) +{ + Q_UNUSED(written); + postMoreData(); +} +#endif + void QHttpPrivate::_q_slotBytesWritten(qint64 written) { Q_Q(QHttp); bytesDone += written; emit q->dataSendProgress(bytesDone, bytesTotal); + postMoreData(); +} +// Send the POST data +void QHttpPrivate::postMoreData() +{ if (pendingPost) return; if (!postDevice) return; + // the following is backported code from Qt 4.6 QNetworkAccessManager. + // We also have to check the encryptedBytesToWrite() if it is an SSL socket. +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + // if it is really an ssl socket, check more than just bytesToWrite() + if ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) == 0) { +#else if (socket->bytesToWrite() == 0) { +#endif int max = qMin<qint64>(4096, postDevice->size() - postDevice->pos()); QByteArray arr; arr.resize(max); @@ -3097,6 +3123,8 @@ void QHttpPrivate::setSock(QTcpSocket *sock) if (qobject_cast<QSslSocket *>(socket)) { QObject::connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), q, SIGNAL(sslErrors(const QList<QSslError> &))); + QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)), + q, SLOT(_q_slotEncryptedBytesWritten(qint64))); } #endif } diff --git a/src/network/access/qhttp.h b/src/network/access/qhttp.h index e5061ca999..f30def2263 100644 --- a/src/network/access/qhttp.h +++ b/src/network/access/qhttp.h @@ -290,6 +290,9 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_slotError(QAbstractSocket::SocketError)) Q_PRIVATE_SLOT(d_func(), void _q_slotClosed()) Q_PRIVATE_SLOT(d_func(), void _q_slotBytesWritten(qint64 numBytes)) +#ifndef QT_NO_OPENSSL + Q_PRIVATE_SLOT(d_func(), void _q_slotEncryptedBytesWritten(qint64 numBytes)) +#endif Q_PRIVATE_SLOT(d_func(), void _q_slotDoFinished()) Q_PRIVATE_SLOT(d_func(), void _q_slotSendRequest()) Q_PRIVATE_SLOT(d_func(), void _q_continuePost()) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 1124337ddb..2dbd512939 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"); @@ -372,7 +381,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); - request.setHeaderField("authorization", response); + request.setHeaderField("Authorization", response); } } } @@ -381,7 +390,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); - request.setHeaderField("proxy-authorization", response); + request.setHeaderField("Proxy-Authorization", response); } } } diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 81fac5743d..6962ab3b19 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -105,6 +105,9 @@ void QHttpNetworkConnectionChannel::init() QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(_q_sslErrors(const QList<QSslError>&)), Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), + this, SLOT(_q_encryptedBytesWritten(qint64)), + Qt::DirectConnection); } #endif } @@ -881,6 +884,15 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; emit connection->sslErrors(errors); } + +void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes) +{ + Q_UNUSED(bytes); + // bytes have been written to the socket. write even more of them :) + if (isSocketWriting()) + sendRequest(); + // otherwise we do nothing +} #endif QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 1d1153e9f5..127a431ed0 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -180,6 +180,7 @@ public: #ifndef QT_NO_OPENSSL void _q_encrypted(); // start sending request (https) void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket + void _q_encryptedBytesWritten(qint64 bytes); // proceed sending #endif }; diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp index 68ed3e5070..e9866ca6b6 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 e990704130..2b0c25266b 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 1a928681a9..3e2db7a9e7 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/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp index b4af5b62b8..fecbe832a9 100644 --- a/src/network/access/qnetworkaccessdebugpipebackend.cpp +++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp @@ -229,7 +229,7 @@ void QNetworkAccessDebugPipeBackend::possiblyFinish() void QNetworkAccessDebugPipeBackend::closeDownstreamChannel() { - qWarning() << "QNetworkAccessDebugPipeBackend::closeDownstreamChannel()" << operation(); + qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());; //if (operation() == QNetworkAccessManager::GetOperation) // socket.disconnectFromHost(); } @@ -237,7 +237,7 @@ void QNetworkAccessDebugPipeBackend::closeDownstreamChannel() void QNetworkAccessDebugPipeBackend::socketError() { - qWarning() << "QNetworkAccessDebugPipeBackend::socketError()" << socket.error(); + qWarning("QNetworkAccessDebugPipeBackend::socketError() %d",socket.error()); QNetworkReply::NetworkError code; switch (socket.error()) { case QAbstractSocket::RemoteHostClosedError: diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index bfcc299977..91f918916e 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 439d564c87..754633d694 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" @@ -95,9 +96,10 @@ static void ensureInitialized() /*! \class QNetworkAccessManager \brief The QNetworkAccessManager class allows the application to - post network requests and receive replies + send network requests and receive replies \since 4.4 + \ingroup network \inmodule QtNetwork \reentrant @@ -680,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/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index 73a8703351..7dfb7af22a 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -984,14 +984,14 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt cookie.setExpirationDate(dt); } else if (field.first == "domain") { QByteArray rawDomain = field.second; + QString maybeLeadingDot; if (rawDomain.startsWith('.')) { + maybeLeadingDot = QLatin1Char('.'); rawDomain = rawDomain.mid(1); } + QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain))); - // always add the dot, there are some servers that forget the - // leading dot. This is actually forbidden according to RFC 2109, - // but all browsers accept it anyway so we do that as well - cookie.setDomain(QLatin1Char('.') + normalizedDomain); + cookie.setDomain(maybeLeadingDot + normalizedDomain); } else if (field.first == "max-age") { bool ok = false; int secs = field.second.toInt(&ok); diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp index 8430966f6f..19f7217fd7 100644 --- a/src/network/access/qnetworkcookiejar.cpp +++ b/src/network/access/qnetworkcookiejar.cpp @@ -198,6 +198,13 @@ bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieLis if (cookie.domain().isEmpty()) { cookie.setDomain(defaultDomain); } else { + // Ensure the domain starts with a dot if its field was not empty + // in the HTTP header. There are some servers that forget the + // leading dot and this is actually forbidden according to RFC 2109, + // but all browsers accept it anyway so we do that as well. + if (!cookie.domain().startsWith(QLatin1Char('.'))) + cookie.setDomain(QLatin1Char('.') + cookie.domain()); + QString domain = cookie.domain(); if (!(isParentDomain(domain, defaultDomain) || isParentDomain(defaultDomain, domain))) { diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index 4eb53bf407..9ab4057488 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -59,9 +59,10 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() \class QNetworkReply \since 4.4 \brief The QNetworkReply class contains the data and headers for a request - posted with QNetworkAccessManager + sent with QNetworkAccessManager \reentrant + \ingroup network \inmodule QtNetwork The QNetworkReply class contains the data and meta data related to diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 33bc57b0e0..c91c608d93 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -48,11 +48,13 @@ #include "QtCore/qlocale.h" #include "QtCore/qdatetime.h" +#include <ctype.h> + QT_BEGIN_NAMESPACE /*! \class QNetworkRequest - \brief The QNetworkRequest class holds one request to be sent with the Network Access API. + \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager. \since 4.4 \ingroup network @@ -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/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index 64c51527b0..afd3570e4c 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -161,9 +161,11 @@ public Q_SLOTS: cond.wakeOne(); } #ifndef QT_NO_THREAD - if (!wait(QHOSTINFO_THREAD_WAIT)) + if (!wait(QHOSTINFO_THREAD_WAIT)) { terminate(); - wait(); + // Don't wait forever; see QTBUG-5296. + wait(QHOSTINFO_THREAD_WAIT); + } #endif } diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp index c3b89ed69b..6f92424068 100644 --- a/src/network/kernel/qnetworkproxy_win.cpp +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -399,7 +399,12 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro if (isBypassed(query.peerHostName(), sp->proxyBypass)) return sp->defaultResult; - return parseServerList(query, sp->proxyServerList); + QList<QNetworkProxy> result = parseServerList(query, sp->proxyServerList); + // In some cases, this was empty. See SF task 00062670 + if (result.isEmpty()) + return sp->defaultResult; + + return result; } QT_END_NAMESPACE diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 86ccef244f..8b4f364cb3 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -462,6 +462,7 @@ QAbstractSocketPrivate::QAbstractSocketPrivate() isBuffered(false), blockingTimeout(30000), connectTimer(0), + disconnectTimer(0), connectTimeElapsed(0), hostLookupId(-1), socketType(QAbstractSocket::UnknownSocketType), @@ -497,9 +498,10 @@ void QAbstractSocketPrivate::resetSocketLayer() socketEngine = 0; cachedSocketDescriptor = -1; } - if (connectTimer) { + if (connectTimer) connectTimer->stop(); - } + if (disconnectTimer) + disconnectTimer->stop(); } /*! \internal @@ -669,11 +671,11 @@ bool QAbstractSocketPrivate::canWriteNotification() if (socketEngine) { #if defined (Q_OS_WIN) - if (!writeBuffer.isEmpty()) - socketEngine->setWriteNotificationEnabled(true); + if (!writeBuffer.isEmpty()) + socketEngine->setWriteNotificationEnabled(true); #else - if (writeBuffer.isEmpty()) - socketEngine->setWriteNotificationEnabled(false); + if (writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0) + socketEngine->setWriteNotificationEnabled(false); #endif } @@ -710,11 +712,17 @@ void QAbstractSocketPrivate::connectionNotification() bool QAbstractSocketPrivate::flush() { Q_Q(QAbstractSocket); - if (!socketEngine || !socketEngine->isValid() || writeBuffer.isEmpty()) { + if (!socketEngine || !socketEngine->isValid() || (writeBuffer.isEmpty() + && socketEngine->bytesToWrite() == 0)) { #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s", socketEngine->isValid() ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no"); #endif + + // this covers the case when the buffer was empty, but we had to wait for the socket engine to finish + if (state == QAbstractSocket::ClosingState) + q->disconnectFromHost(); + return false; } @@ -751,7 +759,8 @@ bool QAbstractSocketPrivate::flush() } } - if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()) + if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled() + && !socketEngine->bytesToWrite()) socketEngine->setWriteNotificationEnabled(false); if (state == QAbstractSocket::ClosingState) q->disconnectFromHost(); @@ -1087,6 +1096,15 @@ void QAbstractSocketPrivate::_q_abortConnectionAttempt() } } +void QAbstractSocketPrivate::_q_forceDisconnect() +{ + Q_Q(QAbstractSocket); + if (socketEngine && socketEngine->isValid() && state == QAbstractSocket::ClosingState) { + socketEngine->close(); + q->disconnectFromHost(); + } +} + /*! \internal Reads data from the socket layer into the read buffer. Returns @@ -1571,13 +1589,20 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock } /*! - Sets the option \a option to the value described by \a value. + \since 4.6 + Sets the given \a option to the value described by \a value. \sa socketOption() - \since 4.6 */ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) { +#ifndef QT_NO_OPENSSL + if (QSslSocket *sslSocket = qobject_cast<QSslSocket*>(this)) { + sslSocket->setSocketOption(option, value); + return; + } +#endif + if (!d_func()->socketEngine) return; @@ -1593,13 +1618,19 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons } /*! + \since 4.6 Returns the value of the \a option option. \sa setSocketOption() - \since 4.6 */ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) { +#ifndef QT_NO_OPENSSL + if (QSslSocket *sslSocket = qobject_cast<QSslSocket*>(this)) { + return sslSocket->socketOption(option); + } +#endif + if (!d_func()->socketEngine) return QVariant(); @@ -2334,7 +2365,22 @@ void QAbstractSocket::disconnectFromHostImplementation() } // Wait for pending data to be written. - if (d->socketEngine && d->socketEngine->isValid() && d->writeBuffer.size() > 0) { + if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0 + || d->socketEngine->bytesToWrite() > 0)) { + // hack: when we are waiting for the socket engine to write bytes (only + // possible when using Socks5 or HTTP socket engine), then close + // anyway after 2 seconds. This is to prevent a timeout on Mac, where we + // sometimes just did not get the write notifier from the underlying + // CFSocket and no progress was made. + if (d->writeBuffer.size() == 0 && d->socketEngine->bytesToWrite() > 0) { + if (!d->disconnectTimer) { + d->disconnectTimer = new QTimer(this); + connect(d->disconnectTimer, SIGNAL(timeout()), this, + SLOT(_q_forceDisconnect()), Qt::DirectConnection); + } + if (!d->disconnectTimer->isActive()) + d->disconnectTimer->start(2000); + } d->socketEngine->setWriteNotificationEnabled(true); #if defined(QABSTRACTSOCKET_DEBUG) diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 5d94a01663..5cfae17711 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -216,6 +216,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_startConnecting(const QHostInfo &)) Q_PRIVATE_SLOT(d_func(), void _q_abortConnectionAttempt()) Q_PRIVATE_SLOT(d_func(), void _q_testConnection()) + Q_PRIVATE_SLOT(d_func(), void _q_forceDisconnect()) #ifdef QT3_SUPPORT public: diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index 8ccddd3c8c..acf82bff3e 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -93,6 +93,7 @@ public: void _q_startConnecting(const QHostInfo &hostInfo); void _q_testConnection(); void _q_abortConnectionAttempt(); + void _q_forceDisconnect(); bool readSocketNotifierCalled; bool readSocketNotifierState; @@ -148,6 +149,7 @@ public: int blockingTimeout; QTimer *connectTimer; + QTimer *disconnectTimer; int connectTimeElapsed; int hostLookupId; diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index c639092f90..14b3c81925 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -126,6 +126,8 @@ public: virtual qint64 pendingDatagramSize() const = 0; #endif + virtual qint64 bytesToWrite() const = 0; + virtual int option(SocketOption option) const = 0; virtual bool setOption(SocketOption option, int value) = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index fb61dbf258..5c28318799 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -276,6 +276,16 @@ qint64 QHttpSocketEngine::pendingDatagramSize() const } #endif // QT_NO_UDPSOCKET +qint64 QHttpSocketEngine::bytesToWrite() const +{ + Q_D(const QHttpSocketEngine); + if (d->socket) { + return d->socket->bytesToWrite(); + } else { + return 0; + } +} + int QHttpSocketEngine::option(SocketOption option) const { Q_D(const QHttpSocketEngine); diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index a423116e6f..76430db104 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -110,6 +110,8 @@ public: qint64 pendingDatagramSize() const; #endif // QT_NO_UDPSOCKET + qint64 bytesToWrite() const; + int option(SocketOption option) const; bool setOption(SocketOption option, int value); diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index 5ffe0c011f..e09e5474cb 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -216,24 +216,14 @@ void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) timeout.tv_sec = msec / 1000; timeout.tv_usec = (msec % 1000) * 1000; - // timeout can not be 0 or else select will return an error. - if (0 == msec) - timeout.tv_usec = 1000; - int result = -1; - // on Linux timeout will be updated by select, but _not_ on other systems. - QTime timer; - timer.start(); - while (pendingConnections.isEmpty() && (-1 == msec || timer.elapsed() < msec)) { - result = ::select(listenSocket + 1, &readfds, 0, 0, &timeout); - if (-1 == result && errno != EINTR) { - setError(QLatin1String("QLocalServer::waitForNewConnection")); - closeServer(); - break; - } - if (result > 0) - _q_onNewConnection(); + result = qt_safe_select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout); + if (-1 == result) { + setError(QLatin1String("QLocalServer::waitForNewConnection")); + closeServer(); } + if (result > 0) + _q_onNewConnection(); if (timedOut) *timedOut = (result == 0); } diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp index 8a745ab0bb..d812d888ca 100644 --- a/src/network/socket/qlocalsocket_win.cpp +++ b/src/network/socket/qlocalsocket_win.cpp @@ -363,7 +363,8 @@ bool QLocalSocket::canReadLine() const Q_D(const QLocalSocket); if (state() != ConnectedState) return false; - return (d->readBuffer.indexOf('\n') != -1 || QIODevice::canReadLine()); + return (QIODevice::canReadLine() + || d->readBuffer.indexOf('\n', d->actualReadBufferSize) != -1); } void QLocalSocket::close() diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index e7f8401836..ecf5ad9d82 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -194,82 +194,82 @@ void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, Er switch (errorString) { case NonBlockingInitFailedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize non-blocking socket")); + socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket"); break; case BroadcastingInitFailedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize broadcast socket")); + socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket"); break; case NoIpV6ErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Attempt to use IPv6 socket on a platform with no IPv6 support")); + socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support"); break; case RemoteHostClosedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The remote host closed the connection")); + socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection"); break; case TimeOutErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network operation timed out")); + socketErrorString = QNativeSocketEngine::tr("Network operation timed out"); break; case ResourceErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Out of resources")); + socketErrorString = QNativeSocketEngine::tr("Out of resources"); break; case OperationUnsupportedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unsupported socket operation")); + socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation"); break; case ProtocolUnsupportedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Protocol type not supported")); + socketErrorString = QNativeSocketEngine::tr("Protocol type not supported"); break; case InvalidSocketErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Invalid socket descriptor")); + socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor"); break; case HostUnreachableErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Host unreachable")); + socketErrorString = QNativeSocketEngine::tr("Host unreachable"); break; case NetworkUnreachableErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network unreachable")); + socketErrorString = QNativeSocketEngine::tr("Network unreachable"); break; case AccessErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Permission denied")); + socketErrorString = QNativeSocketEngine::tr("Permission denied"); break; case ConnectionTimeOutErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection timed out")); + socketErrorString = QNativeSocketEngine::tr("Connection timed out"); break; case ConnectionRefusedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection refused")); + socketErrorString = QNativeSocketEngine::tr("Connection refused"); break; case AddressInuseErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The bound address is already in use")); + socketErrorString = QNativeSocketEngine::tr("The bound address is already in use"); break; case AddressNotAvailableErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is not available")); + socketErrorString = QNativeSocketEngine::tr("The address is not available"); break; case AddressProtectedErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is protected")); + socketErrorString = QNativeSocketEngine::tr("The address is protected"); break; case DatagramTooLargeErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Datagram was too large to send")); + socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send"); break; case SendDatagramErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to send a message")); + socketErrorString = QNativeSocketEngine::tr("Unable to send a message"); break; case ReceiveDatagramErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to receive a message")); + socketErrorString = QNativeSocketEngine::tr("Unable to receive a message"); break; case WriteErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to write")); + socketErrorString = QNativeSocketEngine::tr("Unable to write"); break; case ReadErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network error")); + socketErrorString = QNativeSocketEngine::tr("Network error"); break; case PortInuseErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Another socket is already listening on the same port")); + socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port"); break; case NotSocketErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Operation on non-socket")); + socketErrorString = QNativeSocketEngine::tr("Operation on non-socket"); break; case InvalidProxyTypeString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The proxy type is invalid for this operation")); + socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation"); break; case UnknownSocketErrorString: - socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unknown error")); + socketErrorString = QNativeSocketEngine::tr("Unknown error"); break; } } @@ -754,6 +754,12 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 size) return d->nativeWrite(data, size); } + +qint64 QNativeSocketEngine::bytesToWrite() const +{ + return 0; +} + /*! Reads up to \a maxSize bytes into \a data from the socket. Returns the number of bytes read, or -1 if an error occurred. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 1f6a243245..a03d8f1eeb 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -135,6 +135,8 @@ public: bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; + qint64 bytesToWrite() const; + qint64 receiveBufferSize() const; void setReceiveBufferSize(qint64 bufferSize); diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 30074cf2c2..bd60ad1343 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1235,6 +1235,9 @@ void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketEr if (!readNotificationPending) connectData->readBuffer.clear(); emitReadNotification(); + data->controlSocket->close(); + // cause a disconnect in the outer socket + emitWriteNotification(); } else if (socks5State == Uninitialized || socks5State == AuthenticationMethodsSent || socks5State == Authenticating @@ -1245,6 +1248,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketEr } else { q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString()); emitReadNotification(); + emitWriteNotification(); } } @@ -1623,6 +1627,16 @@ qint64 QSocks5SocketEngine::pendingDatagramSize() const } #endif // QT_NO_UDPSOCKET +qint64 QSocks5SocketEngine::bytesToWrite() const +{ + Q_D(const QSocks5SocketEngine); + if (d->data && d->data->controlSocket) { + return d->data->controlSocket->bytesToWrite(); + } else { + return 0; + } +} + int QSocks5SocketEngine::option(SocketOption option) const { Q_D(const QSocks5SocketEngine); diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 7cb0920a48..240251771e 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -100,6 +100,8 @@ public: qint64 pendingDatagramSize() const; #endif // QT_NO_UDPSOCKET + qint64 bytesToWrite() const; + int option(SocketOption option) const; bool setOption(SocketOption option, int value); diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index 62fcd4ce8a..e912626aee 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -91,6 +91,7 @@ */ #include "qsslerror.h" +#include "qsslsocket.h" #ifndef QT_NO_DEBUG_STREAM #include <QtCore/qdebug.h> @@ -209,81 +210,79 @@ QString QSslError::errorString() const QString errStr; switch (d->error) { case NoError: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No error")); + errStr = QSslSocket::tr("No error"); break; case UnableToGetIssuerCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate could not be found")); + errStr = QSslSocket::tr("The issuer certificate could not be found"); break; case UnableToDecryptCertificateSignature: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate signature could not be decrypted")); + errStr = QSslSocket::tr("The certificate signature could not be decrypted"); break; case UnableToDecodeIssuerPublicKey: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The public key in the certificate could not be read")); + errStr = QSslSocket::tr("The public key in the certificate could not be read"); break; case CertificateSignatureFailed: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The signature of the certificate is invalid")); + errStr = QSslSocket::tr("The signature of the certificate is invalid"); break; case CertificateNotYetValid: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is not yet valid")); + errStr = QSslSocket::tr("The certificate is not yet valid"); break; case CertificateExpired: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate has expired")); + errStr = QSslSocket::tr("The certificate has expired"); break; case InvalidNotBeforeField: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notBefore field contains an invalid time")); + errStr = QSslSocket::tr("The certificate's notBefore field contains an invalid time"); break; case InvalidNotAfterField: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notAfter field contains an invalid time")); + errStr = QSslSocket::tr("The certificate's notAfter field contains an invalid time"); break; case SelfSignedCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is self-signed, and untrusted")); + errStr = QSslSocket::tr("The certificate is self-signed, and untrusted"); break; case SelfSignedCertificateInChain: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root certificate of the certificate chain is self-signed, and untrusted")); + errStr = QSslSocket::tr("The root certificate of the certificate chain is self-signed, and untrusted"); break; case UnableToGetLocalIssuerCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate of a locally looked up certificate could not be found")); + errStr = QSslSocket::tr("The issuer certificate of a locally looked up certificate could not be found"); break; case UnableToVerifyFirstCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No certificates could be verified")); + errStr = QSslSocket::tr("No certificates could be verified"); break; case InvalidCaCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "One of the CA certificates is invalid")); + errStr = QSslSocket::tr("One of the CA certificates is invalid"); break; case PathLengthExceeded: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The basicConstraints pathlength parameter has been exceeded")); + errStr = QSslSocket::tr("The basicConstraints path length parameter has been exceeded"); break; case InvalidPurpose: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The supplied certificate is unsuited for this purpose")); + errStr = QSslSocket::tr("The supplied certificate is unsuitable for this purpose"); break; case CertificateUntrusted: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is not trusted for this purpose")); + errStr = QSslSocket::tr("The root CA certificate is not trusted for this purpose"); break; case CertificateRejected: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is marked to reject the specified purpose")); + errStr = QSslSocket::tr("The root CA certificate is marked to reject the specified purpose"); break; case SubjectIssuerMismatch: // hostname mismatch - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, - "The current candidate issuer certificate was rejected because its" - " subject name did not match the issuer name of the current certificate")); + errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because its" + " subject name did not match the issuer name of the current certificate"); break; case AuthorityIssuerSerialNumberMismatch: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The current candidate issuer certificate was rejected because" - " its issuer name and serial number was present and did not match the" - " authority key identifier of the current certificate")); + errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because" + " its issuer name and serial number was present and did not match the" + " authority key identifier of the current certificate"); break; case NoPeerCertificate: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The peer did not present any certificate")); + errStr = QSslSocket::tr("The peer did not present any certificate"); break; case HostNameMismatch: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, - "The host name did not match any of the valid hosts" - " for this certificate")); + errStr = QSslSocket::tr("The host name did not match any of the valid hosts" + " for this certificate"); break; case NoSslSupport: break; default: - errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "Unknown error")); + errStr = QSslSocket::tr("Unknown error"); break; } diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 0e9cb4f39a..e53d8a42ee 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -149,6 +149,13 @@ This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (\l{http://www.openssl.org/}). + \note Be aware of the difference between the bytesWritten() signal and + the encryptedBytesWritten() signal. For a QTcpSocket, bytesWritten() + will get emitted as soon as data has been written to the TCP socket. + For a QSslSocket, bytesWritten() will get emitted when the data + is being encrypted and encryptedBytesWritten() + will get emitted as soon as data has been written to the TCP socket. + \sa QSslCertificate, QSslCipher, QSslError */ @@ -461,6 +468,34 @@ bool QSslSocket::setSocketDescriptor(int socketDescriptor, SocketState state, Op } /*! + \since 4.6 + Sets the given \a option to the value described by \a value. + + \sa socketOption() +*/ +void QSslSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) +{ + Q_D(QSslSocket); + if (d->plainSocket) + d->plainSocket->setSocketOption(option, value); +} + +/*! + \since 4.6 + Returns the value of the \a option option. + + \sa setSocketOption() +*/ +QVariant QSslSocket::socketOption(QAbstractSocket::SocketOption option) +{ + Q_D(QSslSocket); + if (d->plainSocket) + return d->plainSocket->socketOption(option); + else + return QVariant(); +} + +/*! Returns the current mode for the socket; either UnencryptedMode, where QSslSocket behaves identially to QTcpSocket, or one of SslClientMode or SslServerMode, where the client is either negotiating or in encrypted @@ -684,6 +719,8 @@ void QSslSocket::close() qDebug() << "QSslSocket::close()"; #endif Q_D(QSslSocket); + if (d->plainSocket) + d->plainSocket->close(); QTcpSocket::close(); // must be cleared, reading/writing not possible on closed socket: @@ -1717,6 +1754,11 @@ qint64 QSslSocket::readData(char *data, qint64 maxlen) #ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocket::readData(" << (void *)data << ',' << maxlen << ") ==" << readBytes; #endif + + // possibly trigger another transmit() to decrypt more data from the socket + if (d->readBuffer.isEmpty() && d->plainSocket->bytesAvailable()) + QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection); + return readBytes; } @@ -2111,6 +2153,16 @@ void QSslSocketPrivate::_q_flushWriteBuffer() q->flush(); } +/*! + \internal +*/ +void QSslSocketPrivate::_q_flushReadBuffer() +{ + // trigger a read from the plainSocket into SSL + if (mode != QSslSocket::UnencryptedMode) + transmit(); +} + QT_END_NAMESPACE // For private slots diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index a41e600979..82cda357e2 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -90,6 +90,10 @@ public: bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState, OpenMode openMode = ReadWrite); + // ### Qt 5: Make virtual + void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value); + QVariant socketOption(QAbstractSocket::SocketOption option); + SslMode mode() const; bool isEncrypted() const; @@ -203,6 +207,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_readyReadSlot()) Q_PRIVATE_SLOT(d_func(), void _q_bytesWrittenSlot(qint64)) Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer()) + Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer()) friend class QSslSocketBackendPrivate; }; diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 6f7e55acab..743722f9e3 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -324,7 +324,7 @@ init_context: // Check if the certificate matches the private key. if (!q_SSL_CTX_check_private_key(ctx)) { - q->setErrorString(QSslSocket::tr("Private key does not certificate public key, %1").arg(SSL_ERRORSTR())); + q->setErrorString(QSslSocket::tr("Private key does not certify public key, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); return false; } diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 24d4ebe187..ee21956fb9 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -120,6 +120,7 @@ public: void _q_readyReadSlot(); void _q_bytesWrittenSlot(qint64); void _q_flushWriteBuffer(); + void _q_flushReadBuffer(); // Platform specific functions virtual void startClientEncryption() = 0; |