summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp4
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp13
-rw-r--r--src/network/access/qhttpnetworkreply.cpp5
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp8
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp17
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h2
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp6
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp11
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp6
-rw-r--r--src/network/access/qnetworkaccessftpbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp171
-rw-r--r--src/network/access/qnetworkaccessmanager.h16
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h16
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp4
-rw-r--r--src/network/ssl/qssl.cpp10
-rw-r--r--src/network/ssl/qssl.h4
-rw-r--r--src/network/ssl/qsslconfiguration.cpp65
-rw-r--r--src/network/ssl/qsslconfiguration.h4
-rw-r--r--src/network/ssl/qsslconfiguration_p.h6
-rw-r--r--src/network/ssl/qsslcontext.cpp41
-rw-r--r--src/network/ssl/qsslcontext_p.h5
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp12
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp4
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h2
27 files changed, 408 insertions, 29 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index a279990f4c..5c3c38606d 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -929,7 +929,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
for (int i = 0; i < channelCount; ++i) {
bool connectChannel = false;
if (channels[i].socket) {
- if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) || (channels[i].socket->state() == QAbstractSocket::HostLookupState))
+ if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
+ || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
+ || channels[i].pendingEncrypt) // pendingEncrypt == "EncryptingState"
queuedRequest--;
if ( queuedRequest <=0 )
break;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 4b8fe8aca7..a756f22937 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -183,6 +183,9 @@ void QHttpNetworkConnectionChannel::close()
else
state = QHttpNetworkConnectionChannel::ClosingState;
+ // pendingEncrypt must only be true in between connected and encrypted states
+ pendingEncrypt = false;
+
if (socket)
socket->close();
}
@@ -205,6 +208,16 @@ bool QHttpNetworkConnectionChannel::sendRequest()
// _q_connected or _q_encrypted
return false;
}
+ QString scheme = request.url().scheme();
+ if (scheme == QLatin1String("preconnect-http")
+ || scheme == QLatin1String("preconnect-https")) {
+ state = QHttpNetworkConnectionChannel::IdleState;
+ reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ allDone();
+ reply = 0; // so we can reuse this channel
+ return true; // we have a working connection and are done
+ }
+
written = 0; // excluding the header
bytesTotal = 0;
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index eb8a8869cc..1b9e1f5a53 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -290,6 +290,11 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
#endif
{
+ QString scheme = newUrl.scheme();
+ if (scheme == QLatin1String("preconnect-http")
+ || scheme == QLatin1String("preconnect-https"))
+ // make sure we do not close the socket after preconnecting
+ connectionCloseEnabled = false;
}
QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index a2cee48b22..ee3911c72c 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -107,8 +107,14 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
{
QString result;
QUrl copy = url;
- bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
+ QString scheme = copy.scheme().toLower();
+ bool isEncrypted = scheme == QLatin1String("https");
copy.setPort(copy.port(isEncrypted ? 443 : 80));
+ if (scheme == QLatin1String("preconnect-http")) {
+ copy.setScheme(QLatin1String("http"));
+ } else if (scheme == QLatin1String("preconnect-https")) {
+ copy.setScheme(QLatin1String("https"));
+ }
result = copy.toString(QUrl::RemoveUserInfo | QUrl::RemovePath |
QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::FullyEncoded);
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index a895864d76..47cd928541 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -47,6 +47,7 @@
#include "qnetworkreply_p.h"
#include "QtCore/qhash.h"
#include "QtCore/qmutex.h"
+#include "QtCore/qstringlist.h"
#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkaccesscachebackend_p.h"
@@ -110,6 +111,22 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
return 0;
}
+QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const
+{
+ if (QNetworkAccessBackendFactoryData::valid.load()) {
+ QMutexLocker locker(&factoryData()->mutex);
+ QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin();
+ QNetworkAccessBackendFactoryData::ConstIterator end = factoryData()->constEnd();
+ QStringList schemes;
+ while (it != end) {
+ schemes += (*it)->supportedSchemes();
+ ++it;
+ }
+ return schemes;
+ }
+ return QStringList();
+}
+
QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
{
if (reply->outgoingDataBuffer)
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index bf284414e0..d9657cf750 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -62,6 +62,7 @@ class QAuthenticator;
class QNetworkProxy;
class QNetworkProxyQuery;
class QNetworkRequest;
+class QStringList;
class QUrl;
class QUrlInfo;
class QSslConfiguration;
@@ -219,6 +220,7 @@ class QNetworkAccessBackendFactory
public:
QNetworkAccessBackendFactory();
virtual ~QNetworkAccessBackendFactory();
+ virtual QStringList supportedSchemes() const = 0;
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const = 0;
};
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index b6c04dddea..a91751523a 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -42,6 +42,7 @@
#include "qnetworkaccessdebugpipebackend_p.h"
#include "QtCore/qdatastream.h"
#include <QCoreApplication>
+#include <QStringList>
#include <QUrlQuery>
#include "private/qnoncontiguousbytedevice_p.h"
@@ -54,6 +55,11 @@ enum {
WriteBufferSize = ReadBufferSize
};
+QStringList QNetworkAccessDebugPipeBackendFactory::supportedSchemes() const
+{
+ return QStringList(QStringLiteral("debugpipe"));
+}
+
QNetworkAccessBackend *
QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
index 0ae49de132..7593dfa9b7 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend_p.h
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -102,6 +102,7 @@ private:
class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory
{
public:
+ virtual QStringList supportedSchemes() const Q_DECL_OVERRIDE;
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const;
};
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 13428cc802..13e7394003 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -49,6 +49,17 @@
QT_BEGIN_NAMESPACE
+QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const
+{
+ QStringList schemes;
+ schemes << QStringLiteral("file")
+ << QStringLiteral("qrc");
+#if defined(Q_OS_ANDROID)
+ schemes << QStringLiteral("assets");
+#endif
+ return schemes;
+}
+
QNetworkAccessBackend *
QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
index a52ecef165..157461fee7 100644
--- a/src/network/access/qnetworkaccessfilebackend_p.h
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -88,6 +88,7 @@ private:
class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
{
public:
+ virtual QStringList supportedSchemes() const Q_DECL_OVERRIDE;
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const;
};
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index 737d7d0151..246eb41657 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -43,6 +43,7 @@
#include "qnetworkaccessmanager_p.h"
#include "QtNetwork/qauthenticator.h"
#include "private/qnoncontiguousbytedevice_p.h"
+#include <QStringList>
#ifndef QT_NO_FTP
@@ -61,6 +62,11 @@ static QByteArray makeCacheKey(const QUrl &url)
QUrl::RemoveFragment);
}
+QStringList QNetworkAccessFtpBackendFactory::supportedSchemes() const
+{
+ return QStringList(QStringLiteral("ftp"));
+}
+
QNetworkAccessBackend *
QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const
diff --git a/src/network/access/qnetworkaccessftpbackend_p.h b/src/network/access/qnetworkaccessftpbackend_p.h
index 1bc377d80e..c006d450b8 100644
--- a/src/network/access/qnetworkaccessftpbackend_p.h
+++ b/src/network/access/qnetworkaccessftpbackend_p.h
@@ -111,6 +111,7 @@ private:
class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory
{
public:
+ virtual QStringList supportedSchemes() const Q_DECL_OVERRIDE;
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
const QNetworkRequest &request) const;
};
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 10d19bb7aa..91655ef485 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -96,9 +96,11 @@ bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString&
SecProtocolType protocolType = kSecProtocolTypeAny;
if (scheme.compare(QLatin1String("ftp"),Qt::CaseInsensitive)==0) {
protocolType = kSecProtocolTypeFTP;
- } else if (scheme.compare(QLatin1String("http"),Qt::CaseInsensitive)==0) {
+ } else if (scheme.compare(QLatin1String("http"),Qt::CaseInsensitive)==0
+ || scheme.compare(QLatin1String("preconnect-http"),Qt::CaseInsensitive)==0) {
protocolType = kSecProtocolTypeHTTP;
- } else if (scheme.compare(QLatin1String("https"),Qt::CaseInsensitive)==0) {
+ } else if (scheme.compare(QLatin1String("https"),Qt::CaseInsensitive)==0
+ || scheme.compare(QLatin1String("preconnect-https"),Qt::CaseInsensitive)==0) {
protocolType = kSecProtocolTypeHTTPS;
}
QByteArray proxyHostnameUtf8(proxyHostname.toUtf8());
@@ -431,6 +433,7 @@ static void ensureInitialized()
QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
: QObject(*new QNetworkAccessManagerPrivate, parent)
{
+ Q_D(QNetworkAccessManager);
ensureInitialized();
qRegisterMetaType<QNetworkReply::NetworkError>();
@@ -447,6 +450,19 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
#endif
qRegisterMetaType<QNetworkReply::NetworkError>();
qRegisterMetaType<QSharedPointer<char> >();
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (!d->networkSessionRequired) {
+ // if a session is required, we track online state through
+ // the QNetworkSession's signals
+ connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
+ SLOT(_q_onlineStateChanged(bool)));
+ // we would need all active configurations to check for
+ // d->networkConfigurationManager.isOnline(), which is asynchronous
+ // and potentially expensive. We can just check the configuration here
+ d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
+ }
+#endif
}
/*!
@@ -833,6 +849,11 @@ QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &requ
To restore the default network configuration set the network configuration to the value
returned from QNetworkConfigurationManager::defaultConfiguration().
+ Setting a network configuration means that the QNetworkAccessManager instance will only
+ be using the specified one. In particular, if the default network configuration changes
+ (upon e.g. Wifi being available), this new configuration needs to be enabled
+ manually if desired.
+
\snippet code/src_network_access_qnetworkaccessmanager.cpp 2
If an invalid network configuration is set, a network session will not be created. In this
@@ -844,7 +865,10 @@ QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &requ
*/
void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config)
{
- d_func()->createSession(config);
+ Q_D(QNetworkAccessManager);
+ d->networkConfiguration = config;
+ d->customNetworkConfiguration = true;
+ d->createSession(config);
}
/*!
@@ -926,19 +950,73 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
{
Q_D(const QNetworkAccessManager);
- QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
- if (networkSession) {
- // d->online holds online/offline state of this network session.
+ if (d->networkSessionRequired) {
+ QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
+ if (networkSession) {
+ // d->online holds online/offline state of this network session.
+ if (d->online)
+ return d->networkAccessible;
+ else
+ return NotAccessible;
+ } else {
+ // Network accessibility is either disabled or unknown.
+ return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
+ }
+ } else {
if (d->online)
return d->networkAccessible;
else
return NotAccessible;
- } else {
- // Network accessibility is either disabled or unknown.
- return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
}
}
+#ifndef QT_NO_SSL
+/*!
+ \since 5.2
+
+ Initiates a connection to the host given by \a hostName at port \a port, using
+ \a sslConfiguration. This function is useful to complete the TCP and SSL handshake
+ to a host before the HTTPS request is made, resulting in a lower network latency.
+
+ \note This function has no possibility to report errors.
+
+ \sa connectToHost(), get(), post(), put(), deleteResource()
+*/
+void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port,
+ const QSslConfiguration &sslConfiguration)
+{
+ QUrl url;
+ url.setHost(hostName);
+ url.setPort(port);
+ url.setScheme(QLatin1String("preconnect-https"));
+ QNetworkRequest request(url);
+ if (sslConfiguration != QSslConfiguration::defaultConfiguration())
+ request.setSslConfiguration(sslConfiguration);
+ get(request);
+}
+#endif
+
+/*!
+ \since 5.2
+
+ Initiates a connection to the host given by \a hostName at port \a port.
+ This function is useful to complete the TCP handshake
+ to a host before the HTTP request is made, resulting in a lower network latency.
+
+ \note This function has no possibility to report errors.
+
+ \sa connectToHostEncrypted(), get(), post(), put(), deleteResource()
+*/
+void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
+{
+ QUrl url;
+ url.setHost(hostName);
+ url.setPort(port);
+ url.setScheme(QLatin1String("preconnect-http"));
+ QNetworkRequest request(url);
+ get(request);
+}
+
/*!
\internal
@@ -1050,10 +1128,10 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
return new QDisabledNetworkReply(this, req, op);
}
- if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.isEmpty())) {
+ if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) {
QNetworkConfigurationManager manager;
- if (!d->networkConfiguration.isEmpty()) {
- d->createSession(manager.configurationFromIdentifier(d->networkConfiguration));
+ if (!d->networkConfiguration.identifier().isEmpty()) {
+ d->createSession(d->networkConfiguration);
} else {
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired)
d->createSession(manager.defaultConfiguration());
@@ -1083,9 +1161,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
#ifndef QT_NO_HTTP
// Since Qt 5 we use the new QNetworkReplyHttpImpl
- if (scheme == QLatin1String("http")
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http")
#ifndef QT_NO_SSL
- || scheme == QLatin1String("https")
+ || scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https")
#endif
) {
QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
@@ -1130,6 +1208,57 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
return reply;
}
+/*!
+ \since 5.2
+
+ Lists all the URL schemes supported by the access manager.
+
+ \sa supportedSchemesImplementation()
+*/
+QStringList QNetworkAccessManager::supportedSchemes() const
+{
+ QStringList schemes;
+ QNetworkAccessManager *self = const_cast<QNetworkAccessManager *>(this); // We know we call a const slot
+ QMetaObject::invokeMethod(self, "supportedSchemesImplementation", Qt::DirectConnection,
+ Q_RETURN_ARG(QStringList, schemes));
+ schemes.removeDuplicates();
+ return schemes;
+}
+
+/*!
+ \since 5.2
+
+ Lists all the URL schemes supported by the access manager.
+
+ You should not call this function directly; use
+ QNetworkAccessManager::supportedSchemes() instead.
+
+ Reimplement this slot to provide your own supported schemes
+ in a QNetworkAccessManager subclass. It is for instance necessary
+ when your subclass provides support for new protocols.
+
+ Because of binary compatibility constraints, the supportedSchemes()
+ method (introduced in Qt 5.2) is not virtual. Instead, supportedSchemes()
+ will dynamically detect and call this slot.
+
+ \sa supportedSchemes()
+*/
+QStringList QNetworkAccessManager::supportedSchemesImplementation() const
+{
+ Q_D(const QNetworkAccessManager);
+
+ QStringList schemes = d->backendSupportedSchemes();
+ // Those ones don't exist in backends
+#ifndef QT_NO_HTTP
+ schemes << QStringLiteral("http");
+#ifndef QT_NO_SSL
+ if (QSslSocket::supportsSsl())
+ schemes << QStringLiteral("https");
+#endif
+#endif
+ schemes << QStringLiteral("data");
+ return schemes;
+}
/*!
\since 5.0
@@ -1403,7 +1532,7 @@ void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
Q_Q(QNetworkAccessManager);
QSharedPointer<QNetworkSession> networkSession(getNetworkSession());
if (networkSession) {
- networkConfiguration = networkSession->configuration().identifier();
+ networkConfiguration = networkSession->configuration();
//disconnect from old session
QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
@@ -1437,6 +1566,18 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
}
}
}
+
+void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline)
+{
+ // if the user set a config, we only care whether this one is active.
+ // Otherwise, this QNAM is online if there is an online config.
+ if (customNetworkConfiguration) {
+ online = (networkConfiguration.state() & QNetworkConfiguration::Active);
+ } else {
+ online = isOnline;
+ }
+}
+
#endif // QT_NO_BEARERMANAGEMENT
QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 826c8e47d7..67b9bbcb07 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -43,6 +43,9 @@
#define QNETWORKACCESSMANAGER_H
#include <QtCore/QObject>
+#ifndef QT_NO_SSL
+#include <QtNetwork/QSslConfiguration>
+#endif
QT_BEGIN_NAMESPACE
@@ -97,6 +100,9 @@ public:
explicit QNetworkAccessManager(QObject *parent = 0);
~QNetworkAccessManager();
+ // ### Qt 6: turn into virtual
+ QStringList supportedSchemes() const;
+
void clearAccessCache();
#ifndef QT_NO_NETWORKPROXY
@@ -132,6 +138,12 @@ public:
NetworkAccessibility networkAccessible() const;
#endif
+#ifndef QT_NO_SSL
+ void connectToHostEncrypted(const QString &hostName, quint16 port = 443,
+ const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration());
+#endif
+ void connectToHost(const QString &hostName, quint16 port = 80);
+
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
@@ -153,6 +165,9 @@ protected:
virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
QIODevice *outgoingData = 0);
+protected Q_SLOTS:
+ QStringList supportedSchemesImplementation() const;
+
private:
friend class QNetworkReplyImplPrivate;
friend class QNetworkReplyHttpImpl;
@@ -165,6 +180,7 @@ private:
#ifndef QT_NO_BEARERMANAGEMENT
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed())
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))
+ Q_PRIVATE_SLOT(d_func(), void _q_onlineStateChanged(bool))
#endif
};
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index cf756dad7b..292755e7eb 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -60,6 +60,9 @@
#include "QtNetwork/qnetworkproxy.h"
#include "QtNetwork/qnetworksession.h"
#include "qnetworkaccessauthenticationmanager_p.h"
+#ifndef QT_NO_BEARERMANAGEMENT
+#include "QtNetwork/qnetworkconfigmanager.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -79,6 +82,10 @@ public:
#endif
#ifndef QT_NO_BEARERMANAGEMENT
lastSessionState(QNetworkSession::Invalid),
+ networkConfiguration(networkConfigurationManager.defaultConfiguration()),
+ customNetworkConfiguration(false),
+ networkSessionRequired(networkConfigurationManager.capabilities()
+ & QNetworkConfigurationManager::NetworkSessionRequired),
networkAccessible(QNetworkAccessManager::Accessible),
activeReplyCount(0),
online(false),
@@ -117,6 +124,7 @@ public:
#endif
QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request);
+ QStringList backendSupportedSchemes() const;
#ifndef QT_NO_BEARERMANAGEMENT
void createSession(const QNetworkConfiguration &config);
@@ -127,6 +135,7 @@ public:
void _q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &config,
bool isSeamless);
void _q_networkSessionStateChanged(QNetworkSession::State state);
+ void _q_onlineStateChanged(bool isOnline);
#endif
QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
@@ -148,7 +157,12 @@ public:
QSharedPointer<QNetworkSession> networkSessionStrongRef;
QWeakPointer<QNetworkSession> networkSessionWeakRef;
QNetworkSession::State lastSessionState;
- QString networkConfiguration;
+ QNetworkConfigurationManager networkConfigurationManager;
+ QNetworkConfiguration networkConfiguration;
+ // we need to track whether the user set a config or not,
+ // because the default config might change
+ bool customNetworkConfiguration;
+ bool networkSessionRequired;
QNetworkAccessManager::NetworkAccessibility networkAccessible;
int activeReplyCount;
bool online;
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index c7d3846465..8205d81191 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -629,7 +629,9 @@ void QNetworkReplyHttpImplPrivate::postRequest()
QUrl url = request.url();
httpRequest.setUrl(url);
- bool ssl = url.scheme().toLower() == QLatin1String("https");
+ QString scheme = url.scheme().toLower();
+ bool ssl = (scheme == QLatin1String("https")
+ || scheme == QLatin1String("preconnect-https"));
q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
httpRequest.setSsl(ssl);
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 4e33001d8d..ec771e1f49 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -161,12 +161,20 @@ QT_BEGIN_NAMESPACE
mechanism for renegotiating the connection parameters. When enabled, this
option can allow connections for legacy servers, but it introduces the
possibility that an attacker could inject plaintext into the SSL session.
+ \value SslOptionDisableSessionSharing Disables SSL session sharing via
+ the session ID handshake attribute.
+ \value SslOptionDisableSessionPersistence Disables storing the SSL session
+ in ASN.1 format as returned by QSslConfiguration::session(). Enabling
+ this feature adds memory overhead of approximately 1K per used session
+ ticket.
By default, SslOptionDisableEmptyFragments is turned on since this causes
problems with a large number of servers. SslOptionDisableLegacyRenegotiation
is also turned on, since it introduces a security risk.
SslOptionDisableCompression is turned on to prevent the attack publicised by
- CRIME. The other options are turned off.
+ CRIME.
+ SslOptionDisableSessionPersistence is turned on to optimize memory usage.
+ The other options are turned off.
Note: Availability of above options depends on the version of the SSL
backend in use.
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 06d80965e2..21d03cb703 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -95,7 +95,9 @@ namespace QSsl {
SslOptionDisableSessionTickets = 0x02,
SslOptionDisableCompression = 0x04,
SslOptionDisableServerNameIndication = 0x08,
- SslOptionDisableLegacyRenegotiation = 0x10
+ SslOptionDisableLegacyRenegotiation = 0x10,
+ SslOptionDisableSessionSharing = 0x20,
+ SslOptionDisableSessionPersistence = 0x40
};
Q_DECLARE_FLAGS(SslOptions, SslOption)
}
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index afbd4fac77..6cc06dfbd2 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -49,7 +49,8 @@ QT_BEGIN_NAMESPACE
const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
- |QSsl::SslOptionDisableCompression;
+ |QSsl::SslOptionDisableCompression
+ |QSsl::SslOptionDisableSessionPersistence;
/*!
\class QSslConfiguration
@@ -182,7 +183,9 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth &&
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
- d->sslOptions == other.d->sslOptions;
+ d->sslOptions == other.d->sslOptions &&
+ d->sslSession == other.d->sslSession &&
+ d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint;
}
/*!
@@ -216,7 +219,9 @@ bool QSslConfiguration::isNull() const
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
d->peerCertificateChain.count() == 0 &&
- d->sslOptions == QSslConfigurationPrivate::defaultSslOptions);
+ d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
+ d->sslSession.isNull() &&
+ d->sslSessionTicketLifeTimeHint == -1);
}
/*!
@@ -594,6 +599,60 @@ bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
}
/*!
+ \since 5.2
+
+ If QSsl::SslOptionDisableSessionPersistence was turned off, this
+ function returns the session used in the SSL handshake in ASN.1
+ format, suitable to e.g. be persisted to disk. If no session was
+ used or QSsl::SslOptionDisableSessionPersistence was not turned off,
+ this function returns an empty QByteArray.
+
+ \b{Note:} When persisting the session to disk or similar, be
+ careful not to expose the session to a potential attacker, as
+ knowledge of the session allows for eavesdropping on data
+ encrypted with the session parameters.
+
+ \sa setSession(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+QByteArray QSslConfiguration::session() const
+{
+ return d->sslSession;
+}
+
+/*!
+ \since 5.2
+
+ Sets the session to be used in an SSL handshake.
+ QSsl::SslOptionDisableSessionPersistence must be turned off
+ for this to work, and \a session must be in ASN.1 format
+ as returned by session().
+
+ \sa session(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+void QSslConfiguration::setSession(const QByteArray &session)
+{
+ d->sslSession = session;
+}
+
+/*!
+ \since 5.2
+
+ If QSsl::SslOptionDisableSessionPersistence was turned off, this
+ function returns the session ticket life time hint sent by the
+ server (which might be 0).
+ If the server did not send a session ticket (e.g. when
+ resuming a session or when the server does not support it) or
+ QSsl::SslOptionDisableSessionPersistence was not turned off,
+ this function returns -1.
+
+ \sa session(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+int QSslConfiguration::sessionTicketLifeTimeHint() const
+{
+ return d->sslSessionTicketLifeTimeHint;
+}
+
+/*!
Returns the default SSL configuration to be used in new SSL
connections.
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 0000382ed5..949ce70d4c 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -124,6 +124,10 @@ public:
void setSslOption(QSsl::SslOption option, bool on);
bool testSslOption(QSsl::SslOption option) const;
+ QByteArray session() const;
+ void setSession(const QByteArray &session);
+ int sessionTicketLifeTimeHint() const;
+
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 54b7264d3d..71ee8d2bfe 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -85,7 +85,8 @@ public:
peerVerifyDepth(0),
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
- sslOptions(QSslConfigurationPrivate::defaultSslOptions)
+ sslOptions(QSslConfigurationPrivate::defaultSslOptions),
+ sslSessionTicketLifeTimeHint(-1)
{ }
QSslCertificate peerCertificate;
@@ -110,6 +111,9 @@ public:
Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions;
+ QByteArray sslSession;
+ int sslSessionTicketLifeTimeHint;
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslcontext.cpp b/src/network/ssl/qsslcontext.cpp
index 22ad42116b..6d281c390d 100644
--- a/src/network/ssl/qsslcontext.cpp
+++ b/src/network/ssl/qsslcontext.cpp
@@ -57,7 +57,8 @@ extern QString getErrorsFromOpenSsl();
QSslContext::QSslContext()
: ctx(0),
pkey(0),
- session(0)
+ session(0),
+ m_sessionTicketLifeTimeHint(-1)
{
}
@@ -258,6 +259,10 @@ init_context:
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
+ // set persisted session if the user set it
+ if (!configuration.session().isEmpty())
+ sslContext->setSessionASN1(configuration.session());
+
return sslContext;
}
@@ -267,6 +272,12 @@ SSL* QSslContext::createSsl()
SSL* ssl = q_SSL_new(ctx);
q_SSL_clear(ssl);
+ if (!session && !sessionASN1().isEmpty()
+ && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
+ session = q_d2i_SSL_SESSION(0, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above
+ }
+
if (session) {
// Try to resume the last session we cached
if (!q_SSL_set_session(ssl, session)) {
@@ -292,8 +303,34 @@ bool QSslContext::cacheSession(SSL* ssl)
// cache the session the caller gave us and increase reference count
session = q_SSL_get1_session(ssl);
- return (session != NULL);
+ if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ int sessionSize = q_i2d_SSL_SESSION(session, 0);
+ if (sessionSize > 0) {
+ m_sessionASN1.resize(sessionSize);
+ unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
+ if (!q_i2d_SSL_SESSION(session, &data))
+ qWarning("could not store persistent version of SSL session");
+ m_sessionTicketLifeTimeHint = session->tlsext_tick_lifetime_hint;
+ }
+ }
+
+ return (session != 0);
+}
+
+QByteArray QSslContext::sessionASN1() const
+{
+ return m_sessionASN1;
+}
+
+void QSslContext::setSessionASN1(const QByteArray &session)
+{
+ m_sessionASN1 = session;
+}
+
+int QSslContext::sessionTicketLifeTimeHint() const
+{
+ return m_sessionTicketLifeTimeHint;
}
QSslError::SslError QSslContext::error() const
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
index c8578d349e..2b596798a6 100644
--- a/src/network/ssl/qsslcontext_p.h
+++ b/src/network/ssl/qsslcontext_p.h
@@ -69,6 +69,9 @@ public:
SSL* createSsl();
bool cacheSession(SSL*); // should be called when handshake completed
+ QByteArray sessionASN1() const;
+ void setSessionASN1(const QByteArray &sessionASN1);
+ int sessionTicketLifeTimeHint() const;
protected:
QSslContext();
@@ -76,6 +79,8 @@ private:
SSL_CTX* ctx;
EVP_PKEY *pkey;
SSL_SESSION *session;
+ QByteArray m_sessionASN1;
+ int m_sessionTicketLifeTimeHint;
QSslError::SslError errorCode;
QString errorStr;
QSslConfiguration sslConfiguration;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index f2310356df..0e7ac39d14 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -903,6 +903,8 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
d->configuration.sslOptions = configuration.d->sslOptions;
+ d->configuration.sslSession = configuration.session();
+ d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 675bd7d9f7..e378faae68 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -1449,9 +1449,17 @@ void QSslSocketBackendPrivate::continueHandshake()
#endif
// Cache this SSL session inside the QSslContext
- if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionTickets)) {
- if (!sslContextPointer->cacheSession(ssl))
+ if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) {
+ if (!sslContextPointer->cacheSession(ssl)) {
sslContextPointer.clear(); // we could not cache the session
+ } else {
+ // Cache the session for permanent usage as well
+ if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) {
+ if (!sslContextPointer->sessionASN1().isEmpty())
+ configuration.sslSession = sslContextPointer->sessionASN1();
+ configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint();
+ }
+ }
}
connectionEncrypted = true;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 812703b21a..8ad2395a7f 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -332,6 +332,8 @@ DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYA
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)
+DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
+DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return 0, return)
#define RESOLVEFUNC(func) \
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
@@ -801,6 +803,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_load_verify_locations)
RESOLVEFUNC(SSLeay)
RESOLVEFUNC(SSLeay_version)
+ RESOLVEFUNC(i2d_SSL_SESSION)
+ RESOLVEFUNC(d2i_SSL_SESSION)
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 2e01ee4d31..1fd98cc7fb 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -469,6 +469,8 @@ 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);
+int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
+SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length);
// Helper function
class QDateTime;