summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp54
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h3
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp30
-rw-r--r--src/network/network.pro22
-rw-r--r--src/network/ssl/qsslsocket.cpp25
-rw-r--r--src/network/ssl/qsslsocket_p.h3
6 files changed, 92 insertions, 45 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index c2458152a3..72feffda8c 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -82,27 +82,31 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
: state(RunningState),
networkLayerState(Unknown),
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
+ , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
-, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY || type == QHttpNetworkConnection::ConnectionTypeHTTP2)
- ? 1 : defaultHttpChannelCount)
-#else
-, channelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2 ? 1 : defaultHttpChannelCount)
-#endif // QT_NO_SSL
+ || type == QHttpNetworkConnection::ConnectionTypeSPDY
+#endif
+ ? 1 : defaultHttpChannelCount)
+ , channelCount(defaultHttpChannelCount)
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
#endif
, preConnectRequests(0)
, connectionType(type)
{
+ // We allocate all 6 channels even if it's SPDY or HTTP/2 enabled
+ // connection: in case the protocol negotiation via NPN/ALPN fails,
+ // we will have normally working HTTP/1.1.
+ Q_ASSERT(channelCount >= activeChannelCount);
channels = new QHttpNetworkConnectionChannel[channelCount];
}
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName,
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName,
quint16 port, bool encrypt,
QHttpNetworkConnection::ConnectionType type)
: state(RunningState), networkLayerState(Unknown),
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- channelCount(channelCount)
+ activeChannelCount(connectionCount), channelCount(connectionCount)
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
#endif
@@ -147,7 +151,7 @@ void QHttpNetworkConnectionPrivate::pauseConnection()
state = PausedState;
// Disable all socket notifiers
- for (int i = 0; i < channelCount; i++) {
+ for (int i = 0; i < activeChannelCount; i++) {
if (channels[i].socket) {
#ifndef QT_NO_SSL
if (encrypt)
@@ -163,7 +167,7 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
{
state = RunningState;
// Enable all socket notifiers
- for (int i = 0; i < channelCount; i++) {
+ for (int i = 0; i < activeChannelCount; i++) {
if (channels[i].socket) {
#ifndef QT_NO_SSL
if (encrypt)
@@ -184,7 +188,7 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
{
- for (int i = 0; i < channelCount; ++i)
+ for (int i = 0; i < activeChannelCount; ++i)
if (channels[i].socket == socket)
return i;
@@ -210,7 +214,7 @@ bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *sock
channels[otherSocket].ensureConnection();
}
- if (channelCount == 1) {
+ if (activeChannelCount < channelCount) {
if (networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
channels[0].close();
@@ -405,7 +409,7 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
// select another channel
QAuthenticator* otherAuth = 0;
- for (int i = 0; i < channelCount; ++i) {
+ for (int i = 0; i < activeChannelCount; ++i) {
if (i == fromChannel)
continue;
if (isProxy)
@@ -900,7 +904,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
Q_Q(QHttpNetworkConnection);
// check if the reply is currently being processed or it is pipelined in
- for (int i = 0; i < channelCount; ++i) {
+ for (int i = 0; i < activeChannelCount; ++i) {
// is the reply associated the currently processing of this channel?
if (channels[i].reply == reply) {
channels[i].reply = 0;
@@ -1003,7 +1007,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
return;
//resend the necessary ones.
- for (int i = 0; i < channelCount; ++i) {
+ for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
channels[i].resendCurrent = false;
@@ -1023,7 +1027,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
return;
// try to get a free AND connected socket
- for (int i = 0; i < channelCount; ++i) {
+ for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].socket) {
if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
if (dequeueRequest(channels[i].socket))
@@ -1061,7 +1065,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// return fast if there is nothing to pipeline
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
return;
- for (int i = 0; i < channelCount; i++)
+ for (int i = 0; i < activeChannelCount; i++)
if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
fillPipeline(channels[i].socket);
@@ -1077,7 +1081,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
int normalRequests = queuedRequests - preConnectRequests;
neededOpenChannels = qMax(normalRequests, preConnectRequests);
}
- for (int i = 0; i < channelCount && neededOpenChannels > 0; ++i) {
+ for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
bool connectChannel = false;
if (channels[i].socket) {
if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
@@ -1107,7 +1111,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
{
- for (int i = 0 ; i < channelCount; ++i) {
+ for (int i = 0 ; i < activeChannelCount; ++i) {
if (channels[i].reply == reply) {
// emulate a readyRead() from the socket
QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
@@ -1226,7 +1230,7 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
// connection will then be disconnected.
void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
{
- if (channelCount > 1) {
+ if (activeChannelCount > 1) {
// At this time all channels should be unconnected.
Q_ASSERT(!channels[0].isSocketBusy());
Q_ASSERT(!channels[1].isSocketBusy());
@@ -1264,7 +1268,7 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
void QHttpNetworkConnectionPrivate::networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
{
- for (int i = 0 ; i < channelCount; ++i) {
+ for (int i = 0 ; i < activeChannelCount; ++i) {
if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
channels[i].close();
}
@@ -1361,7 +1365,7 @@ void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
d->networkProxy = networkProxy;
// update the authenticator
if (!d->networkProxy.user().isEmpty()) {
- for (int i = 0; i < d->channelCount; ++i) {
+ for (int i = 0; i < d->activeChannelCount; ++i) {
d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
}
@@ -1377,7 +1381,7 @@ QNetworkProxy QHttpNetworkConnection::cacheProxy() const
void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
{
Q_D(QHttpNetworkConnection);
- for (int i = 0; i < d->channelCount; ++i)
+ for (int i = 0; i < d->activeChannelCount; ++i)
d->channels[i].setProxy(networkProxy);
}
@@ -1409,7 +1413,7 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config
return;
// set the config on all channels
- for (int i = 0; i < d->channelCount; ++i)
+ for (int i = 0; i < d->activeChannelCount; ++i)
d->channels[i].setSslConfiguration(config);
}
@@ -1432,7 +1436,7 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel)
return;
if (channel == -1) { // ignore for all channels
- for (int i = 0; i < d->channelCount; ++i) {
+ for (int i = 0; i < d->activeChannelCount; ++i) {
d->channels[i].ignoreSslErrors();
}
@@ -1448,7 +1452,7 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int
return;
if (channel == -1) { // ignore for all channels
- for (int i = 0; i < d->channelCount; ++i) {
+ for (int i = 0; i < d->activeChannelCount; ++i) {
d->channels[i].ignoreSslErrors(errors);
}
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 430c715717..3dd9bde9bd 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -243,6 +243,9 @@ public:
bool encrypt;
bool delayIpv4;
+ // Number of channels we are trying to use at the moment:
+ int activeChannelCount;
+ // The total number of channels we reserved:
const int channelCount;
QTimer delayedConnectionTimer;
QHttpNetworkConnectionChannel *channels; // parallel connections to the server
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 668409a988..c86cc9d8c9 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -1083,12 +1083,36 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
}
}
Q_FALLTHROUGH();
- case QSslConfiguration::NextProtocolNegotiationNone:
+ case QSslConfiguration::NextProtocolNegotiationNone: {
protocolHandler.reset(new QHttpProtocolHandler(this));
+
+ QList<QByteArray> protocols = sslConfiguration.allowedNextProtocols();
+ const int nProtocols = protocols.size();
+ // Clear the protocol that we failed to negotiate, so we do not try
+ // it again on other channels that our connection can create/open.
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
+ protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
+ else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY)
+ protocols.removeAll(QSslConfiguration::NextProtocolSpdy3_0);
+
+ if (nProtocols > protocols.size()) {
+ sslConfiguration.setAllowedNextProtocols(protocols);
+ const int channelCount = connection->d_func()->channelCount;
+ for (int i = 0; i < channelCount; ++i)
+ connection->d_func()->channels[i].setSslConfiguration(sslConfiguration);
+ }
+
connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
- // re-queue requests from SPDY queue to HTTP queue, if any
- requeueSpdyRequests();
+ // We use only one channel for SPDY or HTTP/2, but normally six for
+ // HTTP/1.1 - let's restore this number to the reserved number of
+ // channels:
+ if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
+ connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
+ // re-queue requests from SPDY queue to HTTP queue, if any
+ requeueSpdyRequests();
+ }
break;
+ }
default:
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
"detected unknown Next Protocol Negotiation protocol");
diff --git a/src/network/network.pro b/src/network/network.pro
index 75105bd681..98fbf82275 100644
--- a/src/network/network.pro
+++ b/src/network/network.pro
@@ -22,16 +22,18 @@ include(ssl/ssl.pri)
QMAKE_LIBS += $$QMAKE_LIBS_NETWORK
-ANDROID_BUNDLED_JAR_DEPENDENCIES = \
- jar/QtAndroidBearer-bundled.jar
-ANDROID_JAR_DEPENDENCIES = \
- jar/QtAndroidBearer.jar
-ANDROID_LIB_DEPENDENCIES = \
- plugins/bearer/libqandroidbearer.so
-MODULE_PLUGIN_TYPES = \
- bearer
-ANDROID_PERMISSIONS += \
- android.permission.ACCESS_NETWORK_STATE
+qtConfig(bearermanagement) {
+ ANDROID_BUNDLED_JAR_DEPENDENCIES = \
+ jar/QtAndroidBearer-bundled.jar
+ ANDROID_JAR_DEPENDENCIES = \
+ jar/QtAndroidBearer.jar
+ ANDROID_LIB_DEPENDENCIES = \
+ plugins/bearer/libqandroidbearer.so
+ MODULE_PLUGIN_TYPES = \
+ bearer
+ ANDROID_PERMISSIONS += \
+ android.permission.ACCESS_NETWORK_STATE
+}
MODULE_WINRT_CAPABILITIES = \
internetClient \
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index b4109cadb5..84b8f3a8d9 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -326,6 +326,7 @@
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
#include <QtCore/qelapsedtimer.h>
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qhostinfo.h>
@@ -2679,31 +2680,35 @@ QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
- const QString lowerPeerName = peerName.toLower();
+ const QString lowerPeerName = QString::fromLatin1(QUrl::toAce(peerName));
const QStringList commonNames = cert.subjectInfo(QSslCertificate::CommonName);
for (const QString &commonName : commonNames) {
- if (isMatchingHostname(commonName.toLower(), lowerPeerName))
+ if (isMatchingHostname(commonName, lowerPeerName))
return true;
}
const auto subjectAlternativeNames = cert.subjectAlternativeNames();
const auto altNames = subjectAlternativeNames.equal_range(QSsl::DnsEntry);
for (auto it = altNames.first; it != altNames.second; ++it) {
- if (isMatchingHostname(it->toLower(), lowerPeerName))
+ if (isMatchingHostname(*it, lowerPeerName))
return true;
}
return false;
}
+/*! \internal
+ Checks if the certificate's name \a cn matches the \a hostname.
+ \a hostname must be normalized in ASCII-Compatible Encoding, but \a cn is not normalized
+ */
bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hostname)
{
int wildcard = cn.indexOf(QLatin1Char('*'));
// Check this is a wildcard cert, if not then just compare the strings
if (wildcard < 0)
- return cn == hostname;
+ return QLatin1String(QUrl::toAce(cn)) == hostname;
int firstCnDot = cn.indexOf(QLatin1Char('.'));
int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
@@ -2720,13 +2725,21 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
return false;
+ // Reject wildcard character embedded within the A-labels or U-labels of an internationalized
+ // domain name (RFC6125 section 7.2)
+ if (cn.startsWith(QLatin1String("xn--"), Qt::CaseInsensitive))
+ return false;
+
// Check characters preceding * (if any) match
- if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard)))
+ if (wildcard && hostname.leftRef(wildcard).compare(cn.leftRef(wildcard), Qt::CaseInsensitive) != 0)
return false;
// Check characters following first . match
- if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot))
+ int hnDot = hostname.indexOf(QLatin1Char('.'));
+ if (hostname.midRef(hnDot + 1) != cn.midRef(firstCnDot + 1)
+ && hostname.midRef(hnDot + 1) != QLatin1String(QUrl::toAce(cn.mid(firstCnDot + 1)))) {
return false;
+ }
// Check if the hostname is an IP address, if so then wildcards are not allowed
QHostAddress addr(hostname);
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index cec61d07c1..aec3437422 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -151,7 +151,8 @@ public:
QRegExp::PatternSyntax syntax);
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
- static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
+ Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert,
+ const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)