diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel_p.h | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend.cpp | 1189 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend_p.h | 169 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.cpp | 1 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.h | 1 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.cpp | 21 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.h | 4 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 1 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate.cpp | 101 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate.h | 6 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate_p.h | 5 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 21 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 17 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols.cpp | 8 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols_p.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_p.h | 2 |
17 files changed, 163 insertions, 1391 deletions
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 8400f62ab9..c159f1a0c5 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -163,8 +163,6 @@ public: bool isSocketWaiting() const; bool isSocketReading() const; - friend class QNetworkAccessHttpBackend; - protected slots: void _q_receiveReply(); void _q_bytesWritten(qint64 bytes); // proceed sending diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp deleted file mode 100644 index 64a22aa0e8..0000000000 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ /dev/null @@ -1,1189 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//#define QNETWORKACCESSHTTPBACKEND_DEBUG - -#include "qnetworkaccesshttpbackend_p.h" -#include "qnetworkaccessmanager_p.h" -#include "qnetworkaccesscache_p.h" -#include "qabstractnetworkcache.h" -#include "qnetworkrequest.h" -#include "qnetworkreply.h" -#include "QtNetwork/private/qnetworksession_p.h" -#include "qnetworkrequest_p.h" -#include "qnetworkcookie_p.h" -#include "QtCore/qdatetime.h" -#include "QtCore/qelapsedtimer.h" -#include "QtNetwork/qsslconfiguration.h" -#include "qhttpthreaddelegate_p.h" -#include "qthread.h" - -#ifndef QT_NO_HTTP - -#include <string.h> // for strchr - -Q_DECLARE_METATYPE(QSharedPointer<char>) - -QT_BEGIN_NAMESPACE - -class QNetworkProxy; - -static inline bool isSeparator(register char c) -{ - static const char separators[] = "()<>@,;:\\\"/[]?={}"; - return isLWS(c) || strchr(separators, c) != 0; -} - -// ### merge with nextField in cookiejar.cpp -static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header) -{ - // The HTTP header is of the form: - // header = #1(directives) - // directives = token | value-directive - // value-directive = token "=" (token | quoted-string) - QHash<QByteArray, QByteArray> result; - - int pos = 0; - while (true) { - // skip spaces - pos = nextNonWhitespace(header, pos); - if (pos == header.length()) - return result; // end of parsing - - // pos points to a non-whitespace - int comma = header.indexOf(',', pos); - int equal = header.indexOf('=', pos); - if (comma == pos || equal == pos) - // huh? Broken header. - return result; - - // The key name is delimited by either a comma, an equal sign or the end - // of the header, whichever comes first - int end = comma; - if (end == -1) - end = header.length(); - if (equal != -1 && end > equal) - end = equal; // equal sign comes before comma/end - QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower(); - pos = end + 1; - - if (uint(equal) < uint(comma)) { - // case: token "=" (token | quoted-string) - // skip spaces - pos = nextNonWhitespace(header, pos); - if (pos == header.length()) - // huh? Broken header - return result; - - QByteArray value; - value.reserve(header.length() - pos); - if (header.at(pos) == '"') { - // case: quoted-string - // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - // qdtext = <any TEXT except <">> - // quoted-pair = "\" CHAR - ++pos; - while (pos < header.length()) { - register char c = header.at(pos); - if (c == '"') { - // end of quoted text - break; - } else if (c == '\\') { - ++pos; - if (pos >= header.length()) - // broken header - return result; - c = header.at(pos); - } - - value += c; - ++pos; - } - } else { - // case: token - while (pos < header.length()) { - register char c = header.at(pos); - if (isSeparator(c)) - break; - value += c; - ++pos; - } - } - - result.insert(key, value); - - // find the comma now: - comma = header.indexOf(',', pos); - if (comma == -1) - return result; // end of parsing - pos = comma + 1; - } else { - // case: token - // key is already set - result.insert(key, QByteArray()); - } - } -} - -QNetworkAccessBackend * -QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op, - const QNetworkRequest &request) const -{ - // check the operation - switch (op) { - case QNetworkAccessManager::GetOperation: - case QNetworkAccessManager::PostOperation: - case QNetworkAccessManager::HeadOperation: - case QNetworkAccessManager::PutOperation: - case QNetworkAccessManager::DeleteOperation: - case QNetworkAccessManager::CustomOperation: - break; - - default: - // no, we can't handle this request - return 0; - } - - QUrl url = request.url(); - QString scheme = url.scheme().toLower(); - if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) - return new QNetworkAccessHttpBackend; - - return 0; -} - -QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() - : QNetworkAccessBackend() - , statusCode(0) - , pendingDownloadDataEmissions(new QAtomicInt()) - , pendingDownloadProgressEmissions(new QAtomicInt()) - , loadingFromCache(false) - , usingZerocopyDownloadBuffer(false) -#ifndef QT_NO_OPENSSL - , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false) -#endif - , resumeOffset(0) -{ -} - -QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend() -{ - // This will do nothing if the request was already finished or aborted - emit abortHttpRequest(); - -#ifndef QT_NO_OPENSSL - delete pendingSslConfiguration; -#endif -} - -/* - For a given httpRequest - 1) If AlwaysNetwork, return - 2) If we have a cache entry for this url populate headers so the server can return 304 - 3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true - */ -bool QNetworkAccessHttpBackend::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest) -{ - QNetworkRequest::CacheLoadControl CacheLoadControlAttribute = - (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(); - if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) { - // If the request does not already specify preferred cache-control - // force reload from the network and tell any caching proxy servers to reload too - if (!request().rawHeaderList().contains("Cache-Control")) { - httpRequest.setHeaderField("Cache-Control", "no-cache"); - httpRequest.setHeaderField("Pragma", "no-cache"); - } - return false; - } - - // The disk cache API does not currently support partial content retrieval. - // That is why we don't use the disk cache for any such requests. - if (request().hasRawHeader("Range")) - return false; - - QAbstractNetworkCache *nc = networkCache(); - if (!nc) - return false; // no local cache - - QNetworkCacheMetaData metaData = nc->metaData(url()); - if (!metaData.isValid()) - return false; // not in cache - - if (!metaData.saveToDisk()) - return false; - - QNetworkHeadersPrivate cacheHeaders; - QNetworkHeadersPrivate::RawHeadersList::ConstIterator it; - cacheHeaders.setAllRawHeaders(metaData.rawHeaders()); - - it = cacheHeaders.findRawHeader("etag"); - if (it != cacheHeaders.rawHeaders.constEnd()) - httpRequest.setHeaderField("If-None-Match", it->second); - - QDateTime lastModified = metaData.lastModified(); - if (lastModified.isValid()) - httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified)); - - it = cacheHeaders.findRawHeader("Cache-Control"); - if (it != cacheHeaders.rawHeaders.constEnd()) { - QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second); - if (cacheControl.contains("must-revalidate")) - return false; - } - - QDateTime currentDateTime = QDateTime::currentDateTime(); - QDateTime expirationDate = metaData.expirationDate(); - -#if 0 - /* - * age_value - * is the value of Age: header received by the cache with - * this response. - * date_value - * is the value of the origin server's Date: header - * request_time - * is the (local) time when the cache made the request - * that resulted in this cached response - * response_time - * is the (local) time when the cache received the - * response - * now - * is the current (local) time - */ - int age_value = 0; - it = cacheHeaders.findRawHeader("age"); - if (it != cacheHeaders.rawHeaders.constEnd()) - age_value = it->second.toInt(); - - QDateTime dateHeader; - int date_value = 0; - it = cacheHeaders.findRawHeader("date"); - if (it != cacheHeaders.rawHeaders.constEnd()) { - dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second); - date_value = dateHeader.toTime_t(); - } - - int now = currentDateTime.toUTC().toTime_t(); - int request_time = now; - int response_time = now; - - // Algorithm from RFC 2616 section 13.2.3 - int apparent_age = qMax(0, response_time - date_value); - int corrected_received_age = qMax(apparent_age, age_value); - int response_delay = response_time - request_time; - int corrected_initial_age = corrected_received_age + response_delay; - int resident_time = now - response_time; - int current_age = corrected_initial_age + resident_time; - - // RFC 2616 13.2.4 Expiration Calculations - if (!expirationDate.isValid()) { - if (lastModified.isValid()) { - int diff = currentDateTime.secsTo(lastModified); - expirationDate = lastModified; - expirationDate.addSecs(diff / 10); - if (httpRequest.headerField("Warning").isEmpty()) { - QDateTime dt; - dt.setTime_t(current_age); - if (dt.daysTo(currentDateTime) > 1) - httpRequest.setHeaderField("Warning", "113"); - } - } - } - - // the cache-saving code below sets the expirationDate with date+max_age - // if "max-age" is present, or to Expires otherwise - int freshness_lifetime = dateHeader.secsTo(expirationDate); - bool response_is_fresh = (freshness_lifetime > current_age); -#else - bool response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0; -#endif - - if (!response_is_fresh) - return false; - -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "response_is_fresh" << CacheLoadControlAttribute; -#endif - return sendCacheContents(metaData); -} - -static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio) -{ - switch (prio) { - case QNetworkRequest::LowPriority: - return QHttpNetworkRequest::LowPriority; - case QNetworkRequest::HighPriority: - return QHttpNetworkRequest::HighPriority; - case QNetworkRequest::NormalPriority: - default: - return QHttpNetworkRequest::NormalPriority; - } -} - -void QNetworkAccessHttpBackend::postRequest() -{ - QThread *thread = 0; - if (isSynchronous()) { - // A synchronous HTTP request uses its own thread - thread = new QThread(); - QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - thread->start(); - } else if (!manager->httpThread) { - // We use the manager-global thread. - // At some point we could switch to having multiple threads if it makes sense. - manager->httpThread = new QThread(); - QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater())); - manager->httpThread->start(); -#ifndef QT_NO_NETWORKPROXY - qRegisterMetaType<QNetworkProxy>("QNetworkProxy"); -#endif -#ifndef QT_NO_OPENSSL - qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); - qRegisterMetaType<QSslConfiguration>("QSslConfiguration"); -#endif - qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >"); - qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest"); - qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); - qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>"); - - thread = manager->httpThread; - } else { - // Asynchronous request, thread already exists - thread = manager->httpThread; - } - - QUrl url = request().url(); - httpRequest.setUrl(url); - - bool ssl = url.scheme().toLower() == QLatin1String("https"); - setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl); - httpRequest.setSsl(ssl); - - -#ifndef QT_NO_NETWORKPROXY - QNetworkProxy transparentProxy, cacheProxy; - - foreach (const QNetworkProxy &p, proxyList()) { - // use the first proxy that works - // for non-encrypted connections, any transparent or HTTP proxy - // for encrypted, only transparent proxies - if (!ssl - && (p.capabilities() & QNetworkProxy::CachingCapability) - && (p.type() == QNetworkProxy::HttpProxy || - p.type() == QNetworkProxy::HttpCachingProxy)) { - cacheProxy = p; - transparentProxy = QNetworkProxy::NoProxy; - break; - } - if (p.isTransparentProxy()) { - transparentProxy = p; - cacheProxy = QNetworkProxy::NoProxy; - break; - } - } - - // check if at least one of the proxies - if (transparentProxy.type() == QNetworkProxy::DefaultProxy && - cacheProxy.type() == QNetworkProxy::DefaultProxy) { - // unsuitable proxies - QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, - Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), - Q_ARG(QString, tr("No suitable proxy found"))); - QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection); - return; - } -#endif - - - bool loadedFromCache = false; - httpRequest.setPriority(convert(request().priority())); - - switch (operation()) { - case QNetworkAccessManager::GetOperation: - httpRequest.setOperation(QHttpNetworkRequest::Get); - loadedFromCache = loadFromCacheIfAllowed(httpRequest); - break; - - case QNetworkAccessManager::HeadOperation: - httpRequest.setOperation(QHttpNetworkRequest::Head); - loadedFromCache = loadFromCacheIfAllowed(httpRequest); - break; - - case QNetworkAccessManager::PostOperation: - invalidateCache(); - httpRequest.setOperation(QHttpNetworkRequest::Post); - createUploadByteDevice(); - break; - - case QNetworkAccessManager::PutOperation: - invalidateCache(); - httpRequest.setOperation(QHttpNetworkRequest::Put); - createUploadByteDevice(); - break; - - case QNetworkAccessManager::DeleteOperation: - invalidateCache(); - httpRequest.setOperation(QHttpNetworkRequest::Delete); - break; - - case QNetworkAccessManager::CustomOperation: - invalidateCache(); // for safety reasons, we don't know what the operation does - httpRequest.setOperation(QHttpNetworkRequest::Custom); - createUploadByteDevice(); - httpRequest.setCustomVerb(request().attribute( - QNetworkRequest::CustomVerbAttribute).toByteArray()); - break; - - default: - break; // can't happen - } - - if (loadedFromCache) { - // commented this out since it will be called later anyway - // by copyFinished() - //QNetworkAccessBackend::finished(); - return; // no need to send the request! :) - } - - QList<QByteArray> headers = request().rawHeaderList(); - if (resumeOffset != 0) { - if (headers.contains("Range")) { - // Need to adjust resume offset for user specified range - - headers.removeOne("Range"); - - // We've already verified that requestRange starts with "bytes=", see canResume. - QByteArray requestRange = request().rawHeader("Range").mid(6); - - int index = requestRange.indexOf('-'); - - quint64 requestStartOffset = requestRange.left(index).toULongLong(); - quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong(); - - requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) + - '-' + QByteArray::number(requestEndOffset); - - httpRequest.setHeaderField("Range", requestRange); - } else { - httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-'); - } - } - - foreach (const QByteArray &header, headers) - httpRequest.setHeaderField(header, request().rawHeader(header)); - - if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true) - httpRequest.setPipeliningAllowed(true); - - if (static_cast<QNetworkRequest::LoadControl> - (request().attribute(QNetworkRequest::AuthenticationReuseAttribute, - QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual) - httpRequest.setWithCredentials(false); - - - // Create the HTTP thread delegate - QHttpThreadDelegate *delegate = new QHttpThreadDelegate; -#ifndef Q_NO_BEARERMANAGEMENT - QVariant v(property("_q_networksession")); - if (v.isValid()) - delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v); -#endif - - // For the synchronous HTTP, this is the normal way the delegate gets deleted - // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished - connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater())); - - // Set the properties it needs - delegate->httpRequest = httpRequest; -#ifndef QT_NO_NETWORKPROXY - delegate->cacheProxy = cacheProxy; - delegate->transparentProxy = transparentProxy; -#endif - delegate->ssl = ssl; -#ifndef QT_NO_OPENSSL - if (ssl) - delegate->incomingSslConfiguration = request().sslConfiguration(); -#endif - - // Do we use synchronous HTTP? - delegate->synchronous = isSynchronous(); - - // The authentication manager is used to avoid the BlockingQueuedConnection communication - // from HTTP thread to user thread in some cases. - delegate->authenticationManager = manager->authenticationManager; - - if (!isSynchronous()) { - // Tell our zerocopy policy to the delegate - delegate->downloadBufferMaximumSize = - request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong(); - - // These atomic integers are used for signal compression - delegate->pendingDownloadData = pendingDownloadDataEmissions; - delegate->pendingDownloadProgress = pendingDownloadProgressEmissions; - - // Connect the signals of the delegate to us - connect(delegate, SIGNAL(downloadData(QByteArray)), - this, SLOT(replyDownloadData(QByteArray)), - Qt::QueuedConnection); - connect(delegate, SIGNAL(downloadFinished()), - this, SLOT(replyFinished()), - Qt::QueuedConnection); - connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), - this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), - Qt::QueuedConnection); - connect(delegate, SIGNAL(downloadProgress(qint64,qint64)), - this, SLOT(replyDownloadProgressSlot(qint64,qint64)), - Qt::QueuedConnection); - connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)), - this, SLOT(httpError(QNetworkReply::NetworkError, const QString)), - Qt::QueuedConnection); -#ifndef QT_NO_OPENSSL - connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)), - this, SLOT(replySslConfigurationChanged(QSslConfiguration)), - Qt::QueuedConnection); -#endif - // Those need to report back, therefire BlockingQueuedConnection - connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), - this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)), - Qt::BlockingQueuedConnection); -#ifndef QT_NO_NETWORKPROXY - connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - Qt::BlockingQueuedConnection); -#endif -#ifndef QT_NO_OPENSSL - connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)), - this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)), - Qt::BlockingQueuedConnection); -#endif - // This signal we will use to start the request. - connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest())); - connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest())); - - if (uploadByteDevice) { - QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice = - new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size()); - if (uploadByteDevice->isResetDisabled()) - forwardUploadDevice->disableReset(); - forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread() - delegate->httpRequest.setUploadByteDevice(forwardUploadDevice); - - // From main thread to user thread: - QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)), - forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection); - QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), - forwardUploadDevice, SIGNAL(readyRead()), - Qt::QueuedConnection); - - // From http thread to user thread: - QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), - this, SLOT(wantUploadDataSlot(qint64))); - QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), - this, SLOT(sentUploadDataSlot(qint64))); - connect(forwardUploadDevice, SIGNAL(resetData(bool*)), - this, SLOT(resetUploadDataSlot(bool*)), - Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! - } - } else if (isSynchronous()) { - connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection); - - if (uploadByteDevice) { - // For the synchronous HTTP use case the use thread (this one here) is blocked - // so we cannot use the asynchronous upload architecture. - // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly - // use the uploadByteDevice provided to us by the QNetworkReplyImpl. - // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread - // since it only wraps a QRingBuffer - delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data()); - } - } - - - // Move the delegate to the http thread - delegate->moveToThread(thread); - // This call automatically moves the uploadDevice too for the asynchronous case. - - // Send an signal to the delegate so it starts working in the other thread - if (isSynchronous()) { - emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done - - if (delegate->incomingErrorCode != QNetworkReply::NoError) { - replyDownloadMetaData - (delegate->incomingHeaders, - delegate->incomingStatusCode, - delegate->incomingReasonPhrase, - delegate->isPipeliningUsed, - QSharedPointer<char>(), - delegate->incomingContentLength); - httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail); - } else { - replyDownloadMetaData - (delegate->incomingHeaders, - delegate->incomingStatusCode, - delegate->incomingReasonPhrase, - delegate->isPipeliningUsed, - QSharedPointer<char>(), - delegate->incomingContentLength); - replyDownloadData(delegate->synchronousDownloadData); - } - - // End the thread. It will delete itself from the finished() signal - thread->quit(); - - finished(); - } else { - emit startHttpRequest(); // Signal to the HTTP thread and go back to user. - } -} - -void QNetworkAccessHttpBackend::invalidateCache() -{ - QAbstractNetworkCache *nc = networkCache(); - if (nc) - nc->remove(url()); -} - -void QNetworkAccessHttpBackend::open() -{ - postRequest(); -} - -void QNetworkAccessHttpBackend::closeDownstreamChannel() -{ - // FIXME Maybe we can get rid of this whole architecture part -} - -void QNetworkAccessHttpBackend::downstreamReadyWrite() -{ - // FIXME Maybe we can get rid of this whole architecture part -} - -void QNetworkAccessHttpBackend::setDownstreamLimited(bool b) -{ - Q_UNUSED(b); - // We know that readBuffer maximum size limiting is broken since quite a while. - // The task to fix this is QTBUG-15065 -} - -void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d) -{ - int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1; - - if (pendingSignals > 0) { - // Some more signal emissions to this slot are pending. - // Instead of writing the downstream data, we wait - // and do it in the next call we get - // (signal comppression) - pendingDownloadData.append(d); - return; - } - - pendingDownloadData.append(d); - d.clear(); - // We need to usa a copy for calling writeDownstreamData as we could - // possibly recurse into this this function when we call - // appendDownstreamDataSignalEmissions because the user might call - // processEvents() or spin an event loop when this occur. - QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData; - pendingDownloadData.clear(); - writeDownstreamData(pendingDownloadDataCopy); -} - -void QNetworkAccessHttpBackend::replyFinished() -{ - // We are already loading from cache, we still however - // got this signal because it was posted already - if (loadingFromCache) - return; - - finished(); -} - -void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode) -{ - switch (statusCode) { - case 301: // Moved Permanently - case 302: // Found - case 303: // See Other - case 307: // Temporary Redirect - // What do we do about the caching of the HTML note? - // The response to a 303 MUST NOT be cached, while the response to - // all of the others is cacheable if the headers indicate it to be - QByteArray header = rawHeader("location"); - QUrl url = QUrl::fromEncoded(header); - if (!url.isValid()) - url = QUrl(QLatin1String(header)); - redirectionRequested(url); - } -} - -void QNetworkAccessHttpBackend::replyDownloadMetaData - (QList<QPair<QByteArray,QByteArray> > hm, - int sc,QString rp,bool pu, - QSharedPointer<char> db, - qint64 contentLength) -{ - statusCode = sc; - reasonPhrase = rp; - - // Download buffer - if (!db.isNull()) { - reply->setDownloadBuffer(db, contentLength); - usingZerocopyDownloadBuffer = true; - } else { - usingZerocopyDownloadBuffer = false; - } - - setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu); - - // reconstruct the HTTP header - QList<QPair<QByteArray, QByteArray> > headerMap = hm; - QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(), - end = headerMap.constEnd(); - QByteArray header; - - for (; it != end; ++it) { - QByteArray value = rawHeader(it->first); - if (!value.isEmpty()) { - if (qstricmp(it->first.constData(), "set-cookie") == 0) - value += '\n'; - else - value += ", "; - } - value += it->second; - setRawHeader(it->first, value); - } - - setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode); - setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase); - - // is it a redirection? - checkForRedirect(statusCode); - - if (statusCode >= 500 && statusCode < 600) { - QAbstractNetworkCache *nc = networkCache(); - if (nc) { - QNetworkCacheMetaData metaData = nc->metaData(url()); - QNetworkHeadersPrivate cacheHeaders; - cacheHeaders.setAllRawHeaders(metaData.rawHeaders()); - QNetworkHeadersPrivate::RawHeadersList::ConstIterator it; - it = cacheHeaders.findRawHeader("Cache-Control"); - bool mustReValidate = false; - if (it != cacheHeaders.rawHeaders.constEnd()) { - QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second); - if (cacheControl.contains("must-revalidate")) - mustReValidate = true; - } - if (!mustReValidate && sendCacheContents(metaData)) - return; - } - } - - if (statusCode == 304) { -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "Received a 304 from" << url(); -#endif - QAbstractNetworkCache *nc = networkCache(); - if (nc) { - QNetworkCacheMetaData oldMetaData = nc->metaData(url()); - QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData); - if (oldMetaData != metaData) - nc->updateMetaData(metaData); - if (sendCacheContents(metaData)) - return; - } - } - - - if (statusCode != 304 && statusCode != 303) { - if (!isCachingEnabled()) - setCachingEnabled(true); - } - - metaDataChanged(); -} - -void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received, qint64 total) -{ - // we can be sure here that there is a download buffer - - int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1; - if (pendingSignals > 0) { - // Let's ignore this signal and look at the next one coming in - // (signal comppression) - return; - } - - // Now do the actual notification of new bytes - writeDownstreamDataDownloadBuffer(received, total); -} - -void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &, - QAuthenticator *auth) -{ - authenticationRequired(auth); -} - -void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode, - const QString &errorString) -{ -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "http error!" << errorCode << errorString; -#endif - - error(errorCode, errorString); -} - -#ifndef QT_NO_OPENSSL -void QNetworkAccessHttpBackend::replySslErrors( - const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored) -{ - // Go to generic backend - sslErrors(list); - // Check if the callback set any ignore and return this here to http thread - if (pendingIgnoreAllSslErrors) - *ignoreAll = true; - if (!pendingIgnoreSslErrorsList.isEmpty()) - *toBeIgnored = pendingIgnoreSslErrorsList; -} - -void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfiguration &c) -{ - // Receiving the used SSL configuration from the HTTP thread - if (pendingSslConfiguration) - *pendingSslConfiguration = c; - else if (!c.isNull()) - pendingSslConfiguration = new QSslConfiguration(c); -} -#endif - -// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread -void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r) -{ - *r = uploadByteDevice->reset(); -} - -// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread -void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount) -{ - uploadByteDevice->advanceReadPointer(amount); -} - -// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread -void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize) -{ - // call readPointer - qint64 currentUploadDataLength = 0; - char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength)); - // Let's make a copy of this data - QByteArray dataArray(data, currentUploadDataLength); - - // Communicate back to HTTP thread - emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); -} - -/* - A simple web page that can be used to test us: http://www.procata.com/cachetest/ - */ -bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &metaData) -{ - setCachingEnabled(false); - if (!metaData.isValid()) - return false; - - QAbstractNetworkCache *nc = networkCache(); - Q_ASSERT(nc); - QIODevice *contents = nc->data(url()); - if (!contents) { -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "Can not send cache, the contents are 0" << url(); -#endif - return false; - } - contents->setParent(this); - - QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes(); - int status = attributes.value(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (status < 100) - status = 200; // fake it - - setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status); - setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute)); - setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true); - - QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders(); - QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(), - end = rawHeaders.constEnd(); - for ( ; it != end; ++it) - setRawHeader(it->first, it->second); - - checkForRedirect(status); - - // This needs to be emitted in the event loop because it can be reached at - // the direct code path of qnam.get(...) before the user has a chance - // to connect any signals. - QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection); - qRegisterMetaType<QIODevice*>("QIODevice*"); - QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents)); - - -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes"; -#endif - - // Set the following flag so we can ignore some signals from HTTP thread - // that would still come - loadingFromCache = true; - return true; -} - -void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev) -{ - delete dev; - finished(); -} - -#ifndef QT_NO_OPENSSL -void QNetworkAccessHttpBackend::ignoreSslErrors() -{ - pendingIgnoreAllSslErrors = true; -} - -void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors) -{ - // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) - // is called before QNetworkAccessManager::get() (or post(), etc.) - pendingIgnoreSslErrorsList = errors; -} - -void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const -{ - if (pendingSslConfiguration) - config = *pendingSslConfiguration; - else - config = request().sslConfiguration(); -} - -void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig) -{ - // Setting a SSL configuration on a reply is not supported. The user needs to set - // her/his QSslConfiguration on the QNetworkRequest. - Q_UNUSED(newconfig); -} -#endif - -QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const -{ - QNetworkCacheMetaData metaData = oldMetaData; - - QNetworkHeadersPrivate cacheHeaders; - cacheHeaders.setAllRawHeaders(metaData.rawHeaders()); - QNetworkHeadersPrivate::RawHeadersList::ConstIterator it; - - QList<QByteArray> newHeaders = rawHeaderList(); - foreach (QByteArray header, newHeaders) { - QByteArray originalHeader = header; - header = header.toLower(); - bool hop_by_hop = - (header == "connection" - || header == "keep-alive" - || header == "proxy-authenticate" - || header == "proxy-authorization" - || header == "te" - || header == "trailers" - || header == "transfer-encoding" - || header == "upgrade"); - if (hop_by_hop) - continue; - - // we are currently not using the date header to determine the expiration time of a page, - // but only the "Expires", "max-age" and "s-maxage" headers, see - // QNetworkAccessHttpBackend::validateCache() and below ("metaData.setExpirationDate()"). - if (header == "date") - continue; - - // Don't store Warning 1xx headers - if (header == "warning") { - QByteArray v = rawHeader(header); - if (v.length() == 3 - && v[0] == '1' - && v[1] >= '0' && v[1] <= '9' - && v[2] >= '0' && v[2] <= '9') - continue; - } - - it = cacheHeaders.findRawHeader(header); - if (it != cacheHeaders.rawHeaders.constEnd()) { - // Match the behavior of Firefox and assume Cache-Control: "no-transform" - if (header == "content-encoding" - || header == "content-range" - || header == "content-type") - continue; - - // For MS servers that send "Content-Length: 0" on 304 responses - // ignore this too - if (header == "content-length") - continue; - } - -#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - QByteArray n = rawHeader(header); - QByteArray o; - if (it != cacheHeaders.rawHeaders.constEnd()) - o = (*it).second; - if (n != o && header != "date") { - qDebug() << "replacing" << header; - qDebug() << "new" << n; - qDebug() << "old" << o; - } -#endif - cacheHeaders.setRawHeader(originalHeader, rawHeader(header)); - } - metaData.setRawHeaders(cacheHeaders.rawHeaders); - - bool checkExpired = true; - - QHash<QByteArray, QByteArray> cacheControl; - it = cacheHeaders.findRawHeader("Cache-Control"); - if (it != cacheHeaders.rawHeaders.constEnd()) { - cacheControl = parseHttpOptionHeader(it->second); - QByteArray maxAge = cacheControl.value("max-age"); - if (!maxAge.isEmpty()) { - checkExpired = false; - QDateTime dt = QDateTime::currentDateTime(); - dt = dt.addSecs(maxAge.toInt()); - metaData.setExpirationDate(dt); - } - } - if (checkExpired) { - it = cacheHeaders.findRawHeader("expires"); - if (it != cacheHeaders.rawHeaders.constEnd()) { - QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second); - metaData.setExpirationDate(expiredDateTime); - } - } - - it = cacheHeaders.findRawHeader("last-modified"); - if (it != cacheHeaders.rawHeaders.constEnd()) - metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second)); - - bool canDiskCache; - // only cache GET replies by default, all other replies (POST, PUT, DELETE) - // are not cacheable by default (according to RFC 2616 section 9) - if (httpRequest.operation() == QHttpNetworkRequest::Get) { - - canDiskCache = true; - // 14.32 - // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client - // had sent "Cache-Control: no-cache". - it = cacheHeaders.findRawHeader("pragma"); - if (it != cacheHeaders.rawHeaders.constEnd() - && it->second == "no-cache") - canDiskCache = false; - - // HTTP/1.1. Check the Cache-Control header - if (cacheControl.contains("no-cache")) - canDiskCache = false; - else if (cacheControl.contains("no-store")) - canDiskCache = false; - - // responses to POST might be cacheable - } else if (httpRequest.operation() == QHttpNetworkRequest::Post) { - - canDiskCache = false; - // some pages contain "expires:" and "cache-control: no-cache" field, - // so we only might cache POST requests if we get "cache-control: max-age ..." - if (cacheControl.contains("max-age")) - canDiskCache = true; - - // responses to PUT and DELETE are not cacheable - } else { - canDiskCache = false; - } - - metaData.setSaveToDisk(canDiskCache); - QNetworkCacheMetaData::AttributesMap attributes; - if (statusCode != 304) { - // update the status code - attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode); - attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase); - } else { - // this is a redirection, keep the attributes intact - attributes = oldMetaData.attributes(); - } - metaData.setAttributes(attributes); - return metaData; -} - -bool QNetworkAccessHttpBackend::canResume() const -{ - // Only GET operation supports resuming. - if (operation() != QNetworkAccessManager::GetOperation) - return false; - - // Can only resume if server/resource supports Range header. - QByteArray acceptRangesheaderName("Accept-Ranges"); - if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none") - return false; - - // We only support resuming for byte ranges. - if (request().hasRawHeader("Range")) { - QByteArray range = request().rawHeader("Range"); - if (!range.startsWith("bytes=")) - return false; - } - - // If we're using a download buffer then we don't support resuming/migration - // right now. Too much trouble. - if (usingZerocopyDownloadBuffer) - return false; - - return true; -} - -void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset) -{ - resumeOffset = offset; -} - -QT_END_NAMESPACE - -#endif // QT_NO_HTTP diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h deleted file mode 100644 index 7d4ea56260..0000000000 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QNETWORKACCESSHTTPBACKEND_P_H -#define QNETWORKACCESSHTTPBACKEND_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 "qhttpnetworkconnection_p.h" -#include "qnetworkaccessbackend_p.h" -#include "qnetworkrequest.h" -#include "qnetworkreply.h" -#include "qabstractsocket.h" - -#include "QtCore/qpointer.h" -#include "QtCore/qdatetime.h" -#include "QtCore/qsharedpointer.h" -#include "qatomic.h" - -#ifndef QT_NO_HTTP - -QT_BEGIN_NAMESPACE - -class QNetworkAccessCachedHttpConnection; - -class QNetworkAccessHttpBackendIODevice; - -class QNetworkAccessHttpBackend: public QNetworkAccessBackend -{ - Q_OBJECT -public: - QNetworkAccessHttpBackend(); - virtual ~QNetworkAccessHttpBackend(); - - virtual void open(); - virtual void closeDownstreamChannel(); - - virtual void downstreamReadyWrite(); - virtual void setDownstreamLimited(bool b); - - virtual void copyFinished(QIODevice *); -#ifndef QT_NO_OPENSSL - virtual void ignoreSslErrors(); - virtual void ignoreSslErrors(const QList<QSslError> &errors); - - virtual void fetchSslConfiguration(QSslConfiguration &configuration) const; - virtual void setSslConfiguration(const QSslConfiguration &configuration); -#endif - QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const; - - // we return true since HTTP needs to send PUT/POST data again after having authenticated - bool needsResetableUploadData() { return true; } - - bool canResume() const; - void setResumeOffset(quint64 offset); - -signals: - // To HTTP thread: - void startHttpRequest(); - void abortHttpRequest(); - - void startHttpRequestSynchronously(); - - void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); -private slots: - // From HTTP thread: - void replyDownloadData(QByteArray); - void replyFinished(); - void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64); - void replyDownloadProgressSlot(qint64,qint64); - void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); - void httpError(QNetworkReply::NetworkError error, const QString &errorString); -#ifndef QT_NO_OPENSSL - void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *); - void replySslConfigurationChanged(const QSslConfiguration&); -#endif - - // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: - void resetUploadDataSlot(bool *r); - void wantUploadDataSlot(qint64); - void sentUploadDataSlot(qint64); - - bool sendCacheContents(const QNetworkCacheMetaData &metaData); - -private: - QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread - int statusCode; - QString reasonPhrase; - // Will be increased by HTTP thread: - QSharedPointer<QAtomicInt> pendingDownloadDataEmissions; - QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions; - bool loadingFromCache; - QByteDataBuffer pendingDownloadData; - bool usingZerocopyDownloadBuffer; - -#ifndef QT_NO_OPENSSL - QSslConfiguration *pendingSslConfiguration; - bool pendingIgnoreAllSslErrors; - QList<QSslError> pendingIgnoreSslErrorsList; -#endif - - quint64 resumeOffset; - - bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest); - void invalidateCache(); - void postRequest(); - void readFromHttp(); - void checkForRedirect(const int statusCode); -}; - -class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory -{ -public: - virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op, - const QNetworkRequest &request) const; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_HTTP - -#endif diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 0b51f7255b..eba5880352 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -50,7 +50,6 @@ #include "QtNetwork/qnetworksession.h" #include "QtNetwork/private/qsharednetworksession_p.h" -#include "qnetworkaccesshttpbackend_p.h" #include "qnetworkaccessftpbackend_p.h" #include "qnetworkaccessfilebackend_p.h" #include "qnetworkaccessdebugpipebackend_p.h" diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index ea295dbe97..8ce6080d3a 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -159,7 +159,6 @@ protected: private: friend class QNetworkReplyImplPrivate; - friend class QNetworkAccessHttpBackend; friend class QNetworkReplyHttpImpl; Q_DECLARE_PRIVATE(QNetworkAccessManager) diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 7f61ef9239..21b1d84a78 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -105,6 +105,10 @@ QT_BEGIN_NAMESPACE header and contains a QList<QNetworkCookie> representing the cookies sent by the server to be stored locally. + \value UserAgentHeader The User-Agent header sent by HTTP clients. + + \value ServerHeader The Server header received by HTTP clients. + \sa header(), setHeader(), rawHeader(), setRawHeader() */ @@ -650,6 +654,12 @@ static QByteArray headerName(QNetworkRequest::KnownHeaders header) case QNetworkRequest::ContentDispositionHeader: return "Content-Disposition"; + case QNetworkRequest::UserAgentHeader: + return "User-Agent"; + + case QNetworkRequest::ServerHeader: + return "Server"; + // no default: // if new values are added, this will generate a compiler warning } @@ -663,6 +673,8 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria case QNetworkRequest::ContentTypeHeader: case QNetworkRequest::ContentLengthHeader: case QNetworkRequest::ContentDispositionHeader: + case QNetworkRequest::UserAgentHeader: + case QNetworkRequest::ServerHeader: return value.toByteArray(); case QNetworkRequest::LocationHeader: @@ -745,6 +757,13 @@ static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerNam case 's': if (qstricmp(headerName.constData(), "set-cookie") == 0) return QNetworkRequest::SetCookieHeader; + else if (qstricmp(headerName.constData(), "server") == 0) + return QNetworkRequest::ServerHeader; + break; + + case 'u': + if (qstricmp(headerName.constData(), "user-agent") == 0) + return QNetworkRequest::UserAgentHeader; break; } @@ -778,6 +797,8 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy { // header is always a valid value switch (header) { + case QNetworkRequest::UserAgentHeader: + case QNetworkRequest::ServerHeader: case QNetworkRequest::ContentTypeHeader: // copy exactly, convert to QString return QString::fromLatin1(value); diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index eedb0f6592..aa86007127 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -66,7 +66,9 @@ public: LastModifiedHeader, CookieHeader, SetCookieHeader, - ContentDispositionHeader // added for QMultipartMessage + ContentDispositionHeader, // added for QMultipartMessage + UserAgentHeader, + ServerHeader }; enum Attribute { HttpStatusCodeAttribute, diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index df7766e2c5..d42c2598f8 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -527,6 +527,7 @@ void QHostInfoRunnable::run() iterator.remove(); hostInfo.setLookupId(postponed->id); postponed->resultEmitter.emitResultsReady(hostInfo); + delete postponed; } } } diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 7403590f8c..839a253b99 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -128,7 +128,7 @@ QT_BEGIN_NAMESPACE // forward declaration -static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name); +static QMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name); /*! Constructs a QSslCertificate by reading \a format encoded data @@ -297,19 +297,19 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons return QCryptographicHash::hash(toDer(), algorithm); } -static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info) +static QByteArray _q_SubjectInfoToString(QSslCertificate::SubjectInfo info) { - QString str; + QByteArray str; switch (info) { - case QSslCertificate::Organization: str = QLatin1String("O"); break; - case QSslCertificate::CommonName: str = QLatin1String("CN"); break; - case QSslCertificate::LocalityName: str = QLatin1String("L"); break; - case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break; - case QSslCertificate::CountryName: str = QLatin1String("C"); break; - case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break; - case QSslCertificate::DistinguishedNameQualifier: str = QLatin1String("dnQualifier"); break; - case QSslCertificate::SerialNumber: str = QLatin1String("serialNumber"); break; - case QSslCertificate::EmailAddress: str = QLatin1String("emailAddress"); break; + case QSslCertificate::Organization: str = QByteArray("O"); break; + case QSslCertificate::CommonName: str = QByteArray("CN"); break; + case QSslCertificate::LocalityName: str = QByteArray("L"); break; + case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break; + case QSslCertificate::CountryName: str = QByteArray("C"); break; + case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break; + case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break; + case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break; + case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break; } return str; } @@ -334,20 +334,20 @@ QStringList QSslCertificate::issuerInfo(SubjectInfo info) const } /*! - Returns the issuer information for \a tag from the certificate, - or an empty string if there is no information for \a tag in the + Returns the issuer information for \a attribute from the certificate, + or an empty string if there is no information for \a attribute in the certificate. \sa subjectInfo() */ -QStringList QSslCertificate::issuerInfo(const QByteArray &tag) const +QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const { // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); - return d->issuerInfo.values(QString::fromLatin1(tag)); + return d->issuerInfo.values(attribute); } /*! @@ -370,19 +370,57 @@ QStringList QSslCertificate::subjectInfo(SubjectInfo info) const } /*! - Returns the subject information for \a tag, or an empty string if - there is no information for \a tag in the certificate. + Returns the subject information for \a attribute, or an empty string if + there is no information for \a attribute in the certificate. \sa issuerInfo() */ -QStringList QSslCertificate::subjectInfo(const QByteArray &tag) const +QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const { // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); - return d->subjectInfo.values(QString::fromLatin1(tag)); + return d->subjectInfo.values(attribute); +} + +/*! + Returns a list of the attributes that have values in the subject + information of this certificate. The information associated + with a given attribute can be accessed using the subjectInfo() + method. Note that this list may include the OIDs for any + elements that are not known by the SSL backend. + + \sa subjectInfo() +*/ +QList<QByteArray> QSslCertificate::subjectInfoAttributes() const +{ + // lazy init + if (d->subjectInfo.isEmpty() && d->x509) + d->subjectInfo = + _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); + + return d->subjectInfo.uniqueKeys(); +} + +/*! + Returns a list of the attributes that have values in the issuer + information of this certificate. The information associated + with a given attribute can be accessed using the issuerInfo() + method. Note that this list may include the OIDs for any + elements that are not known by the SSL backend. + + \sa subjectInfo() +*/ +QList<QByteArray> QSslCertificate::issuerInfoAttributes() const +{ + // lazy init + if (d->issuerInfo.isEmpty() && d->x509) + d->issuerInfo = + _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); + + return d->issuerInfo.uniqueKeys(); } /*! @@ -706,17 +744,32 @@ QByteArray QSslCertificatePrivate::text_from_X509(X509 *x509) return result; } -static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name) +QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object) { - QMap<QString, QString> info; + int nid = q_OBJ_obj2nid(object); + if (nid != NID_undef) + return QByteArray(q_OBJ_nid2sn(nid)); + + // This is used for unknown info so we get the OID as text + char buf[80]; + q_i2t_ASN1_OBJECT(buf, sizeof(buf), object); + + return QByteArray(buf); +} + +static QMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name) +{ + QMap<QByteArray, QString> info; for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) { X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i); - const char *obj = q_OBJ_nid2sn(q_OBJ_obj2nid(q_X509_NAME_ENTRY_get_object(e))); + + QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e)); unsigned char *data = 0; int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e)); - info.insertMulti(QString::fromUtf8(obj), QString::fromUtf8((char*)data, size)); + info.insertMulti(name, QString::fromUtf8((char*)data, size)); q_CRYPTO_free(data); } + return info; } diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index 8abaa3f73e..4de84dd4ba 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -100,9 +100,11 @@ public: QByteArray serialNumber() const; QByteArray digest(QCryptographicHash::Algorithm algorithm = QCryptographicHash::Md5) const; QStringList issuerInfo(SubjectInfo info) const; - QStringList issuerInfo(const QByteArray &tag) const; + QStringList issuerInfo(const QByteArray &attribute) const; QStringList subjectInfo(SubjectInfo info) const; - QStringList subjectInfo(const QByteArray &tag) const; + QStringList subjectInfo(const QByteArray &attribute) const; + QList<QByteArray> subjectInfoAttributes() const; + QList<QByteArray> issuerInfoAttributes() const; QMultiMap<QSsl::AlternateNameEntryType, QString> alternateSubjectNames() const; QDateTime effectiveDate() const; QDateTime expiryDate() const; diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index eb192968c5..3cfcef7b57 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -83,8 +83,8 @@ public: QByteArray versionString; QByteArray serialNumberString; - QMap<QString, QString> issuerInfo; - QMap<QString, QString> subjectInfo; + QMap<QByteArray, QString> issuerInfo; + QMap<QByteArray, QString> subjectInfo; QDateTime notValidAfter; QDateTime notValidBefore; @@ -92,6 +92,7 @@ public: void init(const QByteArray &data, QSsl::EncodingFormat format); + static QByteArray asn1ObjectName(ASN1_OBJECT *object); static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format); static QByteArray text_from_X509(X509 *x509); static QSslCertificate QSslCertificate_from_X509(X509 *x509); diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index f191ed9324..294e2508d8 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1599,6 +1599,27 @@ bool QSslSocket::supportsSsl() } /*! + Returns the version number of the SSL library in use. Note that + this is the version of the library in use at run-time not compile + time. If no SSL support is available then this will return an + undefined value. +*/ +long QSslSocket::sslLibraryVersionNumber() +{ + return QSslSocketPrivate::sslLibraryVersionNumber(); +} + +/*! + Returns the version string of the SSL library in use. Note that + this is the version of the library in use at run-time not compile + time. If no SSL support is available then this will return an empty value. +*/ +QString QSslSocket::sslLibraryVersionString() +{ + return QSslSocketPrivate::sslLibraryVersionString(); +} + +/*! Starts a delayed SSL handshake for a client connection. This function can be called when the socket is in the \l ConnectedState but still in the \l UnencryptedMode. If it is not yet connected, diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index f175ffd946..803e79e0c4 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -176,6 +176,9 @@ public: QList<QSslError> sslErrors() const; static bool supportsSsl(); + static long sslLibraryVersionNumber(); + static QString sslLibraryVersionString(); + void ignoreSslErrors(const QList<QSslError> &errors); public Q_SLOTS: diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 479a6bd60e..c7e938a705 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -602,6 +602,23 @@ void QSslSocketPrivate::ensureInitialized() ensureCiphersAndCertsLoaded(); } +long QSslSocketPrivate::sslLibraryVersionNumber() +{ + return q_SSLeay(); +} + +QString QSslSocketPrivate::sslLibraryVersionString() +{ + if (!supportsSsl()) + return QString(); + + const char *versionString = q_SSLeay_version(SSLEAY_VERSION); + if (!versionString) + return QString(); + + return QString::fromLatin1(versionString); +} + /*! \internal diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index b652833b45..31afab003f 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -133,6 +133,10 @@ DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return) DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return) DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return) +DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return 0, return) +DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return) + + DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) #ifdef SSLEAY_MACROS DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) @@ -276,6 +280,7 @@ DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMM DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return) DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return) +DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return) #ifdef Q_OS_SYMBIAN #define RESOLVEFUNC(func, ordinal, lib) \ @@ -687,6 +692,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_PKEY_new) RESOLVEFUNC(EVP_PKEY_type) RESOLVEFUNC(OBJ_nid2sn) + RESOLVEFUNC(OBJ_nid2ln) + RESOLVEFUNC(i2t_ASN1_OBJECT) RESOLVEFUNC(OBJ_obj2nid) #ifdef SSLEAY_MACROS // ### verify RESOLVEFUNC(PEM_ASN1_read_bio) @@ -788,6 +795,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) RESOLVEFUNC(SSL_CTX_load_verify_locations) RESOLVEFUNC(SSLeay) + RESOLVEFUNC(SSLeay_version) #endif // Q_OS_SYMBIAN symbolsResolved = true; delete libs.first; diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 658aa144a7..cd3aa07bc0 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -237,6 +237,8 @@ int q_EVP_PKEY_type(int a); EVP_PKEY *q_EVP_PKEY_new(); int q_i2d_X509(X509 *a, unsigned char **b); const char *q_OBJ_nid2sn(int a); +const char *q_OBJ_nid2ln(int a); +int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj); int q_OBJ_obj2nid(const ASN1_OBJECT *a); #ifdef SSLEAY_MACROS // ### verify @@ -426,6 +428,7 @@ void q_OPENSSL_add_all_algorithms_noconf(); void q_OPENSSL_add_all_algorithms_conf(); int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); long q_SSLeay(); +const char *q_SSLeay_version(int type); // Helper function class QDateTime; diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 86ecba07ce..b1dc656e88 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -116,6 +116,8 @@ public: bool allowRootCertOnDemandLoading; static bool supportsSsl(); + static long sslLibraryVersionNumber(); + static QString sslLibraryVersionString(); static void ensureInitialized(); static void deinitialize(); static QList<QSslCipher> defaultCiphers(); |