summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri6
-rw-r--r--src/network/access/qabstractprotocolhandler.cpp68
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h88
-rw-r--r--src/network/access/qhttpmultipart.cpp2
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp175
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h38
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp510
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h12
-rw-r--r--src/network/access/qhttpnetworkreply.cpp54
-rw-r--r--src/network/access/qhttpnetworkreply_p.h17
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp34
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h11
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp431
-rw-r--r--src/network/access/qhttpprotocolhandler_p.h77
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp63
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h4
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp13
-rw-r--r--src/network/access/qnetworkcookie.cpp36
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/access/qnetworkreply.cpp22
-rw-r--r--src/network/access/qnetworkreply.h10
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp20
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h7
-rw-r--r--src/network/access/qnetworkreplynsurlconnectionimpl.mm1
-rw-r--r--src/network/access/qnetworkrequest.cpp15
-rw-r--r--src/network/access/qnetworkrequest.h2
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp1331
-rw-r--r--src/network/access/qspdyprotocolhandler_p.h229
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp2
-rw-r--r--src/network/doc/snippets/network/tcpwait.cpp8
-rw-r--r--src/network/kernel/kernel.pri14
-rw-r--r--src/network/kernel/qauthenticator.cpp21
-rw-r--r--src/network/kernel/qdnslookup.cpp23
-rw-r--r--src/network/kernel/qdnslookup.h6
-rw-r--r--src/network/kernel/qdnslookup_android.cpp3
-rw-r--r--src/network/kernel/qdnslookup_p.h7
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp39
-rw-r--r--src/network/kernel/qdnslookup_win.cpp22
-rw-r--r--src/network/kernel/qdnslookup_winrt.cpp149
-rw-r--r--src/network/kernel/qhostaddress.cpp6
-rw-r--r--src/network/kernel/qhostinfo.cpp4
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp6
-rw-r--r--src/network/kernel/qhostinfo_win.cpp12
-rw-r--r--src/network/kernel/qhostinfo_winrt.cpp194
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp2
-rw-r--r--src/network/kernel/qnetworkinterface_winrt.cpp198
-rw-r--r--src/network/kernel/qnetworkproxy.cpp2
-rw-r--r--src/network/socket/qabstractsocket.cpp39
-rw-r--r--src/network/socket/qabstractsocket.h4
-rw-r--r--src/network/socket/qabstractsocketengine.cpp4
-rw-r--r--src/network/socket/qabstractsocketengine_p.h2
-rw-r--r--src/network/socket/qlocalserver.cpp5
-rw-r--r--src/network/socket/qlocalserver_tcp.cpp2
-rw-r--r--src/network/socket/qlocalsocket.cpp2
-rw-r--r--src/network/socket/qnativesocketengine_p.h6
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp20
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp28
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp1182
-rw-r--r--src/network/socket/qnativesocketengine_winrt_p.h209
-rw-r--r--src/network/socket/qsocks5socketengine.cpp7
-rw-r--r--src/network/socket/qtcpsocket.cpp2
-rw-r--r--src/network/socket/socket.pri19
-rw-r--r--src/network/ssl/qssl.cpp4
-rw-r--r--src/network/ssl/qsslcipher.cpp20
-rw-r--r--src/network/ssl/qsslcipher.h1
-rw-r--r--src/network/ssl/qsslconfiguration.cpp110
-rw-r--r--src/network/ssl/qsslconfiguration.h16
-rw-r--r--src/network/ssl/qsslconfiguration_p.h8
-rw-r--r--src/network/ssl/qsslcontext.cpp62
-rw-r--r--src/network/ssl/qsslcontext_p.h20
-rw-r--r--src/network/ssl/qsslkey.cpp2
-rw-r--r--src/network/ssl/qsslsocket.cpp20
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp15
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp20
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h55
75 files changed, 5273 insertions, 607 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index aaaf05b551..e829d52cbe 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -7,6 +7,9 @@ HEADERS += \
access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
access/qhttpnetworkconnectionchannel_p.h \
+ access/qabstractprotocolhandler_p.h \
+ access/qhttpprotocolhandler_p.h \
+ access/qspdyprotocolhandler_p.h \
access/qnetworkaccessauthenticationmanager_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
@@ -43,6 +46,9 @@ SOURCES += \
access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
access/qhttpnetworkconnectionchannel.cpp \
+ access/qabstractprotocolhandler.cpp \
+ access/qhttpprotocolhandler.cpp \
+ access/qspdyprotocolhandler.cpp \
access/qnetworkaccessauthenticationmanager.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
diff --git a/src/network/access/qabstractprotocolhandler.cpp b/src/network/access/qabstractprotocolhandler.cpp
new file mode 100644
index 0000000000..e72bb63236
--- /dev/null
+++ b/src/network/access/qabstractprotocolhandler.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qabstractprotocolhandler_p.h>
+#include <private/qhttpnetworkconnectionchannel_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QAbstractProtocolHandler::QAbstractProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : m_channel(channel), m_reply(0), m_socket(m_channel->socket), m_connection(m_channel->connection)
+{
+ Q_ASSERT(m_channel);
+ Q_ASSERT(m_socket);
+ Q_ASSERT(m_connection);
+}
+
+QAbstractProtocolHandler::~QAbstractProtocolHandler()
+{
+}
+
+void QAbstractProtocolHandler::setReply(QHttpNetworkReply *reply)
+{
+ m_reply = reply;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
new file mode 100644
index 0000000000..387d08ccac
--- /dev/null
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPROTOCOLHANDLER_H
+#define QABSTRACTPROTOCOLHANDLER_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.
+//
+
+#ifndef QT_NO_HTTP
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkConnectionChannel;
+class QHttpNetworkReply;
+class QAbstractSocket;
+class QHttpNetworkConnection;
+
+class QAbstractProtocolHandler {
+public:
+ QAbstractProtocolHandler(QHttpNetworkConnectionChannel *channel);
+ virtual ~QAbstractProtocolHandler();
+
+ virtual void _q_receiveReply() = 0;
+ virtual void _q_readyRead() = 0;
+ virtual bool sendRequest() = 0;
+ void setReply(QHttpNetworkReply *reply);
+
+protected:
+ QHttpNetworkConnectionChannel *m_channel;
+ QHttpNetworkReply *m_reply;
+ QAbstractSocket *m_socket;
+ QHttpNetworkConnection *m_connection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif // QABSTRACTPROTOCOLHANDLER_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index 20860b0fc9..e8dfa636cd 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -169,7 +169,7 @@ void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &
QNetworkRequest::KnownHeaders), the raw format will be parsed and
the corresponding "cooked" header will be set as well.
- Note: setting the same header twice overrides the previous
+ \note Setting the same header twice overrides the previous
setting. To accomplish the behaviour of multiple HTTP headers of
the same name, you should concatenate the two values, separating
them with a comma (",") and set one single raw header.
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index e0996c4072..32b6d902d0 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -68,7 +68,7 @@
QT_BEGIN_NAMESPACE
-const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
+const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
// The pipeline length. So there will be 4 requests in flight.
const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
@@ -77,20 +77,29 @@ const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName,
+ quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type)
: state(RunningState),
networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- channelCount(defaultChannelCount)
+ hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
+#ifndef QT_NO_SSL
+, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY) ? 1 : defaultHttpChannelCount)
+#else
+, channelCount(defaultHttpChannelCount)
+#endif // QT_NO_SSL
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
#endif
, preConnectRequests(0)
+ , connectionType(type)
{
channels = new QHttpNetworkConnectionChannel[channelCount];
}
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName,
+ quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type)
: state(RunningState), networkLayerState(Unknown),
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
channelCount(channelCount)
@@ -98,6 +107,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCoun
, networkProxy(QNetworkProxy::NoProxy)
#endif
, preConnectRequests(0)
+ , connectionType(type)
{
channels = new QHttpNetworkConnectionChannel[channelCount];
}
@@ -367,6 +377,8 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
// Clean the channel
channels[i].close();
channels[i].reply = 0;
+ if (channels[i].protocolHandler)
+ channels[i].protocolHandler->setReply(0);
channels[i].request = QHttpNetworkRequest();
if (socket)
channels[i].requeueCurrentlyPipelinedRequests();
@@ -512,7 +524,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
request.setHeaderField("Authorization", response);
channels[i].authenticationCredentialsSent = true;
}
@@ -524,7 +536,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
request.setHeaderField("Proxy-Authorization", response);
channels[i].proxyCredentialsSent = true;
}
@@ -546,15 +558,24 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
if (request.isPreConnect())
preConnectRequests++;
- switch (request.priority()) {
- case QHttpNetworkRequest::HighPriority:
- highPriorityQueue.prepend(pair);
- break;
- case QHttpNetworkRequest::NormalPriority:
- case QHttpNetworkRequest::LowPriority:
- lowPriorityQueue.prepend(pair);
- break;
+ if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP) {
+ switch (request.priority()) {
+ case QHttpNetworkRequest::HighPriority:
+ highPriorityQueue.prepend(pair);
+ break;
+ case QHttpNetworkRequest::NormalPriority:
+ case QHttpNetworkRequest::LowPriority:
+ lowPriorityQueue.prepend(pair);
+ break;
+ }
+ }
+#ifndef QT_NO_SSL
+ else { // SPDY
+ if (!pair.second->d_func()->requestIsPrepared)
+ prepareRequest(pair);
+ channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
}
+#endif // QT_NO_SSL
// For Happy Eyeballs the networkLayerState is set to Unknown
// untill we have started the first connection attempt. So no
@@ -807,6 +828,8 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
// is the reply associated the currently processing of this channel?
if (channels[i].reply == reply) {
channels[i].reply = 0;
+ if (channels[i].protocolHandler)
+ channels[i].protocolHandler->setReply(0);
channels[i].request = QHttpNetworkRequest();
channels[i].resendCurrent = false;
@@ -845,6 +868,19 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
return;
}
}
+#ifndef QT_NO_SSL
+ // is the reply inside the SPDY pipeline of this channel already?
+ QMultiMap<int, HttpMessagePair>::iterator it = channels[i].spdyRequestsToSend.begin();
+ QMultiMap<int, HttpMessagePair>::iterator end = channels[i].spdyRequestsToSend.end();
+ for (; it != end; ++it) {
+ if (it.value().second == reply) {
+ channels[i].spdyRequestsToSend.remove(it.key());
+
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+ }
+#endif
}
// remove from the high priority queue
if (!highPriorityQueue.isEmpty()) {
@@ -900,17 +936,39 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// dequeue new ones
- // return fast if there is nothing to do
- if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
- return;
- // try to get a free AND connected socket
- for (int i = 0; i < channelCount; ++i) {
- if (channels[i].socket) {
- if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
- if (dequeueRequest(channels[i].socket))
- channels[i].sendRequest();
+ switch (connectionType) {
+ case QHttpNetworkConnection::ConnectionTypeHTTP: {
+ // return fast if there is nothing to do
+ if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
+ return;
+
+ // try to get a free AND connected socket
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].socket) {
+ if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
+ if (dequeueRequest(channels[i].socket))
+ channels[i].sendRequest();
+ }
}
}
+ break;
+ }
+ case QHttpNetworkConnection::ConnectionTypeSPDY: {
+#ifndef QT_NO_SSL
+ if (channels[0].spdyRequestsToSend.isEmpty())
+ return;
+
+ if (networkLayerState == IPv4)
+ channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
+ else if (networkLayerState == IPv6)
+ channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
+ channels[0].ensureConnection();
+ if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
+ && !channels[0].pendingEncrypt)
+ channels[0].sendRequest();
+#endif // QT_NO_SSL
+ break;
+ }
}
// try to push more into all sockets
@@ -1059,7 +1117,19 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
if (dequeueRequest(channels[0].socket)) {
emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
- } else {
+ }
+#ifndef QT_NO_SSL
+ else if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ QList<HttpMessagePair> spdyPairs = channels[0].spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ // emit error for all replies
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
+ }
+ }
+#endif // QT_NO_SSL
+ else {
// Should not happen
qWarning() << "QHttpNetworkConnectionPrivate::_q_hostLookupFinished could not dequeu request";
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
@@ -1127,31 +1197,41 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
}
#ifndef QT_NO_BEARERMANAGEMENT
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType connectionType,
+ QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+ : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
d->networkSession = networkSession;
d->init();
}
-QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
- : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
+ quint16 port, bool encrypt, QObject *parent,
+ QSharedPointer<QNetworkSession> networkSession,
+ QHttpNetworkConnection::ConnectionType connectionType)
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
+ connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
d->networkSession = networkSession;
d->init();
}
#else
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent,
+ QHttpNetworkConnection::ConnectionType connectionType)
+ : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
d->init();
}
-QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
- : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
+ quint16 port, bool encrypt, QObject *parent,
+ QHttpNetworkConnection::ConnectionType connectionType)
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
+ connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
d->init();
@@ -1225,6 +1305,17 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
+QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
+{
+ Q_D(QHttpNetworkConnection);
+ return d->connectionType;
+}
+
+void QHttpNetworkConnection::setConnectionType(ConnectionType type)
+{
+ Q_D(QHttpNetworkConnection);
+ d->connectionType = type;
+}
// SSL support below
#ifndef QT_NO_SSL
@@ -1299,7 +1390,23 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN
// Also pause the connection because socket notifiers may fire while an user
// dialog is displaying
pauseConnection();
- emit chan->reply->proxyAuthenticationRequired(proxy, auth);
+ QHttpNetworkReply *reply;
+#ifndef QT_NO_SSL
+ if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
+ // but that does not matter because the signal will ultimately be emitted
+ // by the QNetworkAccessManager.
+ Q_ASSERT(chan->spdyRequestsToSend.count() > 0);
+ reply = chan->spdyRequestsToSend.values().first().second;
+ } else { // HTTP
+#endif // QT_NO_SSL
+ reply = chan->reply;
+#ifndef QT_NO_SSL
+ }
+#endif // QT_NO_SSL
+
+ Q_ASSERT(reply);
+ emit reply->proxyAuthenticationRequired(proxy, auth);
resumeConnection();
int i = indexOf(chan->socket);
copyCredentials(i, auth, true);
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 2aaaad24ac..9d4257e217 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -94,12 +94,27 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
Q_OBJECT
public:
+ enum ConnectionType {
+ ConnectionTypeHTTP,
+ ConnectionTypeSPDY
+ };
+
#ifndef QT_NO_BEARERMANAGEMENT
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
- QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+ explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
+ ConnectionType connectionType = ConnectionTypeHTTP,
+ QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession
+ = QSharedPointer<QNetworkSession>());
+ QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
+ bool encrypt = false, QObject *parent = 0,
+ QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>(),
+ ConnectionType connectionType = ConnectionTypeHTTP);
#else
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
- QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
+ explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
+ QObject *parent = 0,
+ ConnectionType connectionType = ConnectionTypeHTTP);
+ QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
+ bool encrypt = false, QObject *parent = 0,
+ ConnectionType connectionType = ConnectionTypeHTTP);
#endif
~QHttpNetworkConnection();
@@ -123,6 +138,9 @@ public:
QHttpNetworkConnectionChannel *channels() const;
+ ConnectionType connectionType();
+ void setConnectionType(ConnectionType type);
+
#ifndef QT_NO_SSL
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
@@ -139,6 +157,8 @@ private:
friend class QHttpNetworkReply;
friend class QHttpNetworkReplyPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttpProtocolHandler;
+ friend class QSpdyProtocolHandler;
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo))
@@ -154,7 +174,7 @@ class QHttpNetworkConnectionPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QHttpNetworkConnection)
public:
- static const int defaultChannelCount;
+ static const int defaultHttpChannelCount;
static const int defaultPipelineLength;
static const int defaultRePipelineLength;
@@ -171,8 +191,10 @@ public:
IPv4or6
};
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
- QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
+ QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type);
+ QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type);
~QHttpNetworkConnectionPrivate();
void init();
@@ -244,6 +266,8 @@ public:
int preConnectRequests;
+ QHttpNetworkConnection::ConnectionType connectionType;
+
#ifndef QT_NO_SSL
QSharedPointer<QSslContext> sslContext;
#endif
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6e786893ed..fb40958178 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -48,6 +49,9 @@
#ifndef QT_NO_HTTP
+#include <private/qhttpprotocolhandler_p.h>
+#include <private/qspdyprotocolhandler_p.h>
+
#ifndef QT_NO_SSL
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
@@ -78,6 +82,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
, proxyAuthMethod(QAuthenticatorPrivate::None)
, authenticationCredentialsSent(false)
, proxyCredentialsSent(false)
+ , protocolHandler(0)
#ifndef QT_NO_SSL
, ignoreAllSslErrors(false)
#endif
@@ -162,8 +167,11 @@ void QHttpNetworkConnectionChannel::init()
if (!sslConfiguration.isNull())
sslSocket->setSslConfiguration(sslConfiguration);
+ } else {
+#endif // QT_NO_SSL
+ protocolHandler.reset(new QHttpProtocolHandler(this));
+#ifndef QT_NO_SSL
}
-
#endif
#ifndef QT_NO_NETWORKPROXY
@@ -193,349 +201,21 @@ void QHttpNetworkConnectionChannel::close()
bool QHttpNetworkConnectionChannel::sendRequest()
{
- if (!reply) {
- // heh, how should that happen!
- qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
- state = QHttpNetworkConnectionChannel::IdleState;
- return false;
- }
-
- switch (state) {
- case QHttpNetworkConnectionChannel::IdleState: { // write the header
- if (!ensureConnection()) {
- // wait for the connection (and encryption) to be done
- // sendRequest will be called again from either
- // _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();
- connection->preConnectFinished(); // will only decrease the counter
- 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;
-
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- replyPrivate->clear();
- replyPrivate->connection = connection;
- replyPrivate->connectionChannel = this;
- replyPrivate->autoDecompress = request.d->autoDecompress;
- replyPrivate->pipeliningUsed = false;
-
- // if the url contains authentication parameters, use the new ones
- // both channels will use the new authentication parameters
- if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
- QUrl url = request.url();
- QAuthenticator &auth = authenticator;
- if (url.userName() != auth.user()
- || (!url.password().isEmpty() && url.password() != auth.password())) {
- auth.setUser(url.userName(QUrl::FullyDecoded));
- auth.setPassword(url.password(QUrl::FullyDecoded));
- connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
- }
- // clear the userinfo, since we use the same request for resending
- // userinfo in url can conflict with the one in the authenticator
- url.setUserInfo(QString());
- request.setUrl(url);
- }
- // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
- // and withCredentials has not been set to true.
- if (request.withCredentials())
- connection->d_func()->createAuthorization(socket, request);
-#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(request,
- (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
-#else
- QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
-#endif
- socket->write(header);
- // flushing is dangerous (QSslSocket calls transmit which might read or error)
-// socket->flush();
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (uploadByteDevice) {
- // connect the signals so this function gets called again
- QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
-
- bytesTotal = request.contentLength();
-
- state = QHttpNetworkConnectionChannel::WritingState; // start writing data
- sendRequest(); //recurse
- } else {
- state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
- sendRequest(); //recurse
- }
-
- break;
- }
- case QHttpNetworkConnectionChannel::WritingState:
- {
- // write the data
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (!uploadByteDevice || bytesTotal == written) {
- if (uploadByteDevice)
- emit reply->dataSendProgress(written, bytesTotal);
- state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
- sendRequest(); // recurse
- break;
- }
-
- // only feed the QTcpSocket buffer when there is less than 32 kB in it
- const qint64 socketBufferFill = 32*1024;
- const qint64 socketWriteMaxSize = 16*1024;
-
-
-#ifndef QT_NO_SSL
- QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
- // if it is really an ssl socket, check more than just bytesToWrite()
- while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
- <= socketBufferFill && bytesTotal != written)
-#else
- while (socket->bytesToWrite() <= socketBufferFill
- && bytesTotal != written)
-#endif
- {
- // get pointer to upload data
- qint64 currentReadSize = 0;
- qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
- const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
-
- if (currentReadSize == -1) {
- // premature eof happened
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
- return false;
- } else if (readPointer == 0 || currentReadSize == 0) {
- // nothing to read currently, break the loop
- break;
- } else {
- qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
- if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
- // socket broke down
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
- return false;
- } else {
- written += currentWriteSize;
- uploadByteDevice->advanceReadPointer(currentWriteSize);
-
- emit reply->dataSendProgress(written, bytesTotal);
-
- if (written == bytesTotal) {
- // make sure this function is called once again
- state = QHttpNetworkConnectionChannel::WaitingState;
- sendRequest();
- break;
- }
- }
- }
- }
- break;
- }
-
- case QHttpNetworkConnectionChannel::WaitingState:
- {
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (uploadByteDevice) {
- QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
- }
-
- // HTTP pipelining
- //connection->d_func()->fillPipeline(socket);
- //socket->flush();
-
- // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
- // this is needed if the sends an reply before we have finished sending the request. In that
- // case receiveReply had been called before but ignored the server reply
- if (socket->bytesAvailable())
- QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
- break;
- }
- case QHttpNetworkConnectionChannel::ReadingState:
- // ignore _q_bytesWritten in these states
- // fall through
- default:
- break;
- }
- return true;
+ Q_ASSERT(!protocolHandler.isNull());
+ return protocolHandler->sendRequest();
}
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
- Q_ASSERT(socket);
-
- if (!reply) {
- if (socket->bytesAvailable() > 0)
- qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
- << socket->bytesAvailable() << "bytes on socket.";
- close();
- return;
- }
-
- // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
- // this function is called from _q_disconnected which is called because
- // of ~QHttpNetworkConnectionPrivate
- if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
- return;
- }
-
- QAbstractSocket::SocketState socketState = socket->state();
-
- // connection might be closed to signal the end of data
- if (socketState == QAbstractSocket::UnconnectedState) {
- if (socket->bytesAvailable() <= 0) {
- if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
- // finish this reply. this case happens when the server did not send a content length
- reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
- allDone();
- return;
- } else {
- handleUnexpectedEOF();
- return;
- }
- } else {
- // socket not connected but still bytes for reading.. just continue in this function
- }
- }
-
- // read loop for the response
- qint64 bytes = 0;
- qint64 lastBytes = bytes;
- do {
- lastBytes = bytes;
-
- QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
- switch (state) {
- case QHttpNetworkReplyPrivate::NothingDoneState: {
- state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
- // fallthrough
- }
- case QHttpNetworkReplyPrivate::ReadingStatusState: {
- qint64 statusBytes = reply->d_func()->readStatus(socket);
- if (statusBytes == -1) {
- // connection broke while reading status. also handled if later _q_disconnected is called
- handleUnexpectedEOF();
- return;
- }
- bytes += statusBytes;
- lastStatus = reply->d_func()->statusCode;
- break;
- }
- case QHttpNetworkReplyPrivate::ReadingHeaderState: {
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- qint64 headerBytes = replyPrivate->readHeader(socket);
- if (headerBytes == -1) {
- // connection broke while reading headers. also handled if later _q_disconnected is called
- handleUnexpectedEOF();
- return;
- }
- bytes += headerBytes;
- // If headers were parsed successfully now it is the ReadingDataState
- if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
- if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
- // remove the Content-Length from header
- replyPrivate->removeAutoDecompressHeader();
- } else {
- replyPrivate->autoDecompress = false;
- }
- if (replyPrivate->statusCode == 100) {
- replyPrivate->clearHttpLayerInformation();
- replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
- break; // ignore
- }
- if (replyPrivate->shouldEmitSignals())
- emit reply->headerChanged();
- // After headerChanged had been emitted
- // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
- // this is handled in the ReadingDataState however
-
- if (!replyPrivate->expectContent()) {
- replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
- allDone();
- break;
- }
- }
- break;
- }
- case QHttpNetworkReplyPrivate::ReadingDataState: {
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- if (socket->state() == QAbstractSocket::ConnectedState &&
- replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
- // (only do the following when still connected, not when we have already been disconnected and there is still data)
- // We already have some HTTP body data. We don't read more from the socket until
- // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
- // we could not limit our read buffer usage.
- // We only do this when shouldEmitSignals==true because our HTTP parsing
- // always needs to parse the 401/407 replies. Therefore they don't really obey
- // to the read buffer maximum size, but we don't care since they should be small.
- return;
- }
-
- if (replyPrivate->userProvidedDownloadBuffer) {
- // the user provided a direct buffer where we should put all our data in.
- // this only works when we can tell the user the content length and he/she can allocate
- // the buffer in that size.
- // note that this call will read only from the still buffered data
- qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
- if (haveRead > 0) {
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- // the user will get notified of it via progress signal
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- } else if (haveRead == 0) {
- // Happens since this called in a loop. Currently no bytes available.
- } else if (haveRead < 0) {
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
- break;
- }
- } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
- // bulk files like images should fulfill these properties and
- // we can therefore save on memory copying
- qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- if (replyPrivate->shouldEmitSignals()) {
- emit reply->readyRead();
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- }
- }
- else
- {
- // use the traditional slower reading (for compressed encoding, chunked encoding,
- // no content-length etc)
- qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
- if (haveRead > 0) {
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- if (replyPrivate->shouldEmitSignals()) {
- emit reply->readyRead();
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- }
- } else if (haveRead == -1) {
- // Some error occurred
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
- break;
- }
- }
- // still in ReadingDataState? This function will be called again by the socket's readyRead
- if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
- break;
+ Q_ASSERT(!protocolHandler.isNull());
+ protocolHandler->_q_receiveReply();
+}
- // everything done, fall through
- }
- case QHttpNetworkReplyPrivate::AllDoneState:
- allDone();
- break;
- default:
- break;
- }
- } while (bytes != lastBytes && reply);
+void QHttpNetworkConnectionChannel::_q_readyRead()
+{
+ Q_ASSERT(!protocolHandler.isNull());
+ protocolHandler->_q_readyRead();
}
// called when unexpectedly reading a -1 or when data is expected but socket is closed
@@ -724,6 +404,7 @@ void QHttpNetworkConnectionChannel::allDone()
if (!resendCurrent) {
request = QHttpNetworkRequest();
reply = 0;
+ protocolHandler->setReply(0);
}
// move next from pipeline to current request
@@ -738,6 +419,7 @@ void QHttpNetworkConnectionChannel::allDone()
request = messagePair.first;
reply = messagePair.second;
+ protocolHandler->setReply(messagePair.second);
state = QHttpNetworkConnectionChannel::ReadingState;
resendCurrent = false;
@@ -982,32 +664,6 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
return (state & QHttpNetworkConnectionChannel::ReadingState);
}
-//private slots
-void QHttpNetworkConnectionChannel::_q_readyRead()
-{
- if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
- // We got a readyRead but no bytes are available..
- // This happens for the Unbuffered QTcpSocket
- // Also check if socket is in ConnectedState since
- // this function may also be invoked via the event loop.
- char c;
- qint64 ret = socket->peek(&c, 1);
- if (ret < 0) {
- _q_error(socket->error());
- // We still need to handle the reply so it emits its signals etc.
- if (reply)
- _q_receiveReply();
- return;
- }
- }
-
- if (isSocketWaiting() || isSocketReading()) {
- state = QHttpNetworkConnectionChannel::ReadingState;
- if (reply)
- _q_receiveReply();
- }
-}
-
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
{
Q_UNUSED(bytes);
@@ -1224,7 +880,21 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
reply->d_func()->errorString = errorString;
emit reply->finishedWithError(errorCode, errorString);
reply = 0;
+ if (protocolHandler)
+ protocolHandler->setReply(0);
}
+#ifndef QT_NO_SSL
+ else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ // emit error for all replies
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emit currentReply->finishedWithError(errorCode, errorString);
+ }
+ }
+#endif // QT_NO_SSL
+
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
@@ -1235,11 +905,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
- // Need to dequeue the request before we can emit the error.
- if (!reply)
- connection->d_func()->dequeueRequest(socket);
- if (reply)
+#ifndef QT_NO_SSL
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
+ } else { // HTTP
+#endif // QT_NO_SSL
+ // Need to dequeue the request before we can emit the error.
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
+#ifndef QT_NO_SSL
+ }
+#endif // QT_NO_SSL
}
#endif
@@ -1251,16 +929,85 @@ void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
#ifndef QT_NO_SSL
void QHttpNetworkConnectionChannel::_q_encrypted()
{
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
+ Q_ASSERT(sslSocket);
+
+ if (!protocolHandler) {
+ switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
+ case QSslConfiguration::NextProtocolNegotiationNegotiated: {
+ QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
+ if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
+ // fall through to create a QHttpProtocolHandler
+ } else if (nextProtocol == QSslConfiguration::NextProtocolSpdy3_0) {
+ protocolHandler.reset(new QSpdyProtocolHandler(this));
+ connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeSPDY);
+ // no need to re-queue requests, if SPDY was enabled on the request it
+ // has gone to the SPDY queue already
+ break;
+ } else {
+ emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
+ "detected unknown Next Protocol Negotiation protocol");
+ break;
+ }
+ }
+ case QSslConfiguration::NextProtocolNegotiationNone:
+ protocolHandler.reset(new QHttpProtocolHandler(this));
+ connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
+ // re-queue requests from SPDY queue to HTTP queue, if any
+ requeueSpdyRequests();
+ break;
+ case QSslConfiguration::NextProtocolNegotiationUnsupported:
+ emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
+ "chosen Next Protocol Negotiation value unsupported");
+ break;
+ default:
+ emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
+ "detected unknown Next Protocol Negotiation protocol");
+ }
+ }
+
if (!socket)
return; // ### error
state = QHttpNetworkConnectionChannel::IdleState;
pendingEncrypt = false;
- if (!reply)
- connection->d_func()->dequeueRequest(socket);
- if (reply)
- emit reply->encrypted();
+
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ // we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
+ if (spdyRequestsToSend.count() > 0)
+ // wait for data from the server first (e.g. initial window, max concurrent requests)
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ } else { // HTTP
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply) {
+ reply->setSpdyWasUsed(false);
+ emit reply->encrypted();
+ }
+ if (reply)
+ sendRequest();
+ }
+}
+
+void QHttpNetworkConnectionChannel::requeueSpdyRequests()
+{
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ connection->d_func()->requeueRequest(spdyPairs.at(a));
+ }
+ spdyRequestsToSend.clear();
+}
+
+void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
+ const char *message)
+{
if (reply)
- sendRequest();
+ emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ }
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1273,8 +1020,21 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
connection->d_func()->pauseConnection();
if (pendingEncrypt && !reply)
connection->d_func()->dequeueRequest(socket);
- if (reply)
- emit reply->sslErrors(errors);
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
+ if (reply)
+ emit reply->sslErrors(errors);
+ }
+#ifndef QT_NO_SSL
+ else { // SPDY
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ // emit SSL errors for all replies
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emit currentReply->sslErrors(errors);
+ }
+ }
+#endif // QT_NO_SSL
connection->d_func()->resumeConnection();
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index c8138b5453..450b6fc419 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -66,6 +66,7 @@
#include <private/qhttpnetworkreply_p.h>
#include <private/qhttpnetworkconnection_p.h>
+#include <private/qabstractprotocolhandler_p.h>
#ifndef QT_NO_HTTP
@@ -103,8 +104,8 @@ public:
bool ssl;
bool isInitialized;
ChannelState state;
- QHttpNetworkRequest request; // current request
- QHttpNetworkReply *reply; // current reply for this request
+ QHttpNetworkRequest request; // current request, only used for HTTP
+ QHttpNetworkReply *reply; // current reply for this request, only used for HTTP
qint64 written;
qint64 bytesTotal;
bool resendCurrent;
@@ -117,13 +118,18 @@ public:
QAuthenticator proxyAuthenticator;
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
+ QScopedPointer<QAbstractProtocolHandler> protocolHandler;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
QSslConfiguration sslConfiguration;
+ QMultiMap<int, HttpMessagePair> spdyRequestsToSend; // sorted by priority
void ignoreSslErrors();
void ignoreSslErrors(const QList<QSslError> &errors);
void setSslConfiguration(const QSslConfiguration &config);
+ void requeueSpdyRequests(); // when we wanted SPDY but got HTTP
+ // to emit the signal for all in-flight replies:
+ void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message);
#endif
#ifndef QT_NO_BEARERMANAGEMENT
QSharedPointer<QNetworkSession> networkSession;
@@ -193,6 +199,8 @@ public:
void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
void _q_encryptedBytesWritten(qint64 bytes); // proceed sending
#endif
+
+ friend class QHttpProtocolHandler;
};
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 1b9e1f5a53..b057fee144 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -265,6 +265,16 @@ bool QHttpNetworkReply::isPipeliningUsed() const
return d_func()->pipeliningUsed;
}
+bool QHttpNetworkReply::isSpdyUsed() const
+{
+ return d_func()->spdyUsed;
+}
+
+void QHttpNetworkReply::setSpdyWasUsed(bool spdy)
+{
+ d_func()->spdyUsed = spdy;
+}
+
QHttpNetworkConnection* QHttpNetworkReply::connection()
{
return d_func()->connection;
@@ -281,9 +291,15 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
connectionCloseEnabled(true),
forceConnectionCloseEnabled(false),
lastChunkRead(false),
- currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0), connection(0),
+ currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0),
+ windowSizeDownload(65536), // 64K initial window size according to SPDY standard
+ windowSizeUpload(65536), // 64K initial window size according to SPDY standard
+ currentlyReceivedDataInWindow(0),
+ currentlyUploadedDataInWindow(0),
+ totallyUploadedData(0),
+ connection(0),
autoDecompress(false), responseData(), requestIsPrepared(false)
- ,pipeliningUsed(false), downstreamLimited(false)
+ ,pipeliningUsed(false), spdyUsed(false), downstreamLimited(false)
,userProvidedDownloadBuffer(0)
#ifndef QT_NO_COMPRESS
,inflateStrm(0)
@@ -347,7 +363,7 @@ bool QHttpNetworkReplyPrivate::isCompressed()
void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
{
// The header "Content-Encoding = gzip" is retained.
- // Content-Length is removed since the actual one send by the server is for compressed data
+ // Content-Length is removed since the actual one sent by the server is for compressed data
QByteArray name("content-length");
QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
end = fields.end();
@@ -550,15 +566,7 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
// allocate inflate state
if (!inflateStrm)
inflateStrm = new z_stream;
- inflateStrm->zalloc = Z_NULL;
- inflateStrm->zfree = Z_NULL;
- inflateStrm->opaque = Z_NULL;
- inflateStrm->avail_in = 0;
- inflateStrm->next_in = Z_NULL;
- // "windowBits can also be greater than 15 for optional gzip decoding.
- // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
- // http://www.zlib.net/manual.html
- int ret = inflateInit2(inflateStrm, MAX_WBITS+32);
+ int ret = initializeInflateStream();
if (ret != Z_OK)
return -1;
}
@@ -703,8 +711,28 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
}
#ifndef QT_NO_COMPRESS
+int QHttpNetworkReplyPrivate::initializeInflateStream()
+{
+ inflateStrm->zalloc = Z_NULL;
+ inflateStrm->zfree = Z_NULL;
+ inflateStrm->opaque = Z_NULL;
+ inflateStrm->avail_in = 0;
+ inflateStrm->next_in = Z_NULL;
+ // "windowBits can also be greater than 15 for optional gzip decoding.
+ // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
+ // http://www.zlib.net/manual.html
+ int ret = inflateInit2(inflateStrm, MAX_WBITS+32);
+ Q_ASSERT(ret == Z_OK);
+ return ret;
+}
+
qint64 QHttpNetworkReplyPrivate::uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out)
{
+ if (!inflateStrm) { // happens when called from the SPDY protocol handler
+ inflateStrm = new z_stream;
+ initializeInflateStream();
+ }
+
if (!inflateStrm)
return -1;
@@ -893,7 +921,7 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *c
bool QHttpNetworkReplyPrivate::shouldEmitSignals()
{
// for 401 & 407 don't emit the data signals. Content along with these
- // responses are send only if the authentication fails.
+ // responses are sent only if the authentication fails.
return (statusCode != 401 && statusCode != 407);
}
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 7aea9f14ec..e15ace0072 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -132,6 +132,8 @@ public:
bool isFinished() const;
bool isPipeliningUsed() const;
+ bool isSpdyUsed() const;
+ void setSpdyWasUsed(bool spdy);
QHttpNetworkConnection* connection();
@@ -164,6 +166,8 @@ private:
friend class QHttpNetworkConnection;
friend class QHttpNetworkConnectionPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttpProtocolHandler;
+ friend class QSpdyProtocolHandler;
};
@@ -204,7 +208,11 @@ public:
ReadingStatusState,
ReadingHeaderState,
ReadingDataState,
- AllDoneState
+ AllDoneState,
+ SPDYSYNSent,
+ SPDYUploading,
+ SPDYHalfClosed,
+ SPDYClosed
} state;
QHttpNetworkRequest request;
@@ -225,6 +233,11 @@ public:
qint64 currentChunkSize;
qint64 currentChunkRead;
qint64 readBufferMaxSize;
+ qint32 windowSizeDownload; // only for SPDY
+ qint32 windowSizeUpload; // only for SPDY
+ qint32 currentlyReceivedDataInWindow; // only for SPDY
+ qint32 currentlyUploadedDataInWindow; // only for SPDY
+ qint64 totallyUploadedData; // only for SPDY
QPointer<QHttpNetworkConnection> connection;
QPointer<QHttpNetworkConnectionChannel> connectionChannel;
@@ -235,12 +248,14 @@ public:
bool requestIsPrepared;
bool pipeliningUsed;
+ bool spdyUsed;
bool downstreamLimited;
char* userProvidedDownloadBuffer;
#ifndef QT_NO_COMPRESS
z_stream_s *inflateStrm;
+ int initializeInflateStream();
qint64 uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out);
#endif
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index d9f9b555d7..8ad04c9885 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -49,8 +49,8 @@ QT_BEGIN_NAMESPACE
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
- autoDecompress(false), pipeliningAllowed(false), withCredentials(true),
- preConnect(false)
+ autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false),
+ withCredentials(true), preConnect(false)
{
}
@@ -62,6 +62,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
uploadByteDevice = other.uploadByteDevice;
autoDecompress = other.autoDecompress;
pipeliningAllowed = other.pipeliningAllowed;
+ spdyAllowed = other.spdyAllowed;
customVerb = other.customVerb;
withCredentials = other.withCredentials;
ssl = other.ssl;
@@ -80,6 +81,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (uploadByteDevice == other.uploadByteDevice)
&& (autoDecompress == other.autoDecompress)
&& (pipeliningAllowed == other.pipeliningAllowed)
+ && (spdyAllowed == other.spdyAllowed)
// we do not clear the customVerb in setOperation
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
&& (withCredentials == other.withCredentials)
@@ -87,9 +89,9 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (preConnect == other.preConnect);
}
-QByteArray QHttpNetworkRequestPrivate::methodName() const
+QByteArray QHttpNetworkRequest::methodName() const
{
- switch (operation) {
+ switch (d->operation) {
case QHttpNetworkRequest::Get:
return "GET";
case QHttpNetworkRequest::Head:
@@ -107,24 +109,24 @@ QByteArray QHttpNetworkRequestPrivate::methodName() const
case QHttpNetworkRequest::Connect:
return "CONNECT";
case QHttpNetworkRequest::Custom:
- return customVerb;
+ return d->customVerb;
default:
break;
}
return QByteArray();
}
-QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
+QByteArray QHttpNetworkRequest::uri(bool throughProxy) const
{
QUrl::FormattingOptions format(QUrl::RemoveFragment | QUrl::RemoveUserInfo | QUrl::FullyEncoded);
- // for POST, query data is send as content
- if (operation == QHttpNetworkRequest::Post && !uploadByteDevice)
+ // for POST, query data is sent as content
+ if (d->operation == QHttpNetworkRequest::Post && !d->uploadByteDevice)
format |= QUrl::RemoveQuery;
// for requests through proxy, the Request-URI contains full url
if (!throughProxy)
format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
- QUrl copy = url;
+ QUrl copy = d->url;
if (copy.path().isEmpty())
copy.setPath(QStringLiteral("/"));
QByteArray uri = copy.toEncoded(format);
@@ -137,9 +139,9 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
QByteArray ba;
ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
- ba += request.d->methodName();
+ ba += request.methodName();
ba += ' ';
- ba += request.d->uri(throughProxy);
+ ba += request.uri(throughProxy);
ba += " HTTP/";
ba += QByteArray::number(request.majorVersion());
@@ -299,6 +301,16 @@ void QHttpNetworkRequest::setPipeliningAllowed(bool b)
d->pipeliningAllowed = b;
}
+bool QHttpNetworkRequest::isSPDYAllowed() const
+{
+ return d->spdyAllowed;
+}
+
+void QHttpNetworkRequest::setSPDYAllowed(bool b)
+{
+ d->spdyAllowed = b;
+}
+
bool QHttpNetworkRequest::withCredentials() const
{
return d->withCredentials;
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index ce9fbb1509..09847d715c 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -114,6 +114,9 @@ public:
bool isPipeliningAllowed() const;
void setPipeliningAllowed(bool b);
+ bool isSPDYAllowed() const;
+ void setSPDYAllowed(bool b);
+
bool withCredentials() const;
void setWithCredentials(bool b);
@@ -126,11 +129,16 @@ public:
void setUploadByteDevice(QNonContiguousByteDevice *bd);
QNonContiguousByteDevice* uploadByteDevice() const;
+ QByteArray methodName() const;
+ QByteArray uri(bool throughProxy) const;
+
private:
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
friend class QHttpNetworkRequestPrivate;
friend class QHttpNetworkConnectionPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttpProtocolHandler;
+ friend class QSpdyProtocolHandler;
};
class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
@@ -141,8 +149,6 @@ public:
QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
~QHttpNetworkRequestPrivate();
bool operator==(const QHttpNetworkRequestPrivate &other) const;
- QByteArray methodName() const;
- QByteArray uri(bool throughProxy) const;
static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
@@ -152,6 +158,7 @@ public:
mutable QNonContiguousByteDevice* uploadByteDevice;
bool autoDecompress;
bool pipeliningAllowed;
+ bool spdyAllowed;
bool withCredentials;
bool ssl;
bool preConnect;
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
new file mode 100644
index 0000000000..15cba48285
--- /dev/null
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qhttpprotocolhandler_p.h>
+#include <private/qnoncontiguousbytedevice_p.h>
+#include <private/qhttpnetworkconnectionchannel_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : QAbstractProtocolHandler(channel)
+{
+}
+
+void QHttpProtocolHandler::_q_receiveReply()
+{
+ Q_ASSERT(m_socket);
+
+ if (!m_reply) {
+ if (m_socket->bytesAvailable() > 0)
+ qWarning() << "QAbstractProtocolHandler::_q_receiveReply() called without QHttpNetworkReply,"
+ << m_socket->bytesAvailable() << "bytes on socket.";
+ m_channel->close();
+ return;
+ }
+
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (!qobject_cast<QHttpNetworkConnection*>(m_connection)) {
+ return;
+ }
+
+ QAbstractSocket::SocketState socketState = m_socket->state();
+
+ // connection might be closed to signal the end of data
+ if (socketState == QAbstractSocket::UnconnectedState) {
+ if (m_socket->bytesAvailable() <= 0) {
+ if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
+ // finish this reply. this case happens when the server did not send a content length
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ return;
+ } else {
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ } else {
+ // socket not connected but still bytes for reading.. just continue in this function
+ }
+ }
+
+ // read loop for the response
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
+ QHttpNetworkReplyPrivate::ReplyState state = m_reply->d_func()->state;
+ switch (state) {
+ case QHttpNetworkReplyPrivate::NothingDoneState: {
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ // fallthrough
+ }
+ case QHttpNetworkReplyPrivate::ReadingStatusState: {
+ qint64 statusBytes = m_reply->d_func()->readStatus(m_socket);
+ if (statusBytes == -1) {
+ // connection broke while reading status. also handled if later _q_disconnected is called
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ bytes += statusBytes;
+ m_channel->lastStatus = m_reply->d_func()->statusCode;
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingHeaderState: {
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ qint64 headerBytes = replyPrivate->readHeader(m_socket);
+ if (headerBytes == -1) {
+ // connection broke while reading headers. also handled if later _q_disconnected is called
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ bytes += headerBytes;
+ // If headers were parsed successfully now it is the ReadingDataState
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
+ if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
+ // remove the Content-Length from header
+ replyPrivate->removeAutoDecompressHeader();
+ } else {
+ replyPrivate->autoDecompress = false;
+ }
+ if (replyPrivate->statusCode == 100) {
+ replyPrivate->clearHttpLayerInformation();
+ replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ break; // ignore
+ }
+ if (replyPrivate->shouldEmitSignals())
+ emit m_reply->headerChanged();
+ // After headerChanged had been emitted
+ // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
+ // this is handled in the ReadingDataState however
+
+ if (!replyPrivate->expectContent()) {
+ replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ break;
+ }
+ }
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingDataState: {
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ if (m_socket->state() == QAbstractSocket::ConnectedState &&
+ replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
+ // (only do the following when still connected, not when we have already been disconnected and there is still data)
+ // We already have some HTTP body data. We don't read more from the socket until
+ // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
+ // we could not limit our read buffer usage.
+ // We only do this when shouldEmitSignals==true because our HTTP parsing
+ // always needs to parse the 401/407 replies. Therefore they don't really obey
+ // to the read buffer maximum size, but we don't care since they should be small.
+ return;
+ }
+
+ if (replyPrivate->userProvidedDownloadBuffer) {
+ // the user provided a direct buffer where we should put all our data in.
+ // this only works when we can tell the user the content length and he/she can allocate
+ // the buffer in that size.
+ // note that this call will read only from the still buffered data
+ qint64 haveRead = replyPrivate->readBodyVeryFast(m_socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
+ if (haveRead > 0) {
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ // the user will get notified of it via progress signal
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ } else if (haveRead == 0) {
+ // Happens since this called in a loop. Currently no bytes available.
+ } else if (haveRead < 0) {
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::RemoteHostClosedError);
+ break;
+ }
+ } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
+ && replyPrivate->bodyLength > 0) {
+ // bulk files like images should fulfill these properties and
+ // we can therefore save on memory copying
+ qint64 haveRead = replyPrivate->readBodyFast(m_socket, &replyPrivate->responseData);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ if (replyPrivate->shouldEmitSignals()) {
+ emit m_reply->readyRead();
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ }
+ else
+ {
+ // use the traditional slower reading (for compressed encoding, chunked encoding,
+ // no content-length etc)
+ qint64 haveRead = replyPrivate->readBody(m_socket, &replyPrivate->responseData);
+ if (haveRead > 0) {
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ if (replyPrivate->shouldEmitSignals()) {
+ emit m_reply->readyRead();
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ } else if (haveRead == -1) {
+ // Some error occurred
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
+ break;
+ }
+ }
+ // still in ReadingDataState? This function will be called again by the socket's readyRead
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
+ break;
+
+ // everything done, fall through
+ }
+ case QHttpNetworkReplyPrivate::AllDoneState:
+ m_channel->allDone();
+ break;
+ default:
+ break;
+ }
+ } while (bytes != lastBytes && m_reply);
+}
+
+void QHttpProtocolHandler::_q_readyRead()
+{
+ if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
+ // We got a readyRead but no bytes are available..
+ // This happens for the Unbuffered QTcpSocket
+ // Also check if socket is in ConnectedState since
+ // this function may also be invoked via the event loop.
+ char c;
+ qint64 ret = m_socket->peek(&c, 1);
+ if (ret < 0) {
+ m_channel->_q_error(m_socket->error());
+ // We still need to handle the reply so it emits its signals etc.
+ if (m_reply)
+ _q_receiveReply();
+ return;
+ }
+ }
+
+ if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
+ m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
+ if (m_reply)
+ _q_receiveReply();
+ }
+}
+
+bool QHttpProtocolHandler::sendRequest()
+{
+ m_reply = m_channel->reply;
+
+ if (!m_reply) {
+ // heh, how should that happen!
+ qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply";
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ return false;
+ }
+
+ switch (m_channel->state) {
+ case QHttpNetworkConnectionChannel::IdleState: { // write the header
+ if (!m_channel->ensureConnection()) {
+ // wait for the connection (and encryption) to be done
+ // sendRequest will be called again from either
+ // _q_connected or _q_encrypted
+ return false;
+ }
+ QString scheme = m_channel->request.url().scheme();
+ if (scheme == QLatin1String("preconnect-http")
+ || scheme == QLatin1String("preconnect-https")) {
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ m_connection->preConnectFinished(); // will only decrease the counter
+ m_reply = 0; // so we can reuse this channel
+ return true; // we have a working connection and are done
+ }
+
+ m_channel->written = 0; // excluding the header
+ m_channel->bytesTotal = 0;
+
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ replyPrivate->clear();
+ replyPrivate->connection = m_connection;
+ replyPrivate->connectionChannel = m_channel;
+ replyPrivate->autoDecompress = m_channel->request.d->autoDecompress;
+ replyPrivate->pipeliningUsed = false;
+
+ // if the url contains authentication parameters, use the new ones
+ // both channels will use the new authentication parameters
+ if (!m_channel->request.url().userInfo().isEmpty() && m_channel->request.withCredentials()) {
+ QUrl url = m_channel->request.url();
+ QAuthenticator &auth = m_channel->authenticator;
+ if (url.userName() != auth.user()
+ || (!url.password().isEmpty() && url.password() != auth.password())) {
+ auth.setUser(url.userName());
+ auth.setPassword(url.password());
+ m_connection->d_func()->copyCredentials(m_connection->d_func()->indexOf(m_socket), &auth, false);
+ }
+ // clear the userinfo, since we use the same request for resending
+ // userinfo in url can conflict with the one in the authenticator
+ url.setUserInfo(QString());
+ m_channel->request.setUrl(url);
+ }
+ // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
+ // and withCredentials has not been set to true.
+ if (m_channel->request.withCredentials())
+ m_connection->d_func()->createAuthorization(m_socket, m_channel->request);
+#ifndef QT_NO_NETWORKPROXY
+ QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request,
+ (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+#else
+ QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
+#endif
+ m_socket->write(header);
+ // flushing is dangerous (QSslSocket calls transmit which might read or error)
+// m_socket->flush();
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (uploadByteDevice) {
+ // connect the signals so this function gets called again
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
+
+ m_channel->bytesTotal = m_channel->request.contentLength();
+
+ m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
+ sendRequest(); //recurse
+ } else {
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); //recurse
+ }
+
+ break;
+ }
+ case QHttpNetworkConnectionChannel::WritingState:
+ {
+ // write the data
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
+ if (uploadByteDevice)
+ emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); // recurse
+ break;
+ }
+
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ const qint64 socketBufferFill = 32*1024;
+ const qint64 socketWriteMaxSize = 16*1024;
+
+
+#ifndef QT_NO_SSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(m_socket);
+ // if it is really an ssl socket, check more than just bytesToWrite()
+ while ((m_socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
+ <= socketBufferFill && m_channel->bytesTotal != m_channel->written)
+#else
+ while (m_socket->bytesToWrite() <= socketBufferFill
+ && m_channel->bytesTotal != m_channel->written)
+#endif
+ {
+ // get pointer to upload data
+ qint64 currentReadSize = 0;
+ qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
+ const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
+
+ if (currentReadSize == -1) {
+ // premature eof happened
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else if (readPointer == 0 || currentReadSize == 0) {
+ // nothing to read currently, break the loop
+ break;
+ } else {
+ qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
+ // socket broke down
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else {
+ m_channel->written += currentWriteSize;
+ uploadByteDevice->advanceReadPointer(currentWriteSize);
+
+ emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
+
+ if (m_channel->written == m_channel->bytesTotal) {
+ // make sure this function is called once again
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState;
+ sendRequest();
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case QHttpNetworkConnectionChannel::WaitingState:
+ {
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (uploadByteDevice) {
+ QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
+ }
+
+ // HTTP pipelining
+ //m_connection->d_func()->fillPipeline(m_socket);
+ //m_socket->flush();
+
+ // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
+ // this is needed if the sends an reply before we have finished sending the request. In that
+ // case receiveReply had been called before but ignored the server reply
+ if (m_socket->bytesAvailable())
+ QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
+ break;
+ }
+ case QHttpNetworkConnectionChannel::ReadingState:
+ // ignore _q_bytesWritten in these states
+ // fall through
+ default:
+ break;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpprotocolhandler_p.h b/src/network/access/qhttpprotocolhandler_p.h
new file mode 100644
index 0000000000..2bbc044b6c
--- /dev/null
+++ b/src/network/access/qhttpprotocolhandler_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPPROTOCOLHANDLER_H
+#define QHTTPPROTOCOLHANDLER_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 <private/qabstractprotocolhandler_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QHttpProtocolHandler : public QAbstractProtocolHandler {
+public:
+ QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel);
+
+private:
+ virtual void _q_receiveReply() Q_DECL_OVERRIDE;
+ virtual void _q_readyRead() Q_DECL_OVERRIDE;
+ virtual bool sendRequest() Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index ee3911c72c..e03dcb8ead 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -60,6 +60,10 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const
QNetworkReply::NetworkError code;
// we've got an error
switch (httpStatusCode) {
+ case 400: // Bad Request
+ code = QNetworkReply::ProtocolInvalidOperationError;
+ break;
+
case 401: // Authorization required
code = QNetworkReply::AuthenticationRequiredError;
break;
@@ -80,15 +84,34 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const
code = QNetworkReply::ProxyAuthenticationRequiredError;
break;
+ case 409: // Resource Conflict
+ code = QNetworkReply::ContentConflictError;
+ break;
+
+ case 410: // Content no longer available
+ code = QNetworkReply::ContentGoneError;
+ break;
+
case 418: // I'm a teapot
code = QNetworkReply::ProtocolInvalidOperationError;
break;
+ case 500: // Internal Server Error
+ code = QNetworkReply::InternalServerError;
+ break;
+
+ case 501: // Server does not support this functionality
+ code = QNetworkReply::OperationNotImplementedError;
+ break;
+
+ case 503: // Service unavailable
+ code = QNetworkReply::ServiceUnavailableError;
+ break;
default:
if (httpStatusCode > 500) {
// some kind of server error
- code = QNetworkReply::ProtocolUnknownError;
+ code = QNetworkReply::UnknownServerError;
} else if (httpStatusCode >= 400) {
// content error we did not handle above
code = QNetworkReply::UnknownContentError;
@@ -157,11 +180,15 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
// Q_OBJECT
public:
#ifdef QT_NO_BEARERMANAGEMENT
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
- : QHttpNetworkConnection(hostName, port, encrypt)
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType connectionType)
+ : QHttpNetworkConnection(hostName, port, encrypt, connectionType)
#else
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
- : QHttpNetworkConnection(hostName, port, encrypt, /*parent=*/0, networkSession)
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType connectionType,
+ QSharedPointer<QNetworkSession> networkSession)
+ : QHttpNetworkConnection(hostName, port, encrypt, connectionType, /*parent=*/0,
+ networkSession)
#endif
{
setExpires(true);
@@ -207,6 +234,7 @@ QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
, synchronous(false)
, incomingStatusCode(0)
, isPipeliningUsed(false)
+ , isSpdyUsed(false)
, incomingContentLength(-1)
, incomingErrorCode(QNetworkReply::NoError)
, downloadBuffer(0)
@@ -258,6 +286,19 @@ void QHttpThreadDelegate::startRequest()
QUrl urlCopy = httpRequest.url();
urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
+ QHttpNetworkConnection::ConnectionType connectionType
+ = QHttpNetworkConnection::ConnectionTypeHTTP;
+#ifndef QT_NO_SSL
+ if (httpRequest.isSPDYAllowed() && ssl) {
+ connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
+ urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
+ QList<QByteArray> nextProtocols;
+ nextProtocols << QSslConfiguration::NextProtocolSpdy3_0
+ << QSslConfiguration::NextProtocolHttp1_1;
+ incomingSslConfiguration.setAllowedNextProtocols(nextProtocols);
+ }
+#endif // QT_NO_SSL
+
#ifndef QT_NO_NETWORKPROXY
if (transparentProxy.type() != QNetworkProxy::NoProxy)
cacheKey = makeCacheKey(urlCopy, &transparentProxy);
@@ -274,9 +315,12 @@ void QHttpThreadDelegate::startRequest()
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
#ifdef QT_NO_BEARERMANAGEMENT
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
+ connectionType);
#else
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
+ connectionType,
+ networkSession);
#endif
#ifndef QT_NO_SSL
// Set the QSslConfiguration from this QNetworkRequest.
@@ -552,13 +596,15 @@ void QHttpThreadDelegate::headerChangedSlot()
incomingReasonPhrase = httpReply->reasonPhrase();
isPipeliningUsed = httpReply->isPipeliningUsed();
incomingContentLength = httpReply->contentLength();
+ isSpdyUsed = httpReply->isSpdyUsed();
emit downloadMetaData(incomingHeaders,
incomingStatusCode,
incomingReasonPhrase,
isPipeliningUsed,
downloadBuffer,
- incomingContentLength);
+ incomingContentLength,
+ isSpdyUsed);
}
void QHttpThreadDelegate::synchronousHeaderChangedSlot()
@@ -574,6 +620,7 @@ void QHttpThreadDelegate::synchronousHeaderChangedSlot()
incomingStatusCode = httpReply->statusCode();
incomingReasonPhrase = httpReply->reasonPhrase();
isPipeliningUsed = httpReply->isPipeliningUsed();
+ isSpdyUsed = httpReply->isSpdyUsed();
incomingContentLength = httpReply->contentLength();
}
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index d9ef1a0a55..6e6d3b5797 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -111,6 +111,7 @@ public:
int incomingStatusCode;
QString incomingReasonPhrase;
bool isPipeliningUsed;
+ bool isSpdyUsed;
qint64 incomingContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
@@ -139,7 +140,8 @@ signals:
void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
void sslConfigurationChanged(const QSslConfiguration);
#endif
- void downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void downloadMetaData(QList<QPair<QByteArray,QByteArray> >, int, QString, bool,
+ QSharedPointer<char>, qint64, bool);
void downloadProgress(qint64, qint64);
void downloadData(QByteArray);
void error(QNetworkReply::NetworkError, const QString);
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index c89419091f..473acc5f22 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -977,6 +977,12 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
\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 Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols()
+ on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in
+ the list of allowed protocols. When using SPDY, one single connection per host is
+ enough, i.e. calling this method multiple times per host will not result in faster
+ network transactions.
+
\note This function has no possibility to report errors.
\sa connectToHost(), get(), post(), put(), deleteResource()
@@ -991,6 +997,13 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
QNetworkRequest request(url);
if (sslConfiguration != QSslConfiguration::defaultConfiguration())
request.setSslConfiguration(sslConfiguration);
+
+ // There is no way to enable SPDY via a request, so we need to check
+ // the ssl configuration whether SPDY is allowed here.
+ if (sslConfiguration.allowedNextProtocols().contains(
+ QSslConfiguration::NextProtocolSpdy3_0))
+ request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true);
+
get(request);
}
#endif
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index a871c04d56..664bc8282c 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -51,6 +51,7 @@
#include "QtCore/qstring.h"
#include "QtCore/qstringlist.h"
#include "QtCore/qurl.h"
+#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -466,12 +467,19 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
}
if (!d->domain.isEmpty()) {
result += "; domain=";
- QString domainNoDot = d->domain;
- if (domainNoDot.startsWith(QLatin1Char('.'))) {
+ if (d->domain.startsWith(QLatin1Char('.'))) {
result += '.';
- domainNoDot = domainNoDot.mid(1);
+ result += QUrl::toAce(d->domain.mid(1));
+ } else {
+ QHostAddress hostAddr(d->domain);
+ if (hostAddr.protocol() == QAbstractSocket::IPv6Protocol) {
+ result += '[';
+ result += d->domain.toUtf8();
+ result += ']';
+ } else {
+ result += QUrl::toAce(d->domain);
+ }
}
- result += QUrl::toAce(domainNoDot);
}
if (!d->path.isEmpty()) {
result += "; path=";
@@ -1015,14 +1023,20 @@ void QNetworkCookie::normalize(const QUrl &url)
d->path = defaultPath;
}
- if (d->domain.isEmpty())
+ if (d->domain.isEmpty()) {
d->domain = url.host();
- else if (!d->domain.startsWith(QLatin1Char('.')))
- // Ensure the domain starts with a dot if its field was not empty
- // in the HTTP header. There are some servers that forget the
- // leading dot and this is actually forbidden according to RFC 2109,
- // but all browsers accept it anyway so we do that as well.
- d->domain.prepend(QLatin1Char('.'));
+ } else {
+ QHostAddress hostAddress(d->domain);
+ if (hostAddress.protocol() != QAbstractSocket::IPv4Protocol
+ && hostAddress.protocol() != QAbstractSocket::IPv6Protocol
+ && !d->domain.startsWith(QLatin1Char('.'))) {
+ // Ensure the domain starts with a dot if its field was not empty
+ // in the HTTP header. There are some servers that forget the
+ // leading dot and this is actually forbidden according to RFC 2109,
+ // but all browsers accept it anyway so we do that as well.
+ d->domain.prepend(QLatin1Char('.'));
+ }
+ }
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index 2f2d1ab9a9..a1cbd9364e 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -518,7 +518,7 @@ void QNetworkDiskCache::setMaximumCacheSize(qint64 size)
knows about that QNetworkDiskCache does not, for example the number of times
a cache is accessed.
- Note: cacheSize() calls expire if the current cache size is unknown.
+ \note cacheSize() calls expire if the current cache size is unknown.
\sa maximumCacheSize(), fileMetaData()
*/
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index ba6f706f7a..bfe870c783 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -173,6 +173,21 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
again, but this failed for example because the upload data
could not be read a second time.
+ \value ContentConflictError the request could not be completed due
+ to a conflict with the current state of the resource.
+
+ \value ContentGoneError the requested resource is no longer
+ available at the server.
+
+ \value InternalServerError the server encountered an unexpected
+ condition which prevented it from fulfilling the request.
+
+ \value OperationNotImplementedError the server does not support the
+ functionality required to fulfill the request.
+
+ \value ServiceUnavailableError the server is unable to handle the
+ request at this time.
+
\value ProtocolUnknownError the Network Access API cannot
honor the request because the protocol is not known
@@ -191,6 +206,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
\value ProtocolFailure a breakdown in protocol was
detected (parsing error, invalid or unexpected responses, etc.)
+ \value UnknownServerError an unknown error related to
+ the server response was detected
+
\sa error()
*/
@@ -735,7 +753,7 @@ qint64 QNetworkReply::writeData(const char *, qint64)
Sets the associated operation for this object to be \a
operation. This value will be returned by operation().
- Note: the operation should be set when this object is created and
+ \note The operation should be set when this object is created and
not changed again.
\sa operation(), setRequest()
@@ -750,7 +768,7 @@ void QNetworkReply::setOperation(QNetworkAccessManager::Operation operation)
Sets the associated request for this object to be \a request. This
value will be returned by request().
- Note: the request should be set when this object is created and
+ \note The request should be set when this object is created and
not changed again.
\sa request(), setOperation()
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index a7db2d189c..f11a5e816a 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -93,12 +93,20 @@ public:
ContentNotFoundError,
AuthenticationRequiredError,
ContentReSendError,
+ ContentConflictError,
+ ContentGoneError,
UnknownContentError = 299,
// protocol errors
ProtocolUnknownError = 301,
ProtocolInvalidOperationError,
- ProtocolFailure = 399
+ ProtocolFailure = 399,
+
+ // Server side errors (401-499)
+ InternalServerError = 401,
+ OperationNotImplementedError,
+ ServiceUnavailableError,
+ UnknownServerError = 499
};
~QNetworkReply();
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 29d23bfd8f..de4c8d0964 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -752,6 +752,9 @@ void QNetworkReplyHttpImplPrivate::postRequest()
if (request.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
httpRequest.setPipeliningAllowed(true);
+ if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool() == true)
+ httpRequest.setSPDYAllowed(true);
+
if (static_cast<QNetworkRequest::LoadControl>
(request.attribute(QNetworkRequest::AuthenticationReuseAttribute,
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
@@ -811,8 +814,12 @@ void QNetworkReplyHttpImplPrivate::postRequest()
QObject::connect(delegate, SIGNAL(downloadFinished()),
q, SLOT(replyFinished()),
Qt::QueuedConnection);
- QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
- q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+ QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,
+ int, QString, bool,
+ QSharedPointer<char>, qint64, bool)),
+ q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
+ int, QString, bool,
+ QSharedPointer<char>, qint64, bool)),
Qt::QueuedConnection);
QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
@@ -907,7 +914,8 @@ void QNetworkReplyHttpImplPrivate::postRequest()
delegate->incomingReasonPhrase,
delegate->isPipeliningUsed,
QSharedPointer<char>(),
- delegate->incomingContentLength);
+ delegate->incomingContentLength,
+ delegate->isSpdyUsed);
replyDownloadData(delegate->synchronousDownloadData);
httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
} else {
@@ -917,7 +925,8 @@ void QNetworkReplyHttpImplPrivate::postRequest()
delegate->incomingReasonPhrase,
delegate->isPipeliningUsed,
QSharedPointer<char>(),
- delegate->incomingContentLength);
+ delegate->incomingContentLength,
+ delegate->isSpdyUsed);
replyDownloadData(delegate->synchronousDownloadData);
}
@@ -1074,7 +1083,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData
(QList<QPair<QByteArray,QByteArray> > hm,
int sc,QString rp,bool pu,
QSharedPointer<char> db,
- qint64 contentLength)
+ qint64 contentLength, bool spdyWasUsed)
{
Q_Q(QNetworkReplyHttpImpl);
Q_UNUSED(contentLength);
@@ -1091,6 +1100,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData
}
q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
+ q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, spdyWasUsed);
// reconstruct the HTTP header
QList<QPair<QByteArray, QByteArray> > headerMap = hm;
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 15cc0ec476..aa2d6f0ec9 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -111,7 +111,9 @@ public:
// From reply
Q_PRIVATE_SLOT(d_func(), void replyDownloadData(QByteArray))
Q_PRIVATE_SLOT(d_func(), void replyFinished())
- Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64))
+ Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
+ int, QString, bool, QSharedPointer<char>,
+ qint64, bool))
Q_PRIVATE_SLOT(d_func(), void replyDownloadProgressSlot(qint64,qint64))
Q_PRIVATE_SLOT(d_func(), void httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *))
Q_PRIVATE_SLOT(d_func(), void httpError(QNetworkReply::NetworkError, const QString &))
@@ -276,7 +278,8 @@ public:
// From HTTP thread:
void replyDownloadData(QByteArray);
void replyFinished();
- void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >, int, QString, bool,
+ QSharedPointer<char>, qint64, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
diff --git a/src/network/access/qnetworkreplynsurlconnectionimpl.mm b/src/network/access/qnetworkreplynsurlconnectionimpl.mm
index 293a505912..d49324918e 100644
--- a/src/network/access/qnetworkreplynsurlconnectionimpl.mm
+++ b/src/network/access/qnetworkreplynsurlconnectionimpl.mm
@@ -128,6 +128,7 @@ QNetworkReplyNSURLConnectionImplPrivate::QNetworkReplyNSURLConnectionImplPrivate
QNetworkReplyNSURLConnectionImplPrivate::~QNetworkReplyNSURLConnectionImplPrivate()
{
+ [urlConnection cancel];
[urlConnection release];
[urlConnectionDelegate release];
if (readStream)
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 65fd693771..850f5bc7e7 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -246,6 +246,17 @@ QT_BEGIN_NAMESPACE
The QNetworkSession ConnectInBackground property will be set according to
this attribute.
+ \value SpdyAllowedAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates whether the QNetworkAccessManager code is
+ allowed to use SPDY with this request. This applies only
+ to SSL requests, and depends on the server supporting SPDY.
+
+ \value SpdyWasUsedAttribute
+ Replies only, type: QMetaType::Bool
+ Indicates whether SPDY was used for receiving
+ this reply.
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -501,7 +512,7 @@ QList<QByteArray> QNetworkRequest::rawHeaderList() const
will also set the known header LastModifiedHeader to be the
QDateTime object of the parsed date.
- Note: setting the same header twice overrides the previous
+ \note Setting the same header twice overrides the previous
setting. To accomplish the behaviour of multiple HTTP headers of
the same name, you should concatenate the two values, separating
them with a comma (",") and set one single raw header.
@@ -517,7 +528,7 @@ void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArra
Returns the attribute associated with the code \a code. If the
attribute has not been set, it returns \a defaultValue.
- Note: this function does not apply the defaults listed in
+ \note This function does not apply the defaults listed in
QNetworkRequest::Attribute.
\sa setAttribute(), QNetworkRequest::Attribute
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 1512c6dadd..27b02a89ef 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -86,6 +86,8 @@ public:
DownloadBufferAttribute, // internal
SynchronousRequestAttribute, // internal
BackgroundRequestAttribute,
+ SpdyAllowedAttribute,
+ SpdyWasUsedAttribute,
User = 1000,
UserMax = 32767
diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp
new file mode 100644
index 0000000000..6d22ebeb35
--- /dev/null
+++ b/src/network/access/qspdyprotocolhandler.cpp
@@ -0,0 +1,1331 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qspdyprotocolhandler_p.h>
+#include <private/qnoncontiguousbytedevice_p.h>
+#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <QtCore/QtEndian>
+
+#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+
+QT_BEGIN_NAMESPACE
+
+static const char spdyDictionary[] = {
+ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti
+ 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h
+ 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p
+ 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de
+ 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete....
+ 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace...
+ 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept.
+ 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
+ 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse
+ 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc
+ 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding....
+ 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage.
+ 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
+ 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges
+ 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age.
+ 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow
+ 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth
+ 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio
+ 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac
+ 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr
+ 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection
+ 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base
+ 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding....
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content-
+ 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language
+ 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng
+ 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation..
+ 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
+ 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5...
+ 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content
+ 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range..
+ 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
+ 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type..
+ 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date..
+ 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag..
+ 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect
+ 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi
+ 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f
+ 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i
+ 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match.
+ 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo
+ 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s
+ 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince....
+ 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none-
+ 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match...
+ 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang
+ 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if-
+ 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi
+ 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since
+ 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last
+ 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie
+ 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation...
+ 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for
+ 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards...
+ 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma.
+ 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy
+ 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate...
+ 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza
+ 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion....
+ 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range...
+ 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer
+ 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr
+ 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after.
+ 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve
+ 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te.
+ 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail
+ 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr
+ 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e
+ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding.
+ 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra
+ 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us
+ 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent
+ 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary
+ 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via.
+ 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni
+ 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww
+ 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen
+ 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate..
+ 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method
+ 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get.
+ 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu
+ 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200
+ 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion..
+ 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1
+ 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur
+ 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub
+ 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s
+ 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki
+ 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee
+ 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive.
+ 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi
+ 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012
+ 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205
+ 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030
+ 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043
+ 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307
+ 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540
+ 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084
+ 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411
+ 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341
+ 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164
+ 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504
+ 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N
+ 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho
+ 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative
+ 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa
+ 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204.
+ 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte
+ 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo
+ 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm
+ 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4
+ 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40
+ 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth
+ 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40
+ 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid
+ 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N
+ 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found
+ 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte
+ 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser
+ 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro
+ 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not
+ 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme
+ 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503.
+ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service.
+ 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila
+ 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F
+ 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A
+ 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J
+ 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A
+ 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept.
+ 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov.
+ 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0
+ 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon
+ 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W
+ 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu.
+ 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa
+ 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun..
+ 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk
+ 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text.
+ 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima
+ 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i
+ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg
+ 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g
+ 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
+ 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
+ 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml
+ 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl
+ 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text
+ 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr
+ 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ
+ 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat
+ 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age
+ 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de
+ 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd
+ 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse
+ 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c
+ 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i
+ 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859-
+ 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-..
+ 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0.
+};
+
+// uncomment to debug
+//static void printHex(const QByteArray &ba)
+//{
+// QByteArray hex;
+// QByteArray clearText;
+// for (int a = 0; a < ba.count(); ++a) {
+// QByteArray currentHexChar = QByteArray(1, ba.at(a)).toHex().rightJustified(2, ' ');
+// QByteArray currentChar;
+// if (ba.at(a) >= 32 && ba.at(a) < 126) { // if ASCII, print the letter
+// currentChar = QByteArray(1, ba.at(a));
+// } else {
+// currentChar = " ";
+// }
+// clearText.append(currentChar.rightJustified(2, ' '));
+// hex.append(currentHexChar);
+// hex.append(' ');
+// clearText.append(' ');
+// }
+// int chunkSize = 102; // 12 == 4 bytes per line
+// for (int a = 0; a < hex.count(); a += chunkSize) {
+// qDebug() << hex.mid(a, chunkSize);
+// qDebug() << clearText.mid(a, chunkSize);
+// }
+//}
+
+QSpdyProtocolHandler::QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : QObject(0), QAbstractProtocolHandler(channel),
+ m_nextStreamID(-1),
+ m_maxConcurrentStreams(100), // 100 is recommended in the SPDY RFC
+ m_initialWindowSize(0),
+ m_waitingForCompleteStream(false)
+{
+ m_inflateStream.zalloc = Z_NULL;
+ m_inflateStream.zfree = Z_NULL;
+ m_inflateStream.opaque = Z_NULL;
+ int zlibRet = inflateInit(&m_inflateStream);
+ Q_ASSERT(zlibRet == Z_OK);
+
+ m_deflateStream.zalloc = Z_NULL;
+ m_deflateStream.zfree = Z_NULL;
+ m_deflateStream.opaque = Z_NULL;
+
+ // Do actually not compress (i.e. compression level = 0)
+ // when sending the headers because of the CRIME attack
+ zlibRet = deflateInit(&m_deflateStream, /* compression level = */ 0);
+ Q_ASSERT(zlibRet == Z_OK);
+ Q_UNUSED(zlibRet); // silence -Wunused-variable
+}
+
+QSpdyProtocolHandler::~QSpdyProtocolHandler()
+{
+ deflateEnd(&m_deflateStream);
+ deflateEnd(&m_inflateStream);
+}
+
+bool QSpdyProtocolHandler::sendRequest()
+{
+ Q_ASSERT(!m_reply);
+
+ int maxPossibleRequests = m_maxConcurrentStreams - m_inFlightStreams.count();
+ Q_ASSERT(maxPossibleRequests >= 0);
+ if (maxPossibleRequests == 0)
+ return true; // return early if max concurrent requests are exceeded
+
+ m_channel->state = QHttpNetworkConnectionChannel::WritingState;
+
+ // requests will be ordered by priority (see QMultiMap doc)
+ QList<HttpMessagePair> requests = m_channel->spdyRequestsToSend.values();
+ QList<int> priorities = m_channel->spdyRequestsToSend.keys();
+
+ int requestsToSend = qMin(requests.count(), maxPossibleRequests);
+
+ for (int a = 0; a < requestsToSend; ++a) {
+ HttpMessagePair currentPair = requests.at(a);
+ QHttpNetworkRequest currentRequest = requests.at(a).first;
+ QHttpNetworkReply *currentReply = requests.at(a).second;
+
+ currentReply->setSpdyWasUsed(true);
+ qint32 streamID = generateNextStreamID();
+ currentReply->setProperty("SPDYStreamID", streamID);
+
+ currentReply->setRequest(currentRequest);
+ currentReply->d_func()->connection = m_connection;
+ currentReply->d_func()->connectionChannel = m_channel;
+ m_inFlightStreams.insert(streamID, currentPair);
+ connect(currentReply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*)));
+
+ sendSYN_STREAM(currentPair, streamID, /* associatedToStreamID = */ 0);
+ int requestsRemoved = m_channel->spdyRequestsToSend.remove(
+ priorities.at(a), currentPair);
+ Q_ASSERT(requestsRemoved == 1);
+ Q_UNUSED(requestsRemoved); // silence -Wunused-variable
+ }
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ return true;
+}
+
+void QSpdyProtocolHandler::_q_replyDestroyed(QObject* reply)
+{
+ qint32 streamID = reply->property("SPDYStreamID").toInt();
+ if (m_inFlightStreams.remove(streamID))
+ sendRST_STREAM(streamID, RST_STREAM_CANCEL);
+}
+
+void QSpdyProtocolHandler::_q_receiveReply()
+{
+ Q_ASSERT(m_socket);
+
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (!qobject_cast<QHttpNetworkConnection*>(m_connection)) {
+ return;
+ }
+
+ if (bytesAvailable() < 8)
+ return; // cannot read frame headers, wait for more data
+
+ char frameHeadersRaw[8];
+ if (!readNextChunk(8, frameHeadersRaw))
+ return; // this should not happen, we just checked
+
+ const QByteArray frameHeaders(frameHeadersRaw, 8); // ### try without memcpy
+ if (frameHeadersRaw[0] & 0x80) {
+ handleControlFrame(frameHeaders);
+ } else {
+ handleDataFrame(frameHeaders);
+ }
+
+ // after handling the current frame, check whether there is more data waiting
+ if (m_socket->bytesAvailable() > 0)
+ QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
+}
+
+void QSpdyProtocolHandler::_q_readyRead()
+{
+ _q_receiveReply();
+}
+
+static qint16 twoBytesToInt(const char *bytes)
+{
+ return qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(bytes));
+}
+
+static qint32 threeBytesToInt(const char *bytes)
+{
+ return qFromBigEndian<qint32>(reinterpret_cast<const uchar *>(bytes)) >> 8;
+}
+
+static qint32 fourBytesToInt(const char *bytes)
+{
+ return qFromBigEndian<qint32>(reinterpret_cast<const uchar *>(bytes));
+}
+
+static void appendIntToThreeBytes(char *output, qint32 number)
+{
+ qToBigEndian<qint16>(number, reinterpret_cast<uchar *>(output + 1));
+ qToBigEndian<qint8>(number >> 16, reinterpret_cast<uchar *>(output));
+}
+
+static void appendIntToFourBytes(char *output, qint32 number)
+{
+ qToBigEndian<qint32>(number, reinterpret_cast<uchar *>(output));
+}
+
+static QByteArray intToFourBytes(qint32 number) // ### try to use appendIntToFourBytes where possible
+{
+ uchar data[4];
+ qToBigEndian<qint32>(number, data);
+ QByteArray ret(reinterpret_cast<char *>(data), 4);
+ return ret;
+}
+
+static QByteArray intToThreeBytes(qint32 number)
+{
+ uchar data[4];
+ qToBigEndian<qint32>(number << 8, data);
+ QByteArray ret(reinterpret_cast<char *>(data), 3);
+ return ret;
+}
+
+static qint32 getStreamID(const char *bytes)
+{
+ // eliminate most significant bit; it might be 0 or 1 depending on whether
+ // we are dealing with a control or data frame
+ return fourBytesToInt(bytes) & 0x3fffffff;
+}
+
+static QByteArray headerField(const QByteArray &name, const QByteArray &value)
+{
+ QByteArray ret;
+ ret.reserve(name.count() + value.count() + 8); // 4 byte for length each
+ ret.append(intToFourBytes(name.count()));
+ ret.append(name);
+ ret.append(intToFourBytes(value.count()));
+ ret.append(value);
+ return ret;
+}
+
+bool QSpdyProtocolHandler::uncompressHeader(const QByteArray &input, QByteArray *output)
+{
+ const size_t chunkSize = 1024;
+ char outputRaw[chunkSize];
+ // input bytes will not be changed by zlib, so it is safe to const_cast here
+ m_inflateStream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(input.constData()));
+ m_inflateStream.avail_in = input.count();
+ m_inflateStream.total_in = input.count();
+ int zlibRet;
+
+ do {
+ m_inflateStream.next_out = reinterpret_cast<Bytef *>(outputRaw);
+ m_inflateStream.avail_out = chunkSize;
+ zlibRet = inflate(&m_inflateStream, Z_SYNC_FLUSH);
+ if (zlibRet == Z_NEED_DICT) {
+ zlibRet = inflateSetDictionary(&m_inflateStream,
+ reinterpret_cast<const Bytef*>(spdyDictionary),
+ /* dictionaryLength = */ 1423);
+ Q_ASSERT(zlibRet == Z_OK);
+ continue;
+ }
+ switch (zlibRet) {
+ case Z_BUF_ERROR: {
+ if (m_inflateStream.avail_in == 0) {
+ int outputSize = chunkSize - m_inflateStream.avail_out;
+ output->append(outputRaw, outputSize);
+ m_inflateStream.avail_out = chunkSize;
+ }
+ break;
+ }
+ case Z_OK: {
+ int outputSize = chunkSize - m_inflateStream.avail_out;
+ output->append(outputRaw, outputSize);
+ break;
+ }
+ default: {
+ qWarning() << Q_FUNC_INFO << "got unexpected zlib return value:" << zlibRet;
+ return false;
+ }
+ }
+ } while (m_inflateStream.avail_in > 0 && zlibRet != Z_STREAM_END);
+
+ Q_ASSERT(m_inflateStream.avail_in == 0);
+ return true;
+}
+
+QByteArray QSpdyProtocolHandler::composeHeader(const QHttpNetworkRequest &request)
+{
+ QByteArray uncompressedHeader;
+ uncompressedHeader.reserve(300); // rough estimate
+
+ // calculate additional headers first, because we need to know the size
+ // ### do not partially copy the list, but restrict the set header fields
+ // in QHttpNetworkConnection
+ QList<QPair<QByteArray, QByteArray> > additionalHeaders;
+ for (int a = 0; a < request.header().count(); ++a) {
+ QByteArray key = request.header().at(a).first;
+ if (key == "Connection" || key == "Host" || key == "Keep-Alive"
+ || key == "Proxy-Connection" || key == "Transfer-Encoding")
+ continue; // those headers are not valid (section 3.2.1)
+ additionalHeaders.append(request.header().at(a));
+ }
+
+ qint32 numberOfHeaderPairs = 5 + additionalHeaders.count(); // 5 mandatory below + the additional ones
+ uncompressedHeader.append(intToFourBytes(numberOfHeaderPairs));
+
+ // mandatory header fields:
+
+ uncompressedHeader.append(headerField(":method", request.methodName()));
+#ifndef QT_NO_NETWORKPROXY
+ bool useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
+ uncompressedHeader.append(headerField(":path", request.uri(useProxy)));
+#else
+ uncompressedHeader.append(headerField(":path", request.uri(false)));
+#endif
+ uncompressedHeader.append(headerField(":version", "HTTP/1.1"));
+
+ QHostAddress add; // ### unify with the host parsing from QHttpNetworkConnection
+ QByteArray host;
+ QString hostName = m_connection->hostName();
+ if (add.setAddress(hostName)) {
+ if (add.protocol() == QAbstractSocket::IPv6Protocol)
+ host = "[" + hostName.toLatin1() + "]"; //format the ipv6 in the standard way
+ else
+ host = hostName.toLatin1();
+
+ } else {
+ host = QUrl::toAce(hostName);
+ }
+
+ int port = request.url().port();
+ if (port != -1) {
+ host += ':';
+ host += QByteArray::number(port);
+ }
+ uncompressedHeader.append(headerField(":host", host));
+
+ uncompressedHeader.append(headerField(":scheme", request.url().scheme().toLatin1()));
+
+ // end of mandatory header fields
+
+ // now add the additional headers
+ for (int a = 0; a < additionalHeaders.count(); ++a) {
+ uncompressedHeader.append(headerField(additionalHeaders.at(a).first.toLower(),
+ additionalHeaders.at(a).second));
+ }
+
+ m_deflateStream.total_in = uncompressedHeader.count();
+ m_deflateStream.avail_in = uncompressedHeader.count();
+ m_deflateStream.next_in = reinterpret_cast<unsigned char *>(uncompressedHeader.data());
+ int outputBytes = uncompressedHeader.count() + 30; // 30 bytes of compression header overhead
+ m_deflateStream.avail_out = outputBytes;
+ unsigned char *out = new unsigned char[outputBytes];
+ m_deflateStream.next_out = out;
+ int availOutBefore = m_deflateStream.avail_out;
+ int zlibRet = deflate(&m_deflateStream, Z_SYNC_FLUSH); // do everything in one go since we use no compression
+ int compressedHeaderSize = availOutBefore - m_deflateStream.avail_out;
+ Q_ASSERT(zlibRet == Z_OK); // otherwise, we need to allocate more outputBytes
+ Q_UNUSED(zlibRet); // silence -Wunused-variable
+ Q_ASSERT(m_deflateStream.avail_in == 0);
+ QByteArray compressedHeader(reinterpret_cast<char *>(out), compressedHeaderSize);
+ delete[] out;
+
+ return compressedHeader;
+}
+
+quint64 QSpdyProtocolHandler::bytesAvailable() const
+{
+ Q_ASSERT(m_socket);
+ return m_spdyBuffer.byteAmount() + m_socket->bytesAvailable();
+}
+
+bool QSpdyProtocolHandler::readNextChunk(qint64 length, char *sink)
+{
+ qint64 expectedReadBytes = length;
+ qint64 requiredBytesFromBuffer = 0;
+
+ if (m_waitingForCompleteStream) {
+ requiredBytesFromBuffer = qMin(length, m_spdyBuffer.byteAmount());
+ // ### if next chunk from buffer bigger than what we want to read,
+ // we have to call read() (which memcpy's). Otherwise, we can just
+ // read the next chunk without memcpy'ing.
+ qint64 bytesReadFromBuffer = m_spdyBuffer.read(sink, requiredBytesFromBuffer);
+ Q_ASSERT(bytesReadFromBuffer == requiredBytesFromBuffer);
+ if (length <= bytesReadFromBuffer) {
+ return true; // buffer > required size -> no need to read from socket
+ }
+ expectedReadBytes -= requiredBytesFromBuffer;
+ }
+ qint64 readBytes = m_socket->read(sink + requiredBytesFromBuffer, expectedReadBytes);
+
+ if (readBytes < expectedReadBytes) {
+ m_waitingForCompleteStream = true;
+ // ### this is inefficient, we should not put back so much data into the buffer
+ QByteArray temp(sink, requiredBytesFromBuffer + readBytes);
+ m_spdyBuffer.append(temp);
+ return false;
+ } else {
+ return true; // buffer must be cleared by calling function
+ }
+}
+
+void QSpdyProtocolHandler::sendControlFrame(FrameType type,
+ ControlFrameFlags flags,
+ const char *data,
+ quint32 length)
+{
+ // frame type and stream ID
+ char header[8];
+ header[0] = 0x80; // leftmost bit == 1 -> is a control frame
+ header[1] = 0x03; // 3 bit == version 3
+ header[2] = 0;
+ switch (type) {
+ case FrameType_CREDENTIAL: {
+ qWarning("sending SPDY CREDENTIAL frame is not yet implemented"); // QTBUG-36188
+ return;
+ }
+ default:
+ header[3] = type;
+ }
+
+ // flags
+ header[4] = 0;
+ if (flags & ControlFrame_FLAG_FIN || length == 0) {
+ Q_ASSERT(type == FrameType_SYN_STREAM || type == FrameType_SYN_REPLY
+ || type == FrameType_HEADERS || length == 0);
+ header[4] |= ControlFrame_FLAG_FIN;
+ }
+ if (flags & ControlFrame_FLAG_UNIDIRECTIONAL) {
+ Q_ASSERT(type == FrameType_SYN_STREAM);
+ header[4] |= ControlFrame_FLAG_UNIDIRECTIONAL;
+ }
+
+ // length
+ appendIntToThreeBytes(header + 5, length);
+
+ qint64 written = m_socket->write(header, 8);
+ Q_ASSERT(written == 8);
+ written = m_socket->write(data, length);
+ Q_ASSERT(written == length);
+ Q_UNUSED(written); // silence -Wunused-variable
+}
+
+void QSpdyProtocolHandler::sendSYN_STREAM(HttpMessagePair messagePair,
+ qint32 streamID, qint32 associatedToStreamID)
+{
+ QHttpNetworkRequest request = messagePair.first;
+ QHttpNetworkReply *reply = messagePair.second;
+
+ ControlFrameFlags flags = 0;
+
+ if (!request.uploadByteDevice()) {
+ // no upload -> this is the last frame, send the FIN flag
+ flags |= ControlFrame_FLAG_FIN;
+ reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYHalfClosed;
+ } else {
+ reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYUploading;
+
+ // hack: set the stream ID on the device directly, so when we get
+ // the signal for uploading we know which stream we are sending on
+ request.uploadByteDevice()->setProperty("SPDYStreamID", streamID);
+
+ QObject::connect(request.uploadByteDevice(), SIGNAL(readyRead()), this,
+ SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
+ }
+
+ QByteArray namesAndValues = composeHeader(request);
+ quint32 length = namesAndValues.count() + 10; // 10 == 4 for Stream-ID + 4 for Associated-To-Stream-ID
+ // + 2 for Priority, Unused and Slot
+
+ QByteArray wireData;
+ wireData.reserve(length);
+ wireData.append(intToFourBytes(streamID));
+ wireData.append(intToFourBytes(associatedToStreamID));
+
+ // priority (3 bits) / unused (5 bits) / slot (8 bits)
+ char prioAndSlot[2];
+ switch (request.priority()) {
+ case QHttpNetworkRequest::HighPriority:
+ prioAndSlot[0] = 0x00; // == prio 0 (highest)
+ break;
+ case QHttpNetworkRequest::NormalPriority:
+ prioAndSlot[0] = 0x80; // == prio 4
+ break;
+ case QHttpNetworkRequest::LowPriority:
+ prioAndSlot[0] = 0xe0; // == prio 7 (lowest)
+ break;
+ }
+ prioAndSlot[1] = 0x00; // slot in client certificates (not supported currently)
+ wireData.append(prioAndSlot, 2);
+
+ wireData.append(namesAndValues);
+
+ sendControlFrame(FrameType_SYN_STREAM, flags, wireData.constData(), length);
+
+ if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYUploading)
+ uploadData(streamID);
+}
+
+void QSpdyProtocolHandler::sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode)
+{
+ char wireData[8];
+ appendIntToFourBytes(wireData, streamID);
+ appendIntToFourBytes(wireData + 4, statusCode);
+ sendControlFrame(FrameType_RST_STREAM, /* flags = */ 0, wireData, /* length = */ 8);
+}
+
+void QSpdyProtocolHandler::sendPING(quint32 pingID)
+{
+ char rawData[4];
+ appendIntToFourBytes(rawData, pingID);
+ sendControlFrame(FrameType_PING, /* flags = */ 0, rawData, /* length = */ 4);
+}
+
+bool QSpdyProtocolHandler::uploadData(qint32 streamID)
+{
+ // we only rely on SPDY flow control here and don't care about TCP buffers
+ if (!m_inFlightStreams.contains(streamID)) {
+ sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
+ return false;
+ }
+
+ HttpMessagePair messagePair = m_inFlightStreams.value(streamID);
+ QHttpNetworkRequest request = messagePair.first;
+ QHttpNetworkReply *reply = messagePair.second;
+ Q_ASSERT(reply);
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ Q_ASSERT(replyPrivate);
+
+ if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) {
+ qWarning() << Q_FUNC_INFO << "Trying to upload to closed stream";
+ return false;
+ }
+
+ qint32 dataLeftInWindow = replyPrivate->windowSizeUpload
+ - replyPrivate->currentlyUploadedDataInWindow;
+
+ while (dataLeftInWindow > 0 && !request.uploadByteDevice()->atEnd()) {
+
+ // get pointer to upload data
+ qint64 currentReadSize = 0;
+ const char *readPointer = request.uploadByteDevice()->readPointer(dataLeftInWindow,
+ currentReadSize);
+
+ if (currentReadSize == -1) {
+ // premature eof happened
+ m_connection->d_func()->emitReplyError(m_socket, reply,
+ QNetworkReply::UnknownNetworkError);
+ return false;
+ } else if (readPointer == 0 || currentReadSize == 0) {
+ // nothing to read currently, break the loop
+ break;
+ } else {
+ DataFrameFlags flags = 0;
+ // we will send the FIN flag later if appropriate
+ qint64 currentWriteSize = sendDataFrame(streamID, flags, currentReadSize, readPointer);
+ if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
+ // socket broke down
+ m_connection->d_func()->emitReplyError(m_socket, reply,
+ QNetworkReply::UnknownNetworkError);
+ return false;
+ } else {
+ replyPrivate->currentlyUploadedDataInWindow += currentWriteSize;
+ replyPrivate->totallyUploadedData += currentWriteSize;
+ dataLeftInWindow = replyPrivate->windowSizeUpload
+ - replyPrivate->currentlyUploadedDataInWindow;
+ request.uploadByteDevice()->advanceReadPointer(currentWriteSize);
+
+ emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
+ request.contentLength());
+ }
+ }
+ }
+ if (replyPrivate->totallyUploadedData == request.contentLength()) {
+ DataFrameFlags finFlag = DataFrame_FLAG_FIN;
+ qint64 writeSize = sendDataFrame(streamID, finFlag, 0, 0);
+ Q_ASSERT(writeSize == 0);
+ Q_UNUSED(writeSize); // silence -Wunused-variable
+ replyPrivate->state = QHttpNetworkReplyPrivate::SPDYHalfClosed;
+ if (reply->request().uploadByteDevice())
+ reply->request().uploadByteDevice()->disconnect(this);
+ // ### this will not work if the content length is not known, but
+ // then again many servers will fail in this case anyhow according
+ // to the SPDY RFC
+ }
+ return true;
+}
+
+void QSpdyProtocolHandler::_q_uploadDataReadyRead()
+{
+ QNonContiguousByteDevice *device = qobject_cast<QNonContiguousByteDevice *>(sender());
+ Q_ASSERT(device);
+ qint32 streamID = device->property("SPDYStreamID").toInt();
+ Q_ASSERT(streamID > 0);
+ uploadData(streamID);
+}
+
+void QSpdyProtocolHandler::sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize)
+{
+ char windowUpdateData[8];
+ appendIntToFourBytes(windowUpdateData, streamID);
+ appendIntToFourBytes(windowUpdateData + 4, deltaWindowSize);
+
+ sendControlFrame(FrameType_WINDOW_UPDATE, /* flags = */ 0, windowUpdateData, /* length = */ 8);
+}
+
+qint64 QSpdyProtocolHandler::sendDataFrame(qint32 streamID, DataFrameFlags flags,
+ quint32 length, const char *data)
+{
+ QByteArray wireData;
+ wireData.reserve(8);
+
+ wireData.append(intToFourBytes(streamID));
+ wireData.append(flags);
+ wireData.append(intToThreeBytes(length));
+
+ Q_ASSERT(m_socket);
+ m_socket->write(wireData);
+
+ if (data) {
+ qint64 ret = m_socket->write(data, length);
+ return ret;
+ } else {
+ return 0; // nothing to write, e.g. FIN flag
+ }
+}
+
+void QSpdyProtocolHandler::handleControlFrame(const QByteArray &frameHeaders) // ### make it char *
+{
+ Q_ASSERT(frameHeaders.count() >= 8);
+ qint16 version = twoBytesToInt(frameHeaders.constData());
+ version &= 0x3fff; // eliminate most significant bit to determine version
+ Q_ASSERT(version == 3);
+
+ qint16 type = twoBytesToInt(frameHeaders.constData() + 2);
+
+ char flags = frameHeaders.at(4);
+ qint32 length = threeBytesToInt(frameHeaders.constData() + 5);
+ Q_ASSERT(length > 0);
+
+ QByteArray frameData;
+ frameData.resize(length);
+ if (!readNextChunk(length, frameData.data())) {
+ // put back the frame headers to the buffer
+ m_spdyBuffer.prepend(frameHeaders);
+ return; // we couldn't read the whole frame and need to wait
+ } else {
+ m_spdyBuffer.clear();
+ m_waitingForCompleteStream = false;
+ }
+
+ switch (type) {
+ case FrameType_SYN_STREAM: {
+ handleSYN_STREAM(flags, length, frameData);
+ break;
+ }
+ case FrameType_SYN_REPLY: {
+ handleSYN_REPLY(flags, length, frameData);
+ break;
+ }
+ case FrameType_RST_STREAM: {
+ handleRST_STREAM(flags, length, frameData);
+ break;
+ }
+ case FrameType_SETTINGS: {
+ handleSETTINGS(flags, length, frameData);
+ break;
+ }
+ case FrameType_PING: {
+ handlePING(flags, length, frameData);
+ break;
+ }
+ case FrameType_GOAWAY: {
+ handleGOAWAY(flags, length, frameData);
+ break;
+ }
+ case FrameType_HEADERS: {
+ handleHEADERS(flags, length, frameData);
+ break;
+ }
+ case FrameType_WINDOW_UPDATE: {
+ handleWINDOW_UPDATE(flags, length, frameData);
+ break;
+ }
+ default:
+ qWarning() << Q_FUNC_INFO << "cannot handle frame of type" << type;
+ }
+}
+
+void QSpdyProtocolHandler::handleSYN_STREAM(char /*flags*/, quint32 /*length*/,
+ const QByteArray &frameData)
+{
+ // not implemented; will be implemented when servers start using it
+ // we just tell the server that we do not accept that
+
+ qint32 streamID = getStreamID(frameData.constData());
+
+ sendRST_STREAM(streamID, RST_STREAM_REFUSED_STREAM);
+}
+
+void QSpdyProtocolHandler::handleSYN_REPLY(char flags, quint32 /*length*/, const QByteArray &frameData)
+{
+ parseHttpHeaders(flags, frameData);
+}
+
+void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameData)
+{
+ qint32 streamID = getStreamID(frameData.constData());
+ if (!m_inFlightStreams.contains(streamID)) {
+ sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
+ return;
+ }
+
+ flags &= 0x3f;
+ bool flag_fin = flags & 0x01;
+
+ QByteArray headerValuePairs = frameData.mid(4);
+
+ HttpMessagePair pair = m_inFlightStreams.value(streamID);
+ QHttpNetworkReply *httpReply = pair.second;
+ Q_ASSERT(httpReply != 0);
+
+ if (httpReply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) {
+ sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED);
+ return;
+ }
+
+ QByteArray uncompressedHeader;
+ if (!uncompressHeader(headerValuePairs, &uncompressedHeader)) {
+ qWarning() << Q_FUNC_INFO << "error reading header from SYN_REPLY message";
+ return;
+ }
+
+ qint32 headerCount = fourBytesToInt(uncompressedHeader.constData());
+ if (headerCount * 8 > uncompressedHeader.size()) {
+ qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message";
+ sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
+ return;
+ }
+ qint32 readPointer = 4;
+ for (qint32 a = 0; a < headerCount; ++a) {
+ qint32 count = fourBytesToInt(uncompressedHeader.constData() + readPointer);
+ readPointer += 4;
+ QByteArray name = uncompressedHeader.mid(readPointer, count);
+ readPointer += count;
+ if (readPointer > uncompressedHeader.size()) {
+ qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message";
+ sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
+ return;
+ }
+ count = fourBytesToInt(uncompressedHeader.constData() + readPointer);
+ readPointer += 4;
+ QByteArray value = uncompressedHeader.mid(readPointer, count);
+ readPointer += count;
+ if (readPointer > uncompressedHeader.size()) {
+ qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message";
+ sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
+ return;
+ }
+ if (name == ":status") {
+ httpReply->setStatusCode(value.left(3).toInt());
+ httpReply->d_func()->reasonPhrase = QString::fromLatin1(value.mid(4));
+ } else if (name == ":version") {
+ int majorVersion = value.at(5) - 48;
+ int minorVersion = value.at(7) - 48;
+ httpReply->d_func()->majorVersion = majorVersion;
+ httpReply->d_func()->minorVersion = minorVersion;
+ } else if (name == "content-length") {
+ httpReply->setContentLength(value.toLongLong());
+ } else {
+ if (value.contains('\0')) {
+ QList<QByteArray> values = value.split('\0');
+ QByteArray binder(", ");
+ if (name == "set-cookie")
+ binder = "\n";
+ value.clear();
+ Q_FOREACH (const QByteArray& ivalue, values) {
+ if (value.isEmpty())
+ value = ivalue;
+ else
+ value += binder + ivalue;
+ }
+ }
+ httpReply->setHeaderField(name, value);
+ }
+ }
+ emit httpReply->headerChanged();
+
+ if (flag_fin) {
+ if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed)
+ sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0);
+ replyFinished(httpReply, streamID);
+ }
+}
+
+void QSpdyProtocolHandler::handleRST_STREAM(char /*flags*/, quint32 length,
+ const QByteArray &frameData)
+{
+ // flags are ignored
+
+ Q_ASSERT(length == 8);
+ Q_UNUSED(length); // silence -Wunused-parameter
+ qint32 streamID = getStreamID(frameData.constData());
+ QHttpNetworkReply *httpReply = m_inFlightStreams.value(streamID).second;
+
+ qint32 statusCodeInt = fourBytesToInt(frameData.constData() + 4);
+ RST_STREAM_STATUS_CODE statusCode = static_cast<RST_STREAM_STATUS_CODE>(statusCodeInt);
+ QNetworkReply::NetworkError errorCode;
+ QByteArray errorMessage;
+
+ switch (statusCode) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "SPDY protocol error";
+ break;
+ case RST_STREAM_INVALID_STREAM:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "SPDY stream is not active";
+ break;
+ case RST_STREAM_REFUSED_STREAM:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "SPDY stream was refused";
+ break;
+ case RST_STREAM_UNSUPPORTED_VERSION:
+ errorCode = QNetworkReply::ProtocolUnknownError;
+ errorMessage = "SPDY version is unknown to the server";
+ break;
+ case RST_STREAM_CANCEL:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "SPDY stream is no longer needed";
+ break;
+ case RST_STREAM_INTERNAL_ERROR:
+ errorCode = QNetworkReply::InternalServerError;
+ errorMessage = "Internal server error";
+ break;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "peer violated the flow control protocol";
+ break;
+ case RST_STREAM_STREAM_IN_USE:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "server received a SYN_REPLY for an already open stream";
+ break;
+ case RST_STREAM_STREAM_ALREADY_CLOSED:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "server received data or a SYN_REPLY for an already half-closed stream";
+ break;
+ case RST_STREAM_INVALID_CREDENTIALS:
+ errorCode = QNetworkReply::ContentAccessDenied;
+ errorMessage = "server received invalid credentials";
+ break;
+ case RST_STREAM_FRAME_TOO_LARGE:
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "server cannot process the frame because it is too large";
+ break;
+ default:
+ qWarning() << Q_FUNC_INFO << "could not understand servers RST_STREAM status code";
+ errorCode = QNetworkReply::ProtocolFailure;
+ errorMessage = "got SPDY RST_STREAM message with unknown error code";
+ }
+ if (httpReply)
+ replyFinishedWithError(httpReply, streamID, errorCode, errorMessage.constData());
+}
+
+void QSpdyProtocolHandler::handleSETTINGS(char flags, quint32 /*length*/, const QByteArray &frameData)
+{
+ Q_ASSERT(frameData.count() > 0);
+
+ SETTINGS_Flags settingsFlags = static_cast<SETTINGS_Flags>(flags);
+ if (settingsFlags & FLAG_SETTINGS_CLEAR_SETTINGS) {
+ // ### clear all persistent settings; since we do not persist settings
+ // as of now, we don't need to clear anything either
+ }
+
+ qint32 numberOfEntries = fourBytesToInt(frameData.constData());
+ Q_ASSERT(numberOfEntries > 0);
+ for (int a = 0, frameDataIndex = 4; a < numberOfEntries; ++a, frameDataIndex += 8) {
+ SETTINGS_ID_Flag idFlag = static_cast<SETTINGS_ID_Flag>(frameData[frameDataIndex]);
+ if (idFlag & FLAG_SETTINGS_PERSIST_VALUE) {
+ // ### we SHOULD persist the settings here according to the RFC, but we don't have to,
+ // so implement that later
+ } // the other value is only sent by us, but not received
+
+ quint32 uniqueID = static_cast<SETTINGS_ID>(
+ threeBytesToInt(frameData.constData() + frameDataIndex + 1));
+ quint32 value = fourBytesToInt(frameData.constData() + frameDataIndex + 4);
+ switch (uniqueID) {
+ case SETTINGS_UPLOAD_BANDWIDTH: {
+ // ignored for now, just an estimated informative value
+ break;
+ }
+ case SETTINGS_DOWNLOAD_BANDWIDTH: {
+ // ignored for now, just an estimated informative value
+ break;
+ }
+ case SETTINGS_ROUND_TRIP_TIME: {
+ // ignored for now, just an estimated informative value
+ break;
+ }
+ case SETTINGS_MAX_CONCURRENT_STREAMS: {
+ m_maxConcurrentStreams = value;
+ break;
+ }
+ case SETTINGS_CURRENT_CWND: {
+ // ignored for now, just an informative value
+ break;
+ }
+ case SETTINGS_DOWNLOAD_RETRANS_RATE: {
+ // ignored for now, just an estimated informative value
+ break;
+ }
+ case SETTINGS_INITIAL_WINDOW_SIZE: {
+ m_initialWindowSize = value;
+ break;
+ }
+ case SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: {
+ // client certificates are not supported
+ break;
+ }
+ default:
+ qWarning() << Q_FUNC_INFO << "found unknown settings value" << value;
+ }
+ }
+}
+
+void QSpdyProtocolHandler::handlePING(char /*flags*/, quint32 length, const QByteArray &frameData)
+{
+ // flags are ignored
+
+ Q_ASSERT(length == 4);
+ Q_UNUSED(length); // silence -Wunused-parameter
+ quint32 pingID = fourBytesToInt(frameData.constData());
+
+ // odd numbered IDs must be ignored
+ if ((pingID & 1) == 0) // is even?
+ sendPING(pingID);
+}
+
+void QSpdyProtocolHandler::handleGOAWAY(char /*flags*/, quint32 /*length*/,
+ const QByteArray &frameData)
+{
+ // flags are ignored
+
+ qint32 statusCode = static_cast<GOAWAY_STATUS>(fourBytesToInt(frameData.constData() + 4));
+ QNetworkReply::NetworkError errorCode;
+ switch (statusCode) {
+ case GOAWAY_OK: {
+ errorCode = QNetworkReply::NoError;
+ break;
+ }
+ case GOAWAY_PROTOCOL_ERROR: {
+ errorCode = QNetworkReply::ProtocolFailure;
+ break;
+ }
+ case GOAWAY_INTERNAL_ERROR: {
+ errorCode = QNetworkReply::InternalServerError;
+ break;
+ }
+ default:
+ qWarning() << Q_FUNC_INFO << "unexpected status code" << statusCode;
+ errorCode = QNetworkReply::ProtocolUnknownError;
+ }
+
+ qint32 lastGoodStreamID = getStreamID(frameData.constData());
+
+ // emit errors for all replies after the last good stream ID
+ Q_ASSERT(m_connection);
+ for (qint32 currentStreamID = lastGoodStreamID + 2; currentStreamID <= m_nextStreamID;
+ ++currentStreamID) {
+ QHttpNetworkReply *reply = m_inFlightStreams.value(currentStreamID).second;
+ Q_ASSERT(reply);
+ m_connection->d_func()->emitReplyError(m_socket, reply, errorCode);
+ }
+ // ### we could make sure a new session is initiated anyhow
+}
+
+void QSpdyProtocolHandler::handleHEADERS(char flags, quint32 /*length*/,
+ const QByteArray &frameData)
+{
+ parseHttpHeaders(flags, frameData);
+}
+
+void QSpdyProtocolHandler::handleWINDOW_UPDATE(char /*flags*/, quint32 /*length*/,
+ const QByteArray &frameData)
+{
+ qint32 streamID = getStreamID(frameData.constData());
+ qint32 deltaWindowSize = fourBytesToInt(frameData.constData() + 4);
+
+ if (!m_inFlightStreams.contains(streamID)) {
+ sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
+ return;
+ }
+
+ QHttpNetworkReply *reply = m_inFlightStreams.value(streamID).second;
+ Q_ASSERT(reply);
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ Q_ASSERT(replyPrivate);
+
+ // Ignore WINDOW_UPDATE if we are already done.
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed)
+ return;
+
+ replyPrivate->currentlyUploadedDataInWindow = replyPrivate->windowSizeUpload - deltaWindowSize;
+ uploadData(streamID); // we hopefully can continue to upload
+}
+
+
+void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders)
+{
+ Q_ASSERT(frameHeaders.count() >= 8);
+
+ qint32 streamID = getStreamID(frameHeaders.constData());
+ if (!m_inFlightStreams.contains(streamID)) {
+ sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
+ return;
+ }
+
+ unsigned char flags = static_cast<unsigned char>(frameHeaders.at(4));
+ flags &= 0x3f;
+ bool flag_fin = flags & 0x01;
+ bool flag_compress = flags & 0x02;
+ qint32 length = threeBytesToInt(frameHeaders.constData() + 5);
+
+ QByteArray data;
+ data.resize(length);
+ if (!readNextChunk(length, data.data())) {
+ // put back the frame headers to the buffer
+ m_spdyBuffer.prepend(frameHeaders);
+ return; // we couldn't read the whole frame and need to wait
+ } else {
+ m_spdyBuffer.clear();
+ m_waitingForCompleteStream = false;
+ }
+
+ HttpMessagePair pair = m_inFlightStreams.value(streamID);
+ QHttpNetworkRequest httpRequest = pair.first;
+ QHttpNetworkReply *httpReply = pair.second;
+ Q_ASSERT(httpReply != 0);
+
+ QHttpNetworkReplyPrivate *replyPrivate = httpReply->d_func();
+
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) {
+ sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED);
+ return;
+ }
+
+ // check whether we need to send WINDOW_UPDATE (i.e. tell the sender it can send more)
+ replyPrivate->currentlyReceivedDataInWindow += length;
+ qint32 dataLeftInWindow = replyPrivate->windowSizeDownload - replyPrivate->currentlyReceivedDataInWindow;
+
+ if (replyPrivate->currentlyReceivedDataInWindow > 0
+ && dataLeftInWindow < replyPrivate->windowSizeDownload / 2) {
+
+ // socket read buffer size is 64K actually, hard coded in the channel
+ // We can read way more than 64K per socket, because the window size
+ // here is per stream.
+ if (replyPrivate->windowSizeDownload >= m_socket->readBufferSize()) {
+ replyPrivate->windowSizeDownload = m_socket->readBufferSize();
+ } else {
+ replyPrivate->windowSizeDownload *= 1.5;
+ }
+ QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
+ Q_ARG(qint32, streamID),
+ Q_ARG(quint32, replyPrivate->windowSizeDownload));
+ // setting the current data count to 0 is a race condition,
+ // because we call sendWINDOW_UPDATE through the event loop.
+ // But then again, the whole situation is a race condition because
+ // we don't know when the packet will arrive at the server; so
+ // this is most likely good enough here.
+ replyPrivate->currentlyReceivedDataInWindow = 0;
+ }
+
+ httpReply->d_func()->compressedData.append(data);
+
+
+ replyPrivate->totalProgress += length;
+
+ if (httpRequest.d->autoDecompress && httpReply->d_func()->isCompressed()) {
+ QByteDataBuffer inDataBuffer; // ### should we introduce one in the http reply?
+ inDataBuffer.append(data);
+ qint64 compressedCount = httpReply->d_func()->uncompressBodyData(&inDataBuffer,
+ &replyPrivate->responseData);
+ Q_ASSERT(compressedCount >= 0);
+ Q_UNUSED(compressedCount); // silence -Wunused-variable
+ } else {
+ replyPrivate->responseData.append(data);
+ }
+
+ if (replyPrivate->shouldEmitSignals()) {
+ emit httpReply->readyRead();
+ emit httpReply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+
+ if (flag_compress) {
+ qWarning() << Q_FUNC_INFO << "SPDY level compression is not supported";
+ }
+
+ if (flag_fin) {
+ if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed)
+ sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0);
+ replyFinished(httpReply, streamID);
+ }
+}
+
+void QSpdyProtocolHandler::replyFinished(QHttpNetworkReply *httpReply, qint32 streamID)
+{
+ httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed;
+ httpReply->disconnect(this);
+ if (httpReply->request().uploadByteDevice())
+ httpReply->request().uploadByteDevice()->disconnect(this);
+ int streamsRemoved = m_inFlightStreams.remove(streamID);
+ Q_ASSERT(streamsRemoved == 1);
+ Q_UNUSED(streamsRemoved); // silence -Wunused-variable
+ emit httpReply->finished();
+}
+
+void QSpdyProtocolHandler::replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID,
+ QNetworkReply::NetworkError errorCode, const char *errorMessage)
+{
+ Q_ASSERT(httpReply);
+ httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed;
+ httpReply->disconnect(this);
+ if (httpReply->request().uploadByteDevice())
+ httpReply->request().uploadByteDevice()->disconnect(this);
+ int streamsRemoved = m_inFlightStreams.remove(streamID);
+ Q_ASSERT(streamsRemoved == 1);
+ Q_UNUSED(streamsRemoved); // silence -Wunused-variable
+ emit httpReply->finishedWithError(errorCode, QSpdyProtocolHandler::tr(errorMessage));
+}
+
+qint32 QSpdyProtocolHandler::generateNextStreamID()
+{
+ // stream IDs initiated by the client must be odd
+ m_nextStreamID += 2;
+ return m_nextStreamID;
+}
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
diff --git a/src/network/access/qspdyprotocolhandler_p.h b/src/network/access/qspdyprotocolhandler_p.h
new file mode 100644
index 0000000000..50c5ed52a1
--- /dev/null
+++ b/src/network/access/qspdyprotocolhandler_p.h
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPDYPROTOCOLHANDLER_H
+#define QSPDYPROTOCOLHANDLER_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 <private/qabstractprotocolhandler_p.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <private/qbytedata_p.h>
+
+#include <zlib.h>
+
+#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkRequest;
+
+#ifndef HttpMessagePair
+typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
+#endif
+
+class QSpdyProtocolHandler : public QObject, public QAbstractProtocolHandler {
+ Q_OBJECT
+public:
+ QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel);
+ ~QSpdyProtocolHandler();
+
+ enum DataFrameFlag {
+ DataFrame_FLAG_FIN = 0x01,
+ DataFrame_FLAG_COMPRESS = 0x02
+ };
+
+ Q_DECLARE_FLAGS(DataFrameFlags, DataFrameFlag)
+
+ enum ControlFrameFlag {
+ ControlFrame_FLAG_FIN = 0x01,
+ ControlFrame_FLAG_UNIDIRECTIONAL = 0x02
+ };
+
+ Q_DECLARE_FLAGS(ControlFrameFlags, ControlFrameFlag)
+
+ enum SETTINGS_Flag {
+ FLAG_SETTINGS_CLEAR_SETTINGS = 0x01
+ };
+
+ Q_DECLARE_FLAGS(SETTINGS_Flags, SETTINGS_Flag)
+
+ enum SETTINGS_ID_Flag {
+ FLAG_SETTINGS_PERSIST_VALUE = 0x01,
+ FLAG_SETTINGS_PERSISTED = 0x02
+ };
+
+ Q_DECLARE_FLAGS(SETTINGS_ID_Flags, SETTINGS_ID_Flag)
+
+ virtual void _q_receiveReply() Q_DECL_OVERRIDE;
+ virtual void _q_readyRead() Q_DECL_OVERRIDE;
+ virtual bool sendRequest() Q_DECL_OVERRIDE;
+
+private slots:
+ void _q_uploadDataReadyRead();
+ void _q_replyDestroyed(QObject*);
+
+private:
+
+ enum FrameType {
+ FrameType_SYN_STREAM = 1,
+ FrameType_SYN_REPLY = 2,
+ FrameType_RST_STREAM = 3,
+ FrameType_SETTINGS = 4,
+ FrameType_PING = 6,
+ FrameType_GOAWAY = 7,
+ FrameType_HEADERS = 8,
+ FrameType_WINDOW_UPDATE = 9,
+ FrameType_CREDENTIAL // has a special type
+ };
+
+ enum StatusCode {
+ StatusCode_PROTOCOL_ERROR = 1,
+ StatusCode_INVALID_STREAM = 2,
+ StatusCode_REFUSED_STREAM = 3,
+ StatusCode_UNSUPPORTED_VERSION = 4,
+ StatusCode_CANCEL = 5,
+ StatusCode_INTERNAL_ERROR = 6,
+ StatusCode_FLOW_CONTROL_ERROR = 7,
+ StatusCode_STREAM_IN_USE = 8,
+ StatusCode_STREAM_ALREADY_CLOSED = 9,
+ StatusCode_INVALID_CREDENTIALS = 10,
+ StatusCode_FRAME_TOO_LARGE = 11
+ };
+
+ enum SETTINGS_ID {
+ SETTINGS_UPLOAD_BANDWIDTH = 1,
+ SETTINGS_DOWNLOAD_BANDWIDTH = 2,
+ SETTINGS_ROUND_TRIP_TIME = 3,
+ SETTINGS_MAX_CONCURRENT_STREAMS = 4,
+ SETTINGS_CURRENT_CWND = 5,
+ SETTINGS_DOWNLOAD_RETRANS_RATE = 6,
+ SETTINGS_INITIAL_WINDOW_SIZE = 7,
+ SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
+ };
+
+ enum GOAWAY_STATUS {
+ GOAWAY_OK = 0,
+ GOAWAY_PROTOCOL_ERROR = 1,
+ GOAWAY_INTERNAL_ERROR = 11
+ };
+
+ enum RST_STREAM_STATUS_CODE {
+ RST_STREAM_PROTOCOL_ERROR = 1,
+ RST_STREAM_INVALID_STREAM = 2,
+ RST_STREAM_REFUSED_STREAM = 3,
+ RST_STREAM_UNSUPPORTED_VERSION = 4,
+ RST_STREAM_CANCEL = 5,
+ RST_STREAM_INTERNAL_ERROR = 6,
+ RST_STREAM_FLOW_CONTROL_ERROR = 7,
+ RST_STREAM_STREAM_IN_USE = 8,
+ RST_STREAM_STREAM_ALREADY_CLOSED = 9,
+ RST_STREAM_INVALID_CREDENTIALS = 10,
+ RST_STREAM_FRAME_TOO_LARGE = 11
+ };
+
+ quint64 bytesAvailable() const;
+ bool readNextChunk(qint64 length, char *sink);
+
+ void sendControlFrame(FrameType type, ControlFrameFlags flags, const char *data, quint32 length);
+
+ void sendSYN_STREAM(HttpMessagePair pair, qint32 streamID,
+ qint32 associatedToStreamID);
+ void sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode);
+ void sendPING(quint32 pingID);
+
+ bool uploadData(qint32 streamID);
+ Q_INVOKABLE void sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize);
+
+ qint64 sendDataFrame(qint32 streamID, DataFrameFlags flags, quint32 length,
+ const char *data);
+
+ QByteArray composeHeader(const QHttpNetworkRequest &request);
+ bool uncompressHeader(const QByteArray &input, QByteArray *output);
+
+ void handleControlFrame(const QByteArray &frameHeaders);
+ void handleDataFrame(const QByteArray &frameHeaders);
+
+ void handleSYN_STREAM(char, quint32, const QByteArray &frameData);
+ void handleSYN_REPLY(char flags, quint32, const QByteArray &frameData);
+ void handleRST_STREAM(char flags, quint32 length, const QByteArray &frameData);
+ void handleSETTINGS(char flags, quint32 length, const QByteArray &frameData);
+ void handlePING(char, quint32 length, const QByteArray &frameData);
+ void handleGOAWAY(char flags, quint32, const QByteArray &frameData);
+ void handleHEADERS(char flags, quint32, const QByteArray &frameData);
+ void handleWINDOW_UPDATE(char, quint32, const QByteArray &frameData);
+
+ qint32 generateNextStreamID();
+ void parseHttpHeaders(char flags, const QByteArray &frameData);
+
+ void replyFinished(QHttpNetworkReply *httpReply, qint32 streamID);
+ void replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID,
+ QNetworkReply::NetworkError errorCode, const char *errorMessage);
+
+ qint32 m_nextStreamID;
+ QHash<quint32, HttpMessagePair> m_inFlightStreams;
+ qint32 m_maxConcurrentStreams;
+ quint32 m_initialWindowSize;
+ QByteDataBuffer m_spdyBuffer;
+ bool m_waitingForCompleteStream;
+ z_stream m_deflateStream;
+ z_stream m_inflateStream;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::DataFrameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::ControlFrameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_ID_Flags)
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+
+#endif // QSPDYPROTOCOLHANDLER_H
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
index dc36443718..855ab4e485 100644
--- a/src/network/bearer/qnetworkconfigmanager.cpp
+++ b/src/network/bearer/qnetworkconfigmanager.cpp
@@ -132,7 +132,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
Some configuration updates may require some time to perform updates. A WLAN scan is
such an example. Unless the platform performs internal updates it may be required to
manually trigger configuration updates via QNetworkConfigurationManager::updateConfigurations().
- The completion of the update process is indicted by emitting the updateCompleted()
+ The completion of the update process is indicated by emitting the updateCompleted()
signal. The update process ensures that every existing QNetworkConfiguration instance
is updated. There is no need to ask for a renewed configuration list via allConfigurations().
diff --git a/src/network/doc/snippets/network/tcpwait.cpp b/src/network/doc/snippets/network/tcpwait.cpp
index fb44e2ded9..e5e4c1ed40 100644
--- a/src/network/doc/snippets/network/tcpwait.cpp
+++ b/src/network/doc/snippets/network/tcpwait.cpp
@@ -55,13 +55,13 @@ int main(int argv, char **args)
char buffer[50];
forever {
- numRead = socket.read(buffer, 50);
+ numRead = socket.read(buffer, 50);
- // do whatever with array
+ // do whatever with array
- numReadTotal += numRead;
+ numReadTotal += numRead;
if (numRead == 0 && !socket.waitForReadyRead())
- break;
+ break;
}
//! [0]
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 97f52fdb6e..9b584be206 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -33,9 +33,17 @@ android {
}
win32: {
- HEADERS += kernel/qnetworkinterface_win_p.h
- SOURCES += kernel/qdnslookup_win.cpp kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp
- LIBS_PRIVATE += -ldnsapi
+ !winrt {
+ HEADERS += kernel/qnetworkinterface_win_p.h
+ SOURCES += kernel/qdnslookup_win.cpp \
+ kernel/qhostinfo_win.cpp \
+ kernel/qnetworkinterface_win.cpp
+ LIBS_PRIVATE += -ldnsapi
+ } else {
+ SOURCES += kernel/qdnslookup_winrt.cpp \
+ kernel/qhostinfo_winrt.cpp \
+ kernel/qnetworkinterface_winrt.cpp
+ }
}
integrity:SOURCES += kernel/qdnslookup_unix.cpp kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 8c16486878..edbbbf5a75 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -55,9 +55,11 @@
#include <qmutex.h>
#include <private/qmutexpool_p.h>
#include <rpc.h>
+#ifndef Q_OS_WINRT
#define SECURITY_WIN32 1
#include <security.h>
#endif
+#endif
//#define NTLMV1_CLIENT
@@ -69,7 +71,7 @@ QT_BEGIN_NAMESPACE
static QByteArray qNtlmPhase1();
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx);
static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
#endif
@@ -328,7 +330,7 @@ bool QAuthenticator::isNull() const
return !d;
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
class QNtlmWindowsHandles
{
public:
@@ -340,7 +342,7 @@ public:
QAuthenticatorPrivate::QAuthenticatorPrivate()
: method(None)
- #ifdef Q_OS_WIN
+ #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
, ntlmWindowsHandles(0)
#endif
, hasFailed(false)
@@ -354,7 +356,7 @@ QAuthenticatorPrivate::QAuthenticatorPrivate()
QAuthenticatorPrivate::~QAuthenticatorPrivate()
{
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (ntlmWindowsHandles)
delete ntlmWindowsHandles;
#endif
@@ -485,7 +487,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
case QAuthenticatorPrivate::Ntlm:
methodString = "NTLM ";
if (challenge.isEmpty()) {
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QByteArray phase1Token;
if (user.isEmpty()) // Only pull from system if no user was specified in authenticator
phase1Token = qNtlmPhase1_SSPI(this);
@@ -502,7 +504,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
phase = Phase2;
}
} else {
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QByteArray phase3Token;
if (ntlmWindowsHandles)
phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge));
@@ -1475,7 +1477,7 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
return rc;
}
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// See http://davenport.sourceforge.net/ntlm.html
// and libcurl http_ntlm.c
@@ -1513,7 +1515,6 @@ static bool q_NTLM_SSPI_library_load()
return true;
}
-#ifdef Q_OS_WIN
// Phase 1:
static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx)
{
@@ -1631,8 +1632,6 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray&
return result;
}
-#endif // Q_OS_WIN
-
-#endif
+#endif // Q_OS_WIN && !Q_OS_WINRT
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index 53675d083b..97a402901e 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -363,6 +363,25 @@ void QDnsLookup::setType(Type type)
}
/*!
+ \property QDnsLookup::nameserver
+ \brief the nameserver to use for DNS lookup.
+*/
+
+QHostAddress QDnsLookup::nameserver() const
+{
+ return d_func()->nameserver;
+}
+
+void QDnsLookup::setNameserver(const QHostAddress &nameserver)
+{
+ Q_D(QDnsLookup);
+ if (nameserver != d->nameserver) {
+ d->nameserver = nameserver;
+ emit nameserverChanged(nameserver);
+ }
+}
+
+/*!
Returns the list of canonical name records associated with this lookup.
*/
@@ -463,7 +482,7 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup);
d->isFinished = false;
d->reply = QDnsLookupReply();
- d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name));
+ d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
this, SLOT(_q_lookupFinished(QDnsLookupReply)),
Qt::BlockingQueuedConnection);
@@ -971,7 +990,7 @@ void QDnsLookupRunnable::run()
}
// Perform request.
- query(requestType, requestName, &reply);
+ query(requestType, requestName, nameserver, &reply);
// Sort results.
if (!theDnsLookupSeedStorage()->hasLocalData()) {
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index 1df21d866e..ffbef61f92 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -180,6 +180,7 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject
Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
+ Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged)
public:
enum Error
@@ -209,6 +210,7 @@ public:
explicit QDnsLookup(QObject *parent = 0);
QDnsLookup(Type type, const QString &name, QObject *parent = 0);
+ QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = 0);
~QDnsLookup();
Error error() const;
@@ -221,6 +223,9 @@ public:
Type type() const;
void setType(QDnsLookup::Type);
+ QHostAddress nameserver() const;
+ void setNameserver(const QHostAddress &nameserver);
+
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
QList<QDnsHostAddressRecord> hostAddressRecords() const;
QList<QDnsMailExchangeRecord> mailExchangeRecords() const;
@@ -238,6 +243,7 @@ Q_SIGNALS:
void finished();
void nameChanged(const QString &name);
void typeChanged(Type type);
+ void nameserverChanged(const QHostAddress &nameserver);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
diff --git a/src/network/kernel/qdnslookup_android.cpp b/src/network/kernel/qdnslookup_android.cpp
index dff81dba0c..1ace5727c2 100644
--- a/src/network/kernel/qdnslookup_android.cpp
+++ b/src/network/kernel/qdnslookup_android.cpp
@@ -43,10 +43,11 @@
QT_BEGIN_NAMESPACE
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
Q_UNUSED(requestType);
Q_UNUSED(requestName);
+ Q_UNUSED(nameserver);
Q_UNUSED(reply);
qWarning() << Q_FUNC_INFO << "Not yet supported on Android";
reply->error = QDnsLookup::ResolverError;
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 692b9088fe..7c0b0e862a 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -100,6 +100,7 @@ public:
bool isFinished;
QString name;
QDnsLookup::Type type;
+ QHostAddress nameserver;
QDnsLookupReply reply;
QDnsLookupRunnable *runnable;
@@ -111,9 +112,10 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
- QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name)
+ QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name, const QHostAddress &nameserver)
: requestType(type)
, requestName(name)
+ , nameserver(nameserver)
{ }
void run();
@@ -121,9 +123,10 @@ signals:
void finished(const QDnsLookupReply &reply);
private:
- static void query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply);
+ static void query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply);
QDnsLookup::Type requestType;
QByteArray requestName;
+ QHostAddress nameserver;
};
class QDnsLookupThreadPool : public QThreadPool
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 9fb488cee6..26834dff57 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -115,7 +115,7 @@ static void resolveLibrary()
local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
}
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
// Load dn_expand, res_ninit and res_nquery on demand.
static QBasicAtomicInt triedResolve = Q_BASIC_ATOMIC_INITIALIZER(false);
@@ -142,6 +142,43 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
reply->errorString = tr("Resolver initialization failed");
return;
}
+
+ //Check if a nameserver was set. If so, use it
+ if (!nameserver.isNull()) {
+ if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
+ state.nsaddr_list[0].sin_addr.s_addr = htonl(nameserver.toIPv4Address());
+ state.nscount = 1;
+ } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
+#if defined(Q_OS_LINUX)
+ struct sockaddr_in6 *ns;
+ ns = state._u._ext.nsaddrs[0];
+ // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
+ if (!ns) {
+ // Memory allocated here will be free'd in res_close() as we
+ // have done res_init() above.
+ ns = (struct sockaddr_in6*) calloc(1, sizeof(struct sockaddr_in6));
+ Q_CHECK_PTR(ns);
+ state._u._ext.nsaddrs[0] = ns;
+ }
+ // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
+ // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
+ state._u._ext.nsmap[0] = MAXNS + 1;
+ state._u._ext.nscount6 = 1;
+ ns->sin6_family = AF_INET6;
+ ns->sin6_port = htons(53);
+
+ Q_IPV6ADDR ipv6Address = nameserver.toIPv6Address();
+ for (int i=0; i<16; i++) {
+ ns->sin6_addr.s6_addr[i] = ipv6Address[i];
+ }
+#else
+ qWarning() << Q_FUNC_INFO << "IPv6 addresses for nameservers is currently not supported";
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("IPv6 addresses for nameservers is currently not supported");
+ return;
+#endif
+ }
+ }
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
#endif
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
index bf80a23297..6f58e64440 100644
--- a/src/network/kernel/qdnslookup_win.cpp
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -48,15 +48,33 @@
#include <qt_windows.h>
#include <windns.h>
+#include <memory.h>
QT_BEGIN_NAMESPACE
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, QDnsLookupReply *reply)
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
{
// Perform DNS query.
PDNS_RECORD dns_records = 0;
const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
- const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, NULL, &dns_records, NULL);
+ IP4_ARRAY srvList;
+ memset(&srvList, 0, sizeof(IP4_ARRAY));
+ if (!nameserver.isNull()) {
+ if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
+ // The below code is referenced from: http://support.microsoft.com/kb/831226
+ srvList.AddrCount = 1;
+ srvList.AddrArray[0] = htonl(nameserver.toIPv4Address());
+ } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
+ // For supoprting IPv6 nameserver addresses, we'll need to switch
+ // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6
+ // address in the nameserver list
+ qWarning() << Q_FUNC_INFO << "IPv6 addresses for nameservers is currently not supported";
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("IPv6 addresses for nameservers is currently not supported");
+ return;
+ }
+ }
+ const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL);
switch (status) {
case ERROR_SUCCESS:
break;
diff --git a/src/network/kernel/qdnslookup_winrt.cpp b/src/network/kernel/qdnslookup_winrt.cpp
new file mode 100644
index 0000000000..6ac944934a
--- /dev/null
+++ b/src/network/kernel/qdnslookup_winrt.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnslookup_p.h"
+
+#include <qurl.h>
+#include <qdebug.h>
+
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.foundation.collections.h>
+#include <windows.networking.h>
+#include <windows.networking.sockets.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Connectivity;
+using namespace ABI::Windows::Networking::Sockets;
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+{
+ // TODO: Add nameserver support for winRT
+ if (!nameserver.isNull())
+ qWarning() << "Ignoring nameserver as its currently not supported on WinRT";
+
+ // TODO: is there any way to do "proper" dns lookup?
+ if (requestType != QDnsLookup::A && requestType != QDnsLookup::AAAA
+ && requestType != QDnsLookup::ANY) {
+ reply->error = QDnsLookup::InvalidRequestError;
+ reply->errorString = QLatin1String("WinRT only supports IPv4 and IPv6 requests");
+ return;
+ }
+
+ QString aceHostname = QUrl::fromAce(requestName);
+ if (aceHostname.isEmpty()) {
+ reply->error = QDnsLookup::InvalidRequestError;
+ reply->errorString = requestName.isEmpty() ? tr("No hostname given") : tr("Invalid hostname");
+ return;
+ }
+
+ IHostNameFactory *hostnameFactory;
+
+ HStringReference classId(RuntimeClass_Windows_Networking_HostName);
+ if (FAILED(GetActivationFactory(classId.Get(), &hostnameFactory))) {
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = QLatin1String("Could not obtain hostname factory");
+ return;
+ }
+ IHostName *host;
+ HStringReference hostNameRef((const wchar_t*)aceHostname.utf16());
+ hostnameFactory->CreateHostName(hostNameRef.Get(), &host);
+ hostnameFactory->Release();
+
+ IDatagramSocketStatics *datagramSocketStatics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramSocketStatics);
+
+ IAsyncOperation<IVectorView<EndpointPair*> *> *op;
+ HSTRING proto;
+ WindowsCreateString(L"0", 1, &proto);
+ datagramSocketStatics->GetEndpointPairsAsync(host, proto, &op);
+ datagramSocketStatics->Release();
+ host->Release();
+
+ IVectorView<EndpointPair*> *endpointPairs = 0;
+ HRESULT hr = op->GetResults(&endpointPairs);
+ int waitCount = 0;
+ while (hr == E_ILLEGAL_METHOD_CALL) {
+ WaitForSingleObjectEx(GetCurrentThread(), 50, FALSE);
+ hr = op->GetResults(&endpointPairs);
+ if (++waitCount > 1200) // Wait for 1 minute max
+ return;
+ }
+ op->Release();
+
+ if (!endpointPairs)
+ return;
+
+ unsigned int size;
+ endpointPairs->get_Size(&size);
+ for (unsigned int i = 0; i < size; ++i) {
+ IEndpointPair *endpointpair;
+ endpointPairs->GetAt(i, &endpointpair);
+ IHostName *remoteHost;
+ endpointpair->get_RemoteHostName(&remoteHost);
+ endpointpair->Release();
+ HostNameType type;
+ remoteHost->get_Type(&type);
+ if (type == HostNameType_Bluetooth || type == HostNameType_DomainName
+ || (requestType != QDnsLookup::ANY
+ && ((type == HostNameType_Ipv4 && requestType == QDnsLookup::AAAA)
+ || (type == HostNameType_Ipv6 && requestType == QDnsLookup::A))))
+ continue;
+
+ HSTRING name;
+ remoteHost->get_CanonicalName(&name);
+ remoteHost->Release();
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(name, &length);
+ QDnsHostAddressRecord record;
+ record.d->name = aceHostname;
+ record.d->value = QHostAddress(QString::fromWCharArray(rawString, length));
+ reply->hostAddressRecords.append(record);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 18fd6dee58..0ab72191dc 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
// sockaddr_in6 size changed between old and new SDK
// Only the new version is the correct one, so always
// use this structure.
-#if defined(Q_OS_WINCE)
+#if defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
# if !defined(u_char)
# define u_char unsigned char
# endif
@@ -448,10 +448,12 @@ QHostAddress::QHostAddress(const QString &address)
QHostAddress::QHostAddress(const struct sockaddr *sockaddr)
: d(new QHostAddressPrivate)
{
+#ifndef Q_OS_WINRT
if (sockaddr->sa_family == AF_INET)
setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr));
else if (sockaddr->sa_family == AF_INET6)
setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr);
+#endif
}
/*!
@@ -604,11 +606,13 @@ bool QHostAddress::setAddress(const QString &address)
*/
void QHostAddress::setAddress(const struct sockaddr *sockaddr)
{
+#ifndef Q_OS_WINRT
clear();
if (sockaddr->sa_family == AF_INET)
setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr));
else if (sockaddr->sa_family == AF_INET6)
setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr);
+#endif
}
/*!
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index d25372ece6..cafb09b181 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -433,7 +433,7 @@ void QHostInfo::setErrorString(const QString &str)
Returns the DNS domain of this machine.
- Note: DNS domains are not related to domain names found in
+ \note DNS domains are not related to domain names found in
Windows networks.
\sa hostName()
@@ -508,7 +508,7 @@ QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), was
{
moveToThread(QCoreApplicationPrivate::mainThread());
connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
- threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
+ threadPool.setMaxThreadCount(20); // do up to 20 DNS lookups in parallel
}
QHostInfoLookupManager::~QHostInfoLookupManager()
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 32b7318335..df8c8b145a 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -259,10 +259,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
} else if (result == EAI_NONAME
|| result == EAI_FAIL
#ifdef EAI_NODATA
- // EAI_NODATA is deprecated in RFC 3493
- || result == EAI_NODATA
+ // EAI_NODATA is deprecated in RFC 3493
+ || result == EAI_NODATA
#endif
- ) {
+ ) {
results.setError(QHostInfo::HostNotFound);
results.setErrorString(tr("Host not found"));
} else {
diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp
index 570d1a82ea..cbaa9bec6a 100644
--- a/src/network/kernel/qhostinfo_win.cpp
+++ b/src/network/kernel/qhostinfo_win.cpp
@@ -134,8 +134,8 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QHostInfo results;
#if defined(QHOSTINFO_DEBUG)
- qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)",
- this, hostName.toLatin1().constData(),
+ qDebug("QHostInfoAgent::fromName(): looking up \"%s\" (IPv6 support is %s)",
+ hostName.toLatin1().constData(),
(local_getaddrinfo && local_freeaddrinfo) ? "enabled" : "disabled");
#endif
@@ -248,8 +248,8 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
#if defined(QHOSTINFO_DEBUG)
if (results.error() != QHostInfo::NoError) {
- qDebug("QHostInfoAgent::run(%p): error (%s)",
- this, results.errorString().toLatin1().constData());
+ qDebug("QHostInfoAgent::run(): error (%s)",
+ results.errorString().toLatin1().constData());
} else {
QString tmp;
QList<QHostAddress> addresses = results.addresses();
@@ -257,8 +257,8 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
if (i != 0) tmp += ", ";
tmp += addresses.at(i).toString();
}
- qDebug("QHostInfoAgent::run(%p): found %i entries: {%s}",
- this, addresses.count(), tmp.toLatin1().constData());
+ qDebug("QHostInfoAgent::run(): found %i entries: {%s}",
+ addresses.count(), tmp.toLatin1().constData());
}
#endif
return results;
diff --git a/src/network/kernel/qhostinfo_winrt.cpp b/src/network/kernel/qhostinfo_winrt.cpp
new file mode 100644
index 0000000000..928c9e4628
--- /dev/null
+++ b/src/network/kernel/qhostinfo_winrt.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhostinfo_p.h"
+
+#include <qurl.h>
+
+#include <ppltasks.h>
+#include <wrl.h>
+#include <windows.networking.h>
+#include <windows.networking.sockets.h>
+#include <windows.networking.connectivity.h>
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Connectivity;
+using namespace ABI::Windows::Networking::Sockets;
+
+QT_BEGIN_NAMESPACE
+
+//#define QHOSTINFO_DEBUG
+
+QHostInfo QHostInfoAgent::fromName(const QString &hostName)
+{
+ QHostInfo results;
+
+ QHostAddress address;
+ if (address.setAddress(hostName)) {
+ // Reverse lookup
+ // TODO: is there a replacement for getnameinfo for winrt?
+ Q_UNIMPLEMENTED();
+ return results;
+ }
+
+ QByteArray aceHostname = QUrl::toAce(hostName);
+ results.setHostName(hostName);
+ if (aceHostname.isEmpty()) {
+ results.setError(QHostInfo::HostNotFound);
+ results.setErrorString(hostName.isEmpty() ? tr("No host name given") : tr("Invalid hostname"));
+ return results;
+ }
+
+ IHostNameFactory *hostnameFactory;
+
+ HStringReference classId(RuntimeClass_Windows_Networking_HostName);
+ if (FAILED(GetActivationFactory(classId.Get(), &hostnameFactory)))
+ Q_ASSERT(false, "Could not obtain hostname factory.");
+
+ IHostName *host;
+ HStringReference hostNameRef((const wchar_t*)hostName.utf16());
+ hostnameFactory->CreateHostName(hostNameRef.Get(), &host);
+ hostnameFactory->Release();
+
+ IDatagramSocketStatics *datagramSocketStatics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramSocketStatics);
+
+ IAsyncOperation<IVectorView<EndpointPair*> *> *op;
+ HSTRING proto;
+ WindowsCreateString(L"0", 1, &proto);
+ datagramSocketStatics->GetEndpointPairsAsync(host, proto, &op);
+ datagramSocketStatics->Release();
+ host->Release();
+
+ IVectorView<EndpointPair*> *endpointPairs = 0;
+ HRESULT hr = op->GetResults(&endpointPairs);
+ int waitCount = 0;
+ while (hr == E_ILLEGAL_METHOD_CALL) {
+ WaitForSingleObjectEx(GetCurrentThread(), 50, FALSE);
+ hr = op->GetResults(&endpointPairs);
+ if (++waitCount > 1200) // Wait for 1 minute max
+ return results;
+ }
+ op->Release();
+
+ if (!endpointPairs)
+ return results;
+
+ unsigned int size;
+ endpointPairs->get_Size(&size);
+ QList<QHostAddress> addresses;
+ for (unsigned int i = 0; i < size; ++i) {
+ IEndpointPair *endpointpair;
+ endpointPairs->GetAt(i, &endpointpair);
+ IHostName *remoteHost;
+ endpointpair->get_RemoteHostName(&remoteHost);
+ endpointpair->Release();
+ if (!remoteHost)
+ continue;
+ HostNameType type;
+ remoteHost->get_Type(&type);
+ if (type == HostNameType_DomainName)
+ continue;
+
+ HSTRING name;
+ remoteHost->get_CanonicalName(&name);
+ remoteHost->Release();
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(name, &length);
+ QHostAddress addr;
+ addr.setAddress(QString::fromWCharArray(rawString, length));
+ if (!addresses.contains(addr))
+ addresses.append(addr);
+ }
+ results.setAddresses(addresses);
+
+ return results;
+}
+
+QString QHostInfo::localHostName()
+{
+ INetworkInformationStatics *statics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &statics);
+
+ IVectorView<HostName*> *hostNames = 0;
+ statics->GetHostNames(&hostNames);
+ statics->Release();
+ if (!hostNames)
+ return QString();
+
+ unsigned int size;
+ hostNames->get_Size(&size);
+ if (size == 0)
+ return QString();
+
+ for (unsigned int i = 0; i < size; ++i) {
+ IHostName *hostName;
+ hostNames->GetAt(i, &hostName);
+ HostNameType type;
+ hostName->get_Type(&type);
+ if (type != HostNameType_DomainName)
+ continue;
+
+ HSTRING name;
+ hostName->get_CanonicalName(&name);
+ hostName->Release();
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(name, &length);
+ return QString::fromWCharArray(rawString, length);
+ }
+ IHostName *firstHost;
+ hostNames->GetAt(0, &firstHost);
+ hostNames->Release();
+
+ HSTRING name;
+ firstHost->get_CanonicalName(&name);
+ firstHost->Release();
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(name, &length);
+ return QString::fromWCharArray(rawString, length);
+}
+
+// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index d3c830a66f..d0e2eca1e0 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -228,7 +228,7 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa
memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1));
} else
#endif
- {
+ {
// use this name anyways
iface->name = QString::fromLatin1(req.ifr_name);
}
diff --git a/src/network/kernel/qnetworkinterface_winrt.cpp b/src/network/kernel/qnetworkinterface_winrt.cpp
new file mode 100644
index 0000000000..6a814c85d4
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_winrt.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+#include <wrl.h>
+#include <windows.foundation.h>
+#include <windows.foundation.collections.h>
+#include <windows.networking.h>
+#include <windows.networking.connectivity.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Connectivity;
+
+#include <qhostinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+struct HostNameInfo {
+ GUID adapterId;
+ unsigned char prefixLength;
+ QString address;
+};
+
+static QList<QNetworkInterfacePrivate *> interfaceListing()
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ QList<HostNameInfo> hostList;
+
+ INetworkInformationStatics *hostNameStatics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &hostNameStatics);
+
+ IVectorView<HostName*> *hostNames = 0;
+ hostNameStatics->GetHostNames(&hostNames);
+ hostNameStatics->Release();
+ if (!hostNames)
+ return interfaces;
+
+ unsigned int hostNameCount;
+ hostNames->get_Size(&hostNameCount);
+ for (unsigned i = 0; i < hostNameCount; ++i) {
+ HostNameInfo hostInfo;
+ IHostName *hostName;
+ hostNames->GetAt(i, &hostName);
+
+ HostNameType type;
+ hostName->get_Type(&type);
+ if (type == HostNameType_DomainName)
+ continue;
+
+ IIPInformation *ipInformation;
+ hostName->get_IPInformation(&ipInformation);
+ INetworkAdapter *currentAdapter;
+ ipInformation->get_NetworkAdapter(&currentAdapter);
+
+ currentAdapter->get_NetworkAdapterId(&hostInfo.adapterId);
+ currentAdapter->Release();
+
+ IReference<unsigned char> *prefixLengthReference;
+ ipInformation->get_PrefixLength(&prefixLengthReference);
+ ipInformation->Release();
+
+ prefixLengthReference->get_Value(&hostInfo.prefixLength);
+ prefixLengthReference->Release();
+
+ // invalid prefixes
+ if ((type == HostNameType_Ipv4 && hostInfo.prefixLength > 32)
+ || (type == HostNameType_Ipv6 && hostInfo.prefixLength > 128))
+ continue;
+
+ HSTRING name;
+ hostName->get_CanonicalName(&name);
+ hostName->Release();
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(name, &length);
+ hostInfo.address = QString::fromWCharArray(rawString, length);
+
+ hostList << hostInfo;
+ }
+ hostNames->Release();
+
+ INetworkInformationStatics *networkInfoStatics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkInfoStatics);
+ IVectorView<ConnectionProfile *> *connectionProfiles = 0;
+ networkInfoStatics->GetConnectionProfiles(&connectionProfiles);
+ networkInfoStatics->Release();
+ if (!connectionProfiles)
+ return interfaces;
+
+ unsigned int size;
+ connectionProfiles->get_Size(&size);
+ for (unsigned int i = 0; i < size; ++i) {
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+
+ IConnectionProfile *profile;
+ connectionProfiles->GetAt(i, &profile);
+
+ NetworkConnectivityLevel connectivityLevel;
+ profile->GetNetworkConnectivityLevel(&connectivityLevel);
+ if (connectivityLevel != NetworkConnectivityLevel_None)
+ iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+
+ INetworkAdapter *adapter;
+ profile->get_NetworkAdapter(&adapter);
+ profile->Release();
+ UINT32 type;
+ adapter->get_IanaInterfaceType(&type);
+ if (type == 23)
+ iface->flags |= QNetworkInterface::IsPointToPoint;
+ GUID id;
+ adapter->get_NetworkAdapterId(&id);
+ adapter->Release();
+ OLECHAR adapterName[39]={0};
+ StringFromGUID2(id, adapterName, 39);
+ iface->name = QString::fromWCharArray(adapterName);
+
+ // According to http://stackoverflow.com/questions/12936193/how-unique-is-the-ethernet-network-adapter-id-in-winrt-it-is-derived-from-the-m
+ // obtaining the MAC address using WinRT API is impossible
+ // iface->hardwareAddress = ?
+
+ for (int i = 0; i < hostList.length(); ++i) {
+ const HostNameInfo hostInfo = hostList.at(i);
+ if (id != hostInfo.adapterId)
+ continue;
+
+ QNetworkAddressEntry entry;
+ entry.setIp(QHostAddress(hostInfo.address));
+ entry.setPrefixLength(hostInfo.prefixLength);
+ iface->addressEntries << entry;
+
+ hostList.takeAt(i);
+ --i;
+ }
+ }
+ connectionProfiles->Release();
+ return interfaces;
+}
+
+QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
+{
+ return interfaceListing();
+}
+
+QString QHostInfo::localDomainName()
+{
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 5cc41392b5..e797eaaed4 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -841,7 +841,7 @@ QList<QByteArray> QNetworkProxy::rawHeaderList() const
will also set the known header LastModifiedHeader to be the
QDateTime object of the parsed date.
- Note: setting the same header twice overrides the previous
+ \note Setting the same header twice overrides the previous
setting. To accomplish the behaviour of multiple HTTP headers of
the same name, you should concatenate the two values, separating
them with a comma (",") and set one single raw header.
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index bebdf728a7..1ceec717c6 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -372,9 +372,22 @@
IP_MULTICAST_LOOP (multicast loopback) socket option.
\value TypeOfServiceOption This option is not supported on
- Windows. This maps to the IP_TOS socket option.
+ Windows. This maps to the IP_TOS socket option. For possible values,
+ see table below.
- Possible values for the \e{TypeOfServiceOption} are:
+ \value SendBufferSizeSocketOption Sets the socket send buffer size
+ in bytes at the OS level. This maps to the SO_SNDBUF socket option.
+ This option does not affect the QIODevice or QAbstractSocket buffers.
+ This enum value has been introduced in Qt 5.3.
+
+ \value ReceiveBufferSizeSocketOption Sets the socket receive
+ buffer size in bytes at the OS level.
+ This maps to the SO_RCVBUF socket option.
+ This option does not affect the QIODevice or QAbstractSocket buffers
+ (see \l{QAbstractSocket::}{setReadBufferSize()}).
+ This enum value has been introduced in Qt 5.3.
+
+ Possible values for \e{TypeOfServiceOption} are:
\table
\header \li Value \li Description
@@ -735,8 +748,8 @@ bool QAbstractSocketPrivate::canReadNotification()
return true;
}
- if (!hasData && socketEngine)
- socketEngine->setReadNotificationEnabled(true);
+ if (isBuffered && socketEngine)
+ socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable());
// reset the read socket notifier state if we reentered inside the
// readyRead() connected slot.
@@ -1539,7 +1552,7 @@ bool QAbstractSocket::bind(quint16 port, BindMode mode)
Returns \c true if the socket is valid and ready for use; otherwise
returns \c false.
- \b{Note:} The socket's state must be ConnectedState before reading and
+ \note The socket's state must be ConnectedState before reading and
writing can occur.
\sa state()
@@ -1904,6 +1917,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons
case TypeOfServiceOption:
d_func()->socketEngine->setOption(QAbstractSocketEngine::TypeOfServiceOption, value.toInt());
break;
+
+ case SendBufferSizeSocketOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::SendBufferSocketOption, value.toInt());
+ break;
+
+ case ReceiveBufferSizeSocketOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, value.toInt());
+ break;
}
}
@@ -1938,6 +1959,14 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option)
case TypeOfServiceOption:
ret = d_func()->socketEngine->option(QAbstractSocketEngine::TypeOfServiceOption);
break;
+
+ case SendBufferSizeSocketOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::SendBufferSocketOption);
+ break;
+
+ case ReceiveBufferSizeSocketOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::ReceiveBufferSocketOption);
+ break;
}
if (ret == -1)
return QVariant();
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index 46114abf73..8b019cf0fb 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -115,7 +115,9 @@ public:
KeepAliveOption, // SO_KEEPALIVE
MulticastTtlOption, // IP_MULTICAST_TTL
MulticastLoopbackOption, // IP_MULTICAST_LOOPBACK
- TypeOfServiceOption //IP_TOS
+ TypeOfServiceOption, //IP_TOS
+ SendBufferSizeSocketOption, //SO_SNDBUF
+ ReceiveBufferSizeSocketOption //SO_RCVBUF
};
enum BindFlag {
DefaultForPlatform = 0x0,
diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp
index 1275461d7d..d8abe01241 100644
--- a/src/network/socket/qabstractsocketengine.cpp
+++ b/src/network/socket/qabstractsocketengine.cpp
@@ -41,7 +41,11 @@
#include "qabstractsocketengine_p.h"
+#ifndef Q_OS_WINRT
#include "qnativesocketengine_p.h"
+#else
+#include "qnativesocketengine_winrt_p.h"
+#endif
#include "qmutex.h"
#include "qnetworkproxy.h"
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 1dec96762c..6a30012562 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -150,7 +150,7 @@ public:
virtual bool waitForRead(int msecs = 30000, bool *timedOut = 0) = 0;
virtual bool waitForWrite(int msecs = 30000, bool *timedOut = 0) = 0;
virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
- bool checkRead, bool checkWrite,
+ bool checkRead, bool checkWrite,
int msecs = 30000, bool *timedOut = 0) = 0;
QAbstractSocket::SocketError error() const;
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index f7f8aab182..d268a238e0 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -131,7 +131,7 @@ QLocalServer::QLocalServer(QObject *parent)
QLocalServer::~QLocalServer()
{
if (isListening())
- close();
+ close();
}
/*!
@@ -278,8 +278,7 @@ bool QLocalServer::isListening() const
could also be a path such as "/tmp/foo" and on Windows this could
be a pipe path such as "\\\\.\\pipe\\foo"
- Note:
- On Unix if the server crashes without closing listen will fail
+ \note On Unix if the server crashes without closing listen will fail
with AddressInUseError. To create a new server the file should be removed.
On Windows two local servers can listen to the same pipe at the same
time, but any connections will go to one of the server.
diff --git a/src/network/socket/qlocalserver_tcp.cpp b/src/network/socket/qlocalserver_tcp.cpp
index 39e7babf71..de4acd9111 100644
--- a/src/network/socket/qlocalserver_tcp.cpp
+++ b/src/network/socket/qlocalserver_tcp.cpp
@@ -80,8 +80,6 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
bool QLocalServerPrivate::listen(qintptr socketDescriptor)
{
- Q_Q(QLocalServer);
-
return tcpServer.setSocketDescriptor(socketDescriptor);
}
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
index d4251672db..f7033893bc 100644
--- a/src/network/socket/qlocalsocket.cpp
+++ b/src/network/socket/qlocalsocket.cpp
@@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE
waitForReadyRead(), waitForBytesWritten(), and waitForDisconnected()
which blocks until the operation is complete or the timeout expires.
- Note that this feature is not supported on versions of Windows earlier than
+ \note This feature is not supported on versions of Windows earlier than
Windows XP.
\sa QLocalServer
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index 97a9b98c30..fc1afa48c9 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -162,8 +162,8 @@ public:
bool waitForRead(int msecs = 30000, bool *timedOut = 0);
bool waitForWrite(int msecs = 30000, bool *timedOut = 0);
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
- bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0);
+ bool checkRead, bool checkWrite,
+ int msecs = 30000, bool *timedOut = 0);
bool isReadNotificationEnabled() const;
void setReadNotificationEnabled(bool enable);
@@ -271,7 +271,7 @@ public:
qint64 nativeWrite(const char *data, qint64 length);
int nativeSelect(int timeout, bool selectForRead) const;
int nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const;
+ bool *selectForRead, bool *selectForWrite) const;
#ifdef Q_OS_WIN
void setPortAndAddress(sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6,
quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize);
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index e076f2b4bf..b1b19528c2 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -144,7 +144,7 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET;
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
- int socket = qt_safe_socket(protocol, type, 0);
+ int socket = qt_safe_socket(protocol, type, 0);
if (socket <= 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) {
protocol = AF_INET;
socket = qt_safe_socket(protocol, type, 0);
@@ -363,7 +363,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 port)
{
#ifdef QNATIVESOCKETENGINE_DEBUG
- qDebug("QNativeSocketEnginePrivate::nativeConnect() : %d ", socketDescriptor);
+ qDebug("QNativeSocketEnginePrivate::nativeConnect() : %lli", socketDescriptor);
#endif
struct sockaddr_in sockAddrIPv4;
@@ -847,7 +847,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %i", recvResult);
+ qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %zd", recvResult);
#endif
return qint64(recvResult);
@@ -1012,13 +1012,13 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
socketType = QAbstractSocket::UnknownSocketType;
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- QString socketProtocolStr = "UnknownProtocol";
- if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = "IPv4Protocol";
- else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = "IPv6Protocol";
+ QString socketProtocolStr = QStringLiteral("UnknownProtocol");
+ if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = QStringLiteral("IPv4Protocol");
+ else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = QStringLiteral("IPv6Protocol");
- QString socketTypeStr = "UnknownSocketType";
- if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = "TcpSocket";
- else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = "UdpSocket";
+ QString socketTypeStr = QStringLiteral("UnknownSocketType");
+ if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QStringLiteral("TcpSocket");
+ else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QStringLiteral("UdpSocket");
qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() local == %s:%i,"
" peer == %s:%i, socket == %s - %s",
@@ -1112,7 +1112,7 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %llu) == %i",
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %llu) == %zd",
data, qt_prettyDebug(data, qMin(r, ssize_t(16)), r).data(),
maxSize, r);
#endif
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 751ac9b182..b1c9073eb9 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -186,7 +186,7 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt
*address = a;
}
if (port)
- WSANtohs(socketDescriptor, sa6->sin6_port, port);
+ WSANtohs(socketDescriptor, sa6->sin6_port, port);
} else
if (sa->a.sa_family == AF_INET) {
@@ -194,11 +194,11 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt
unsigned long addr;
WSANtohl(socketDescriptor, sa4->sin_addr.s_addr, &addr);
QHostAddress a;
- a.setAddress(addr);
- if (address)
- *address = a;
+ a.setAddress(addr);
+ if (address)
+ *address = a;
if (port)
- WSANtohs(socketDescriptor, sa4->sin_port, port);
+ WSANtohs(socketDescriptor, sa4->sin_port, port);
}
}
@@ -276,7 +276,7 @@ QWindowsSockInit::QWindowsSockInit()
// IPv6 requires Winsock v2.0 or better.
if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
- qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed.");
+ qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed.");
} else {
version = 0x20;
}
@@ -940,14 +940,14 @@ int QNativeSocketEnginePrivate::nativeAccept()
break;
}
} else if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) {
- // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket
- // with the same attributes as the listening socket including the current
- // WSAAsyncSelect(). To be able to change the socket to blocking mode the
- // WSAAsyncSelect() call must be cancled.
- QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read);
- n.setEnabled(true);
- n.setEnabled(false);
- }
+ // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket
+ // with the same attributes as the listening socket including the current
+ // WSAAsyncSelect(). To be able to change the socket to blocking mode the
+ // WSAAsyncSelect() call must be cancled.
+ QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read);
+ n.setEnabled(true);
+ n.setEnabled(false);
+ }
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor);
#endif
diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp
new file mode 100644
index 0000000000..4b2d1c372e
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_winrt.cpp
@@ -0,0 +1,1182 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qt_windows.h>
+
+#include "qnativesocketengine_winrt_p.h"
+
+#include <qcoreapplication.h>
+#include <qabstracteventdispatcher.h>
+#include <qsocketnotifier.h>
+#include <qdatetime.h>
+#include <qnetworkinterface.h>
+#include <qelapsedtimer.h>
+#include <qthread.h>
+#include <qabstracteventdispatcher.h>
+
+#include <private/qeventdispatcher_winrt_p.h>
+
+#include <wrl.h>
+#include <windows.foundation.collections.h>
+#include <windows.storage.streams.h>
+#include <windows.networking.h>
+#include <windows.networking.sockets.h>
+#include <robuffer.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Storage::Streams;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Connectivity;
+using namespace ABI::Windows::Networking::Sockets;
+
+typedef ITypedEventHandler<StreamSocketListener *, StreamSocketListenerConnectionReceivedEventArgs *> ClientConnectedHandler;
+typedef ITypedEventHandler<DatagramSocket *, DatagramSocketMessageReceivedEventArgs *> DatagramReceivedHandler;
+typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
+typedef IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32> SocketWriteCompletedHandler;
+
+QT_BEGIN_NAMESPACE
+
+// Common constructs
+#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \
+ if (!isValid()) { \
+ qWarning(""#function" was called on an uninitialized socket device"); \
+ return returnValue; \
+ } } while (0)
+#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \
+ if (isValid()) { \
+ qWarning(""#function" was called on an already initialized socket device"); \
+ return returnValue; \
+ } } while (0)
+#define Q_CHECK_STATE(function, checkState, returnValue) do { \
+ if (d->socketState != (checkState)) { \
+ qWarning(""#function" was not called in "#checkState); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \
+ if (d->socketState == (checkState)) { \
+ qWarning(""#function" was called in "#checkState); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \
+ if (d->socketState != (state1) && d->socketState != (state2)) { \
+ qWarning(""#function" was called" \
+ " not in "#state1" or "#state2); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_TYPE(function, type, returnValue) do { \
+ if (d->socketType != (type)) { \
+ qWarning(#function" was called by a" \
+ " socket other than "#type""); \
+ return (returnValue); \
+ } } while (0)
+#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a)
+
+typedef QHash<qintptr, IStreamSocket *> TcpSocketHash;
+
+struct SocketHandler
+{
+ SocketHandler() : socketCount(0) {}
+ qintptr socketCount;
+ TcpSocketHash pendingTcpSockets;
+};
+
+Q_GLOBAL_STATIC(SocketHandler, gSocketHandler)
+
+QString qt_QStringFromHSTRING(HSTRING string)
+{
+ UINT32 length;
+ PCWSTR rawString = WindowsGetStringRawBuffer(string, &length);
+ return QString::fromWCharArray(rawString, length);
+}
+
+class ByteArrayBuffer : public Microsoft::WRL::RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>,
+ IBuffer, Windows::Storage::Streams::IBufferByteAccess>
+{
+public:
+ ByteArrayBuffer(int size) : m_bytes(size, Qt::Uninitialized), m_length(0)
+ {
+ }
+
+ ByteArrayBuffer(const char *data, int size) : m_bytes(data, size), m_length(size)
+ {
+ }
+
+ HRESULT __stdcall Buffer(byte **value)
+ {
+ *value = reinterpret_cast<byte *>(m_bytes.data());
+ return S_OK;
+ }
+
+ HRESULT __stdcall get_Capacity(UINT32 *value)
+ {
+ *value = m_bytes.size();
+ return S_OK;
+ }
+
+ HRESULT __stdcall get_Length(UINT32 *value)
+ {
+ *value = m_length;
+ return S_OK;
+ }
+
+ HRESULT __stdcall put_Length(UINT32 value)
+ {
+ Q_ASSERT(value <= UINT32(m_bytes.size()));
+ m_length = value;
+ return S_OK;
+ }
+
+ QNativeSocketEngine *engine() const
+ {
+ return m_engine;
+ }
+
+ void setEngine(QNativeSocketEngine *engine)
+ {
+ m_engine = engine;
+ }
+
+ ComPtr<IInputStream> inputStream() const
+ {
+ return m_stream;
+ }
+
+ void setInputStream(ComPtr<IInputStream> stream)
+ {
+ m_stream = stream;
+ }
+
+private:
+ QByteArray m_bytes;
+ UINT32 m_length;
+ QPointer<QNativeSocketEngine> m_engine;
+ ComPtr<IInputStream> m_stream;
+};
+
+QNativeSocketEngine::QNativeSocketEngine(QObject *parent)
+ : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent)
+{
+}
+
+QNativeSocketEngine::~QNativeSocketEngine()
+{
+ close();
+}
+
+bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ Q_D(QNativeSocketEngine);
+ if (isValid())
+ close();
+
+ // Create the socket
+ if (!d->createNewSocket(type, protocol))
+ return false;
+
+ d->socketType = type;
+ d->socketProtocol = protocol;
+ return true;
+}
+
+bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
+{
+ Q_D(QNativeSocketEngine);
+
+ if (isValid())
+ close();
+
+ d->socketDescriptor = socketDescriptor;
+
+ // Currently, only TCP sockets are initialized this way.
+ SocketHandler *handler = gSocketHandler();
+ d->tcp = handler->pendingTcpSockets.value(socketDescriptor, Q_NULLPTR);
+ d->socketType = QAbstractSocket::TcpSocket;
+
+ if (!d->tcp || !d->fetchConnectionParameters())
+ return false;
+
+ d->socketState = socketState;
+ return true;
+}
+
+qintptr QNativeSocketEngine::socketDescriptor() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->socketDescriptor;
+}
+
+bool QNativeSocketEngine::isValid() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->socketDescriptor != -1;
+}
+
+bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
+{
+ const QString addressString = address.toString();
+ return connectToHostByName(addressString, port);
+}
+
+bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(name.utf16()));
+ ComPtr<IHostNameFactory> hostNameFactory;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ ComPtr<IHostName> remoteHost;
+ if (FAILED(hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost))) {
+ qWarning("QNativeSocketEnginePrivate::nativeConnect:: Could not create hostname");
+ return false;
+ }
+
+ const QString portString = QString::number(port);
+ HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
+ ComPtr<IAsyncAction> action;
+ HRESULT hr = E_FAIL;
+ if (d->socketType == QAbstractSocket::TcpSocket)
+ hr = d->tcp->ConnectAsync(remoteHost.Get(), portReference.Get(), &action);
+ else if (d->socketType == QAbstractSocket::UdpSocket)
+ hr = d->udp->ConnectAsync(remoteHost.Get(), portReference.Get(), &action);
+ if (FAILED(hr)) {
+ qWarning("QNativeSocketEnginePrivate::nativeConnect:: Could not obtain connect action");
+ return false;
+ }
+
+ action->put_Completed(Callback<IAsyncActionCompletedHandler>(&QNativeSocketEnginePrivate::interruptEventDispatcher).Get());
+ hr = action->GetResults();
+ while ((hr = action->GetResults()) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
+ if (hr == 0x8007274c) { // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
+ d->setError(QAbstractSocket::NetworkError, d->ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
+ if (hr == 0x8007274d) { // No connection could be made because the target machine actively refused it.
+ d->setError(QAbstractSocket::ConnectionRefusedError, d->ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
+ if (FAILED(hr)) {
+ d->setError(QAbstractSocket::UnknownSocketError, d->UnknownSocketErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
+
+ if (d->socketType == QAbstractSocket::TcpSocket) {
+ UINT32 capacity;
+ hr = d->inputBuffer->get_Capacity(&capacity);
+ if (FAILED(hr))
+ return false;
+ IInputStream *stream;
+ hr = d->tcp->get_InputStream(&stream);
+ if (FAILED(hr))
+ return false;
+ ByteArrayBuffer *buffer = static_cast<ByteArrayBuffer *>(d->inputBuffer.Get());
+ buffer->setEngine(this);
+ buffer->setInputStream(stream);
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> op;
+ hr = stream->ReadAsync(buffer, capacity, InputStreamOptions_Partial, &op);
+ if (FAILED(hr))
+ return false;
+ hr = op->put_Completed(Callback<SocketReadCompletedHandler>(&QNativeSocketEnginePrivate::handleReadyRead).Get());
+ if (FAILED(hr))
+ return false;
+ }
+ d->socketState = QAbstractSocket::ConnectedState;
+ return true;
+}
+
+bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ ComPtr<IHostName> hostAddress;
+ if (address != QHostAddress::Any && address != QHostAddress::AnyIPv4 && address != QHostAddress::AnyIPv6) {
+ ComPtr<IHostNameFactory> hostNameFactory;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ const QString addressString = address.toString();
+ HStringReference addressRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
+ hostNameFactory->CreateHostName(addressRef.Get(), &hostAddress);
+ }
+
+ HRESULT hr;
+ QString portQString = port ? QString::number(port) : QString();
+ HStringReference portString(reinterpret_cast<LPCWSTR>(portQString.utf16()));
+
+ ComPtr<IAsyncAction> op;
+ if (d->socketType == QAbstractSocket::TcpSocket) {
+ if (!d->tcpListener
+ && FAILED(RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(),
+ &d->tcpListener))) {
+ qWarning("Failed to create listener");
+ return false;
+ }
+
+ EventRegistrationToken token;
+ d->tcpListener->add_ConnectionReceived(Callback<ClientConnectedHandler>(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(), &token);
+ hr = d->tcpListener->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
+ if (FAILED(hr)) {
+ qWarning("Unable to bind"); // ### Set error message
+ return false;
+ }
+ } else if (d->socketType == QAbstractSocket::UdpSocket) {
+ hr = d->udp->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
+ if (FAILED(hr)) {
+ qWarning("unable to bind"); // ### Set error message
+ return false;
+ }
+ }
+
+ if (op) {
+ // Wait for connection to enter bound state - TODO: timeout, check result
+ while ((hr = op->GetResults()) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents();
+
+ d->socketState = QAbstractSocket::BoundState;
+ d->fetchConnectionParameters();
+ return true;
+ }
+
+ return false;
+}
+
+bool QNativeSocketEngine::listen()
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
+
+ if (d->tcpListener && d->socketDescriptor != -1) {
+ d->socketState = QAbstractSocket::ListeningState;
+ return true;
+ }
+ return false;
+}
+
+int QNativeSocketEngine::accept()
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
+ Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1);
+ Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1);
+
+ if (d->socketDescriptor == -1 || d->pendingConnections.isEmpty())
+ return -1;
+
+ // Start processing incoming data
+ if (d->socketType == QAbstractSocket::TcpSocket) {
+ IStreamSocket *socket = d->pendingConnections.takeFirst();
+
+ UINT32 capacity;
+ d->inputBuffer->get_Capacity(&capacity);
+ IInputStream *stream;
+ socket->get_InputStream(&stream);
+ // TODO: delete buffer and stream on socket close
+ ByteArrayBuffer *buffer = static_cast<ByteArrayBuffer *>(d->inputBuffer.Get());
+ buffer->setEngine(this);
+ buffer->setInputStream(stream);
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> op;
+ stream->ReadAsync(buffer, capacity, InputStreamOptions_Partial, &op);
+ op->put_Completed(Callback<SocketReadCompletedHandler>(&QNativeSocketEnginePrivate::handleReadyRead).Get());
+ d->currentConnections.append(socket);
+
+ SocketHandler *handler = gSocketHandler();
+ handler->pendingTcpSockets.insert(++handler->socketCount, socket);
+ return handler->socketCount;
+ }
+
+ return -1;
+}
+
+void QNativeSocketEngine::close()
+{
+ Q_D(QNativeSocketEngine);
+ if (d->socketDescriptor != -1) {
+ IClosable *socket = 0;
+ if (d->socketType == QAbstractSocket::TcpSocket)
+ d->tcp->QueryInterface(IID_PPV_ARGS(&socket));
+ else if (d->socketType == QAbstractSocket::UdpSocket)
+ d->udp->QueryInterface(IID_PPV_ARGS(&socket));
+
+ if (socket) {
+ d->closingDown = true;
+ socket->Close();
+ socket->Release();
+ closeNotification();
+ d->socketDescriptor = -1;
+ }
+ d->socketDescriptor = -1;
+ }
+ d->socketState = QAbstractSocket::UnconnectedState;
+ d->hasSetSocketError = false;
+ d->localPort = 0;
+ d->localAddress.clear();
+ d->peerPort = 0;
+ d->peerAddress.clear();
+}
+
+bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
+{
+ Q_UNUSED(groupAddress);
+ Q_UNUSED(iface);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
+{
+ Q_UNUSED(groupAddress);
+ Q_UNUSED(iface);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+QNetworkInterface QNativeSocketEngine::multicastInterface() const
+{
+ Q_UNIMPLEMENTED();
+ return QNetworkInterface();
+}
+
+bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
+{
+ Q_UNUSED(iface);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+qint64 QNativeSocketEngine::bytesAvailable() const
+{
+ Q_D(const QNativeSocketEngine);
+ if (d->socketType != QAbstractSocket::TcpSocket)
+ return -1;
+
+ if (d->inputBuffer) {
+ UINT32 len;
+ d->inputBuffer->get_Length(&len);
+ return len;
+ }
+
+ return -1;
+}
+
+qint64 QNativeSocketEngine::read(char *data, qint64 maxlen)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->socketType != QAbstractSocket::TcpSocket)
+ return -1;
+
+ ComPtr<IDataReaderStatics> dataReaderStatics;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &dataReaderStatics);
+ ComPtr<IDataReader> reader;
+
+ dataReaderStatics->FromBuffer(d->inputBuffer.Get(), &reader);
+
+ UINT32 bufferCapacity;
+ d->inputBuffer->get_Capacity(&bufferCapacity);
+ qint64 lengthToRead = maxlen < bufferCapacity ? maxlen : bufferCapacity;
+
+ UINT32 bufferLength;
+ d->inputBuffer->get_Length(&bufferLength);
+
+ lengthToRead = bufferLength < lengthToRead ? bufferLength : lengthToRead;
+ reader->ReadBytes(lengthToRead, (unsigned char*)data);
+ return lengthToRead;
+}
+
+template <typename T>
+static qint64 nativeWrite(T *socket, const char *data, qint64 len)
+{
+ ComPtr<IOutputStream> stream;
+ HRESULT hr = socket->get_OutputStream(&stream);
+ if (FAILED(hr))
+ return -1;
+ ComPtr<ByteArrayBuffer> buffer = Make<ByteArrayBuffer>(data, len);
+ ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
+ hr = stream->WriteAsync(buffer.Get(), &op);
+ if (FAILED(hr))
+ return -1;
+ UINT32 bytesWritten;
+ while ((hr = op->GetResults(&bytesWritten)) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ return bytesWritten;
+}
+
+qint64 QNativeSocketEngine::write(const char *data, qint64 len)
+{
+ Q_D(QNativeSocketEngine);
+ qint64 bytesWritten = -1;
+ if (d->socketType == QAbstractSocket::TcpSocket)
+ bytesWritten = nativeWrite(d->tcp, data, len);
+ else if (d->socketType == QAbstractSocket::UdpSocket)
+ bytesWritten = nativeWrite(d->udp, data, len);
+ if (bytesWritten != -1 && d->notifyOnWrite)
+ writeNotification();
+ return bytesWritten;
+
+}
+
+qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->socketType != QAbstractSocket::UdpSocket)
+ return -1;
+
+ QHostAddress returnAddress;
+ quint16 returnPort;
+
+ for (int i = 0; i < d->pendingDatagrams.size(); ++i) {
+ IDatagramSocketMessageReceivedEventArgs *arg = d->pendingDatagrams.at(i);
+ ComPtr<IHostName> remoteHost;
+ HSTRING remoteHostString;
+ HSTRING remotePort;
+ arg->get_RemoteAddress(&remoteHost);
+ arg->get_RemotePort(&remotePort);
+ remoteHost->get_CanonicalName(&remoteHostString);
+ returnAddress.setAddress(qt_QStringFromHSTRING(remoteHostString));
+ returnPort = qt_QStringFromHSTRING(remotePort).toInt();
+ ComPtr<IDataReader> reader;
+ arg->GetDataReader(&reader);
+ if (!reader)
+ continue;
+
+ BYTE buffer[1024];
+ reader->ReadBytes(maxlen, buffer);
+ *addr = returnAddress;
+ *port = returnPort;
+ arg = d->pendingDatagrams.takeFirst();
+
+ // TODO: fill data
+ Q_UNUSED(data);
+ arg->Release();
+ delete arg;
+ --i;
+ return maxlen;
+ }
+
+ return -1;
+}
+
+qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &addr, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->socketType != QAbstractSocket::UdpSocket)
+ return -1;
+
+ ComPtr<IHostName> remoteHost;
+ ComPtr<IHostNameFactory> hostNameFactory;
+ if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory))) {
+ qWarning("QNativeSocketEnginePrivate::nativeSendDatagram: could not obtain hostname factory");
+ return -1;
+ }
+ const QString addressString = addr.toString();
+ HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
+ hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
+
+ ComPtr<IAsyncOperation<IOutputStream *>> streamOperation;
+ ComPtr<IOutputStream> stream;
+ const QString portString = QString::number(port);
+ HStringReference portRef(reinterpret_cast<LPCWSTR>(portString.utf16()));
+ if (FAILED(d->udp->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation)))
+ return -1;
+ HRESULT hr;
+ while (hr = streamOperation->GetResults(&stream) == E_ILLEGAL_METHOD_CALL)
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ ComPtr<IDataWriterFactory> dataWriterFactory;
+ GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &dataWriterFactory);
+ ComPtr<IDataWriter> writer;
+ dataWriterFactory->CreateDataWriter(stream.Get(), &writer);
+ writer->WriteBytes(len, (unsigned char *)data);
+ return len;
+}
+
+bool QNativeSocketEngine::hasPendingDatagrams() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->pendingDatagrams.length() > 0;
+}
+
+qint64 QNativeSocketEngine::pendingDatagramSize() const
+{
+ Q_D(const QNativeSocketEngine);
+ qint64 ret = 0;
+ foreach (IDatagramSocketMessageReceivedEventArgs *arg, d->pendingDatagrams) {
+ ComPtr<IDataReader> reader;
+ UINT32 unconsumedBufferLength;
+ arg->GetDataReader(&reader);
+ if (!reader)
+ return -1;
+ reader->get_UnconsumedBufferLength(&unconsumedBufferLength);
+ ret += unconsumedBufferLength;
+ }
+ return ret;
+}
+
+qint64 QNativeSocketEngine::bytesToWrite() const
+{
+ return 0;
+}
+
+qint64 QNativeSocketEngine::receiveBufferSize() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->option(QAbstractSocketEngine::ReceiveBufferSocketOption);
+}
+
+void QNativeSocketEngine::setReceiveBufferSize(qint64 bufferSize)
+{
+ Q_D(QNativeSocketEngine);
+ d->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, bufferSize);
+}
+
+qint64 QNativeSocketEngine::sendBufferSize() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->option(QAbstractSocketEngine::SendBufferSocketOption);
+}
+
+void QNativeSocketEngine::setSendBufferSize(qint64 bufferSize)
+{
+ Q_D(QNativeSocketEngine);
+ d->setOption(QAbstractSocketEngine::SendBufferSocketOption, bufferSize);
+}
+
+int QNativeSocketEngine::option(QAbstractSocketEngine::SocketOption option) const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->option(option);
+}
+
+bool QNativeSocketEngine::setOption(QAbstractSocketEngine::SocketOption option, int value)
+{
+ Q_D(QNativeSocketEngine);
+ return d->setOption(option, value);
+}
+
+bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForRead(),
+ QAbstractSocket::UnconnectedState, false);
+
+ if (timedOut)
+ *timedOut = false;
+
+ QElapsedTimer timer;
+ timer.start();
+ while (msecs > timer.elapsed()) {
+ // Servers with active connections are ready for reading
+ if (!d->currentConnections.isEmpty())
+ return true;
+
+ // If we are a client, we are ready to read if our buffer has data
+ UINT32 length;
+ if (FAILED(d->inputBuffer->get_Length(&length)))
+ return false;
+ if (length)
+ return true;
+
+ // Nothing to do, wait for more events
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents|QEventLoop::WaitForMoreEvents);
+ }
+
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ QNativeSocketEnginePrivate::TimeOutErrorString);
+
+ if (timedOut)
+ *timedOut = true;
+ return false;
+}
+
+bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
+{
+ Q_UNUSED(msecs);
+ Q_UNUSED(timedOut);
+ return false;
+}
+
+bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut)
+{
+ Q_UNUSED(readyToRead);
+ Q_UNUSED(readyToWrite);
+ Q_UNUSED(checkRead);
+ Q_UNUSED(checkWrite);
+ Q_UNUSED(msecs);
+ Q_UNUSED(timedOut);
+ return false;
+}
+
+bool QNativeSocketEngine::isReadNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->notifyOnRead;
+}
+
+void QNativeSocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ d->notifyOnRead = enable;
+}
+
+bool QNativeSocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->notifyOnWrite;
+}
+
+void QNativeSocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ d->notifyOnWrite = enable;
+ if (enable && d->socketState == QAbstractSocket::ConnectedState) {
+ if (bytesToWrite())
+ return; // will be emitted as a result of bytes written
+ writeNotification();
+ d->notifyOnWrite = false;
+ }
+}
+
+bool QNativeSocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->notifyOnException;
+}
+
+void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ d->notifyOnException = enable;
+}
+
+bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol)
+{
+ Q_UNUSED(socketProtocol);
+ SocketHandler *handler = gSocketHandler();
+ switch (socketType) {
+ case QAbstractSocket::TcpSocket: {
+ if (FAILED(RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(),
+ reinterpret_cast<IInspectable **>(&tcp)))) {
+ qWarning("Failed to create StreamSocket instance");
+ return false;
+ }
+ socketDescriptor = ++handler->socketCount;
+ return true;
+ }
+ case QAbstractSocket::UdpSocket: {
+ if (FAILED(RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(),
+ reinterpret_cast<IInspectable **>(&udp)))) {
+ qWarning("Failed to create stream socket");
+ return false;
+ }
+ EventRegistrationToken token;
+ udp->add_MessageReceived(Callback<DatagramReceivedHandler>(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &token);
+ socketDescriptor = ++handler->socketCount;
+ return true;
+ }
+ default:
+ qWarning("Invalid socket type");
+ return false;
+ }
+ return false;
+}
+
+QNativeSocketEnginePrivate::QNativeSocketEnginePrivate()
+ : QAbstractSocketEnginePrivate()
+ , notifyOnRead(true)
+ , notifyOnWrite(true)
+ , notifyOnException(false)
+ , closingDown(false)
+ , socketDescriptor(-1)
+{
+ ComPtr<ByteArrayBuffer> buffer = Make<ByteArrayBuffer>(8192);
+ inputBuffer = buffer;
+}
+
+QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate()
+{
+}
+
+void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, ErrorString errorString) const
+{
+ if (hasSetSocketError) {
+ // Only set socket errors once for one engine; expect the
+ // socket to recreate its engine after an error. Note: There's
+ // one exception: SocketError(11) bypasses this as it's purely
+ // a temporary internal error condition.
+ // Another exception is the way the waitFor*() functions set
+ // an error when a timeout occurs. After the call to setError()
+ // they reset the hasSetSocketError to false
+ return;
+ }
+ if (error != QAbstractSocket::SocketError(11))
+ hasSetSocketError = true;
+
+ socketError = error;
+
+ switch (errorString) {
+ case NonBlockingInitFailedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket");
+ break;
+ case BroadcastingInitFailedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket");
+ break;
+ // should not happen anymore
+ case NoIpV6ErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
+ break;
+ case RemoteHostClosedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection");
+ break;
+ case TimeOutErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network operation timed out");
+ break;
+ case ResourceErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Out of resources");
+ break;
+ case OperationUnsupportedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation");
+ break;
+ case ProtocolUnsupportedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Protocol type not supported");
+ break;
+ case InvalidSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor");
+ break;
+ case HostUnreachableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Host unreachable");
+ break;
+ case NetworkUnreachableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network unreachable");
+ break;
+ case AccessErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Permission denied");
+ break;
+ case ConnectionTimeOutErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Connection timed out");
+ break;
+ case ConnectionRefusedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Connection refused");
+ break;
+ case AddressInuseErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The bound address is already in use");
+ break;
+ case AddressNotAvailableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The address is not available");
+ break;
+ case AddressProtectedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The address is protected");
+ break;
+ case DatagramTooLargeErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send");
+ break;
+ case SendDatagramErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to send a message");
+ break;
+ case ReceiveDatagramErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to receive a message");
+ break;
+ case WriteErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to write");
+ break;
+ case ReadErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network error");
+ break;
+ case PortInuseErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port");
+ break;
+ case NotSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Operation on non-socket");
+ break;
+ case InvalidProxyTypeString:
+ socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation");
+ break;
+ case TemporaryErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Temporary error");
+ break;
+ case UnknownSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unknown error");
+ break;
+ }
+}
+
+int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) const
+{
+ ComPtr<IStreamSocketControl> control;
+ if (socketType == QAbstractSocket::TcpSocket) {
+ if (FAILED(tcp->get_Control(&control))) {
+ qWarning("QNativeSocketEnginePrivate::option: Could not obtain socket control");
+ return -1;
+ }
+ }
+ switch (opt) {
+ case QAbstractSocketEngine::NonBlockingSocketOption:
+ case QAbstractSocketEngine::BroadcastSocketOption:
+ case QAbstractSocketEngine::ReceiveOutOfBandData:
+ return 1;
+ case QAbstractSocketEngine::SendBufferSocketOption:
+ if (socketType == QAbstractSocket::UdpSocket)
+ return -1;
+
+ UINT32 bufferSize;
+ if (FAILED(control->get_OutboundBufferSizeInBytes(&bufferSize))) {
+ qWarning("Could not obtain OutboundBufferSizeInBytes information vom socket control");
+ return -1;
+ }
+ return bufferSize;
+ case QAbstractSocketEngine::LowDelayOption:
+ if (socketType == QAbstractSocket::UdpSocket)
+ return -1;
+
+ boolean noDelay;
+ if (FAILED(control->get_NoDelay(&noDelay))) {
+ qWarning("Could not obtain NoDelay information from socket control");
+ return -1;
+ }
+ return noDelay;
+ case QAbstractSocketEngine::KeepAliveOption:
+ if (socketType == QAbstractSocket::UdpSocket)
+ return -1;
+
+ boolean keepAlive;
+ if (FAILED(control->get_KeepAlive(&keepAlive))) {
+ qWarning("Could not obtain KeepAlive information from socket control");
+ return -1;
+ }
+ return keepAlive;
+ case QAbstractSocketEngine::ReceiveBufferSocketOption:
+ case QAbstractSocketEngine::AddressReusable:
+ case QAbstractSocketEngine::BindExclusively:
+ case QAbstractSocketEngine::MulticastTtlOption:
+ case QAbstractSocketEngine::MulticastLoopbackOption:
+ case QAbstractSocketEngine::TypeOfServiceOption:
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption opt, int v)
+{
+ ComPtr<IStreamSocketControl> control;
+ if (socketType == QAbstractSocket::TcpSocket) {
+ if (FAILED(tcp->get_Control(&control))) {
+ qWarning("QNativeSocketEnginePrivate::setOption: Could not obtain socket control");
+ return false;
+ }
+ }
+ switch (opt) {
+ case QAbstractSocketEngine::NonBlockingSocketOption:
+ case QAbstractSocketEngine::BroadcastSocketOption:
+ case QAbstractSocketEngine::ReceiveOutOfBandData:
+ return v != 0;
+ case QAbstractSocketEngine::SendBufferSocketOption:
+ if (socketType == QAbstractSocket::UdpSocket)
+ return false;
+
+ if (FAILED(control->put_OutboundBufferSizeInBytes(v))) {
+ qWarning("Could not set OutboundBufferSizeInBytes");
+ return false;
+ }
+ return true;
+ case QAbstractSocketEngine::LowDelayOption: {
+ if (socketType == QAbstractSocket::UdpSocket)
+ return false;
+
+ boolean noDelay = v;
+ if (FAILED(control->put_NoDelay(noDelay))) {
+ qWarning("Could not obtain NoDelay information from socket control");
+ return false;
+ }
+ return true;
+ }
+ case QAbstractSocketEngine::KeepAliveOption: {
+ if (socketType == QAbstractSocket::UdpSocket)
+ return false;
+
+ boolean keepAlive = v;
+ if (FAILED(control->put_KeepAlive(keepAlive))) {
+ qWarning("Could not set KeepAlive value");
+ return false;
+ }
+ return true;
+ }
+ case QAbstractSocketEngine::ReceiveBufferSocketOption:
+ case QAbstractSocketEngine::AddressReusable:
+ case QAbstractSocketEngine::BindExclusively:
+ case QAbstractSocketEngine::MulticastTtlOption:
+ case QAbstractSocketEngine::MulticastLoopbackOption:
+ case QAbstractSocketEngine::TypeOfServiceOption:
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool QNativeSocketEnginePrivate::fetchConnectionParameters()
+{
+ localPort = 0;
+ localAddress.clear();
+ peerPort = 0;
+ peerAddress.clear();
+
+ if (socketType == QAbstractSocket::TcpSocket) {
+ ComPtr<IHostName> hostName;
+ HSTRING tmpHString;
+ ComPtr<IStreamSocketInformation> info;
+ if (FAILED(tcp->get_Information(&info))) {
+ qWarning("QNativeSocketEnginePrivate::fetchConnectionParameters: Could not obtain socket info");
+ return false;
+ }
+ info->get_LocalAddress(&hostName);
+ if (hostName) {
+ hostName->get_CanonicalName(&tmpHString);
+ localAddress.setAddress(qt_QStringFromHSTRING(tmpHString));
+ info->get_LocalPort(&tmpHString);
+ localPort = qt_QStringFromHSTRING(tmpHString).toInt();
+ }
+ if (!localPort && tcpListener) {
+ ComPtr<IStreamSocketListenerInformation> listenerInfo = 0;
+ tcpListener->get_Information(&listenerInfo);
+ listenerInfo->get_LocalPort(&tmpHString);
+ localPort = qt_QStringFromHSTRING(tmpHString).toInt();
+ localAddress == QHostAddress::Any;
+ }
+ info->get_RemoteAddress(&hostName);
+ if (hostName) {
+ hostName->get_CanonicalName(&tmpHString);
+ peerAddress.setAddress(qt_QStringFromHSTRING(tmpHString));
+ info->get_RemotePort(&tmpHString);
+ peerPort = qt_QStringFromHSTRING(tmpHString).toInt();
+ }
+ } else if (socketType == QAbstractSocket::UdpSocket) {
+ ComPtr<IHostName> hostName;
+ HSTRING tmpHString;
+ ComPtr<IDatagramSocketInformation> info;
+ if (FAILED(udp->get_Information(&info))) {
+ qWarning("QNativeSocketEnginePrivate::fetchConnectionParameters: Could not obtain socket information");
+ return false;
+ }
+ info->get_LocalAddress(&hostName);
+ if (hostName) {
+ hostName->get_CanonicalName(&tmpHString);
+ localAddress.setAddress(qt_QStringFromHSTRING(tmpHString));
+ info->get_LocalPort(&tmpHString);
+ localPort = qt_QStringFromHSTRING(tmpHString).toInt();
+ }
+
+ info->get_RemoteAddress(&hostName);
+ if (hostName) {
+ hostName->get_CanonicalName(&tmpHString);
+ peerAddress.setAddress(qt_QStringFromHSTRING(tmpHString));
+ info->get_RemotePort(&tmpHString);
+ peerPort = qt_QStringFromHSTRING(tmpHString).toInt();
+ }
+ }
+ return true;
+}
+
+HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener *listener, IStreamSocketListenerConnectionReceivedEventArgs *args)
+{
+ Q_Q(QNativeSocketEngine);
+ Q_ASSERT(tcpListener.Get() == listener);
+ IStreamSocket *socket;
+ args->get_Socket(&socket);
+ pendingConnections.append(socket);
+ q->connectionNotification();
+ q->readNotification();
+ return interruptEventDispatcher(0, Completed);
+}
+
+HRESULT QNativeSocketEnginePrivate::interruptEventDispatcher(IAsyncAction *, AsyncStatus)
+{
+ if (QThread *thread = QThread::currentThread()) {
+ if (QAbstractEventDispatcher *dispatcher = thread->eventDispatcher())
+ dispatcher->interrupt();
+ }
+ return S_OK;
+}
+
+HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncOperationWithProgress<IBuffer *, UINT32> *asyncInfo, AsyncStatus)
+{
+ ByteArrayBuffer *buffer = 0;
+ HRESULT hr = asyncInfo->GetResults((IBuffer **)&buffer);
+ if (FAILED(hr))
+ return hr;
+ UINT32 len;
+ buffer->get_Length(&len);
+ QNativeSocketEngine *q = buffer->engine();
+ if (!q)
+ return S_OK;
+ if (len > 0 && q->isReadNotificationEnabled()) {
+ q->readNotification();
+ }
+
+ // Continue reading ### TODO: read into offset!!!
+ UINT32 capacity;
+ buffer->get_Capacity(&capacity);
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> op;
+ if (SUCCEEDED(buffer->inputStream()->ReadAsync(buffer, capacity, InputStreamOptions_Partial, &op))) {
+ if (q)
+ return op->put_Completed(Callback<SocketReadCompletedHandler>(&QNativeSocketEnginePrivate::handleReadyRead).Get());
+ else
+ return op->put_Completed(nullptr);
+ }
+
+ return E_FAIL;
+}
+
+HRESULT QNativeSocketEnginePrivate::handleNewDatagram(IDatagramSocket *socket, IDatagramSocketMessageReceivedEventArgs *args)
+{
+ Q_Q(QNativeSocketEngine);
+ Q_ASSERT(udp == socket);
+ pendingDatagrams.append(args);
+ q->readNotification();
+
+ return S_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h
new file mode 100644
index 0000000000..b5be5fa830
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_winrt_p.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNATIVESOCKETENGINE_WINRT_P_H
+#define QNATIVESOCKETENGINE_WINRT_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 QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "QtNetwork/qhostaddress.h"
+#include "private/qabstractsocketengine_p.h"
+#include <wrl.h>
+#include <windows.networking.sockets.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNativeSocketEnginePrivate;
+
+class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
+{
+ Q_OBJECT
+public:
+ QNativeSocketEngine(QObject *parent = 0);
+ ~QNativeSocketEngine();
+
+ bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
+ bool initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
+
+ qintptr socketDescriptor() const;
+
+ bool isValid() const;
+
+ bool connectToHost(const QHostAddress &address, quint16 port);
+ bool connectToHostByName(const QString &name, quint16 port);
+ bool bind(const QHostAddress &address, quint16 port);
+ bool listen();
+ int accept();
+ void close();
+
+#ifndef QT_NO_NETWORKINTERFACE
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface multicastInterface() const;
+ bool setMulticastInterface(const QNetworkInterface &iface);
+#endif
+
+ qint64 bytesAvailable() const;
+
+ qint64 read(char *data, qint64 maxlen);
+ qint64 write(const char *data, qint64 len);
+
+ qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
+ quint16 *port = 0);
+ qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
+ quint16 port);
+ bool hasPendingDatagrams() const;
+ qint64 pendingDatagramSize() const;
+
+ qint64 bytesToWrite() const;
+
+ qint64 receiveBufferSize() const;
+ void setReceiveBufferSize(qint64 bufferSize);
+
+ qint64 sendBufferSize() const;
+ void setSendBufferSize(qint64 bufferSize);
+
+ int option(SocketOption option) const;
+ bool setOption(SocketOption option, int value);
+
+ bool waitForRead(int msecs = 30000, bool *timedOut = 0);
+ bool waitForWrite(int msecs = 30000, bool *timedOut = 0);
+ bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs = 30000, bool *timedOut = 0);
+
+ bool isReadNotificationEnabled() const;
+ void setReadNotificationEnabled(bool enable);
+ bool isWriteNotificationEnabled() const;
+ void setWriteNotificationEnabled(bool enable);
+ bool isExceptionNotificationEnabled() const;
+ void setExceptionNotificationEnabled(bool enable);
+
+private:
+ Q_DECLARE_PRIVATE(QNativeSocketEngine)
+ Q_DISABLE_COPY(QNativeSocketEngine)
+};
+
+class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QNativeSocketEngine)
+public:
+ QNativeSocketEnginePrivate();
+ ~QNativeSocketEnginePrivate();
+
+ qintptr socketDescriptor;
+
+ bool notifyOnRead, notifyOnWrite, notifyOnException;
+ bool closingDown;
+
+ enum ErrorString {
+ NonBlockingInitFailedErrorString,
+ BroadcastingInitFailedErrorString,
+ NoIpV6ErrorString,
+ RemoteHostClosedErrorString,
+ TimeOutErrorString,
+ ResourceErrorString,
+ OperationUnsupportedErrorString,
+ ProtocolUnsupportedErrorString,
+ InvalidSocketErrorString,
+ HostUnreachableErrorString,
+ NetworkUnreachableErrorString,
+ AccessErrorString,
+ ConnectionTimeOutErrorString,
+ ConnectionRefusedErrorString,
+ AddressInuseErrorString,
+ AddressNotAvailableErrorString,
+ AddressProtectedErrorString,
+ DatagramTooLargeErrorString,
+ SendDatagramErrorString,
+ ReceiveDatagramErrorString,
+ WriteErrorString,
+ ReadErrorString,
+ PortInuseErrorString,
+ NotSocketErrorString,
+ InvalidProxyTypeString,
+ TemporaryErrorString,
+
+ UnknownSocketErrorString = -1
+ };
+
+ void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
+
+ // native functions
+ int option(QNativeSocketEngine::SocketOption option) const;
+ bool setOption(QNativeSocketEngine::SocketOption option, int value);
+
+ bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
+
+ bool checkProxy(const QHostAddress &address);
+ bool fetchConnectionParameters();
+private:
+ union {
+ ABI::Windows::Networking::Sockets::IStreamSocket *tcp;
+ ABI::Windows::Networking::Sockets::IDatagramSocket *udp;
+ };
+ Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> tcpListener;
+ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> inputBuffer;
+ QList<ABI::Windows::Networking::Sockets::IDatagramSocketMessageReceivedEventArgs *> pendingDatagrams;
+ QList<ABI::Windows::Networking::Sockets::IStreamSocket *> pendingConnections;
+ QList<ABI::Windows::Networking::Sockets::IStreamSocket *> currentConnections;
+
+ HRESULT handleNewDatagram(ABI::Windows::Networking::Sockets::IDatagramSocket *socket,
+ ABI::Windows::Networking::Sockets::IDatagramSocketMessageReceivedEventArgs *args);
+ HRESULT handleClientConnection(ABI::Windows::Networking::Sockets::IStreamSocketListener *tcpListener,
+ ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args);
+ static HRESULT interruptEventDispatcher(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus);
+ static HRESULT handleReadyRead(ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer *, UINT32> *asyncInfo, ABI::Windows::Foundation::AsyncStatus);
+};
+
+QT_END_NAMESPACE
+
+#endif // QNATIVESOCKETENGINE_WINRT_P_H
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 6818ff6354..b62c4a6bef 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -735,9 +735,10 @@ void QSocks5SocketEnginePrivate::reauthenticate()
proxyInfo.setPassword(auth.password());
data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
- data->controlSocket->blockSignals(true);
- data->controlSocket->abort();
- data->controlSocket->blockSignals(false);
+ {
+ const QSignalBlocker blocker(data->controlSocket);
+ data->controlSocket->abort();
+ }
data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
} else {
// authentication failure
diff --git a/src/network/socket/qtcpsocket.cpp b/src/network/socket/qtcpsocket.cpp
index ce37401a89..e6f5887095 100644
--- a/src/network/socket/qtcpsocket.cpp
+++ b/src/network/socket/qtcpsocket.cpp
@@ -58,7 +58,7 @@
allows you to establish a TCP connection and transfer streams of
data. See the QAbstractSocket documentation for details.
- \b{Note:} TCP sockets cannot be opened in QIODevice::Unbuffered mode.
+ \note TCP sockets cannot be opened in QIODevice::Unbuffered mode.
\sa QTcpServer, QUdpSocket, QNetworkAccessManager,
{Fortune Server Example}, {Fortune Client Example},
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
index c0c6d750d9..7e3a54e303 100644
--- a/src/network/socket/socket.pri
+++ b/src/network/socket/socket.pri
@@ -24,8 +24,10 @@ SOURCES += socket/qabstractsocketengine.cpp \
socket/qlocalsocket.cpp \
socket/qlocalserver.cpp
-SOURCES += socket/qnativesocketengine.cpp
-HEADERS += socket/qnativesocketengine_p.h
+!winrt {
+ SOURCES += socket/qnativesocketengine.cpp
+ HEADERS += socket/qnativesocketengine_p.h
+}
unix: {
SOURCES += socket/qnativesocketengine_unix.cpp \
@@ -36,11 +38,20 @@ unix: {
unix:HEADERS += \
socket/qnet_unix_p.h
-win32:SOURCES += socket/qnativesocketengine_win.cpp \
+win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp \
socket/qlocalsocket_win.cpp \
socket/qlocalserver_win.cpp
-win32:!wince*: LIBS_PRIVATE += -ladvapi32
+win32:!wince*:!winrt:LIBS_PRIVATE += -ladvapi32
+
+winrt {
+ SOURCES += socket/qnativesocketengine_winrt.cpp \
+ socket/qlocalsocket_tcp.cpp \
+ socket/qlocalserver_tcp.cpp
+ HEADERS += socket/qnativesocketengine_winrt_p.h
+
+ DEFINES += QT_LOCALSOCKET_TCP
+}
wince*: {
SOURCES -= socket/qlocalsocket_win.cpp \
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 5b7274e3e6..b555c885b1 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -129,7 +129,7 @@ QT_BEGIN_NAMESPACE
\value SecureProtocols The default option, using protocols known to be secure;
currently behaves like TlsV1SslV3.
- Note: most servers using SSL understand both versions (2 and 3),
+ \note most servers using SSL understand both versions (2 and 3),
but it is recommended to use the latest version only for security
reasons. However, SSL and TLS are not compatible with each other:
if you get unexpected handshake failures, verify that you chose
@@ -176,7 +176,7 @@ QT_BEGIN_NAMESPACE
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
+ \note Availability of above options depends on the version of the SSL
backend in use.
*/
diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp
index cdb0ed9063..bb5d93e528 100644
--- a/src/network/ssl/qsslcipher.cpp
+++ b/src/network/ssl/qsslcipher.cpp
@@ -79,6 +79,26 @@ QSslCipher::QSslCipher()
/*!
Constructs a QSslCipher object for the cipher determined by \a
+ name. The constructor accepts only supported ciphers (i.e., the
+ \a name must identify a cipher in the list of ciphers returned by
+ QSslSocket::supportedCiphers()).
+
+ You can call isNull() after construction to check if \a name
+ correctly identified a supported cipher.
+*/
+QSslCipher::QSslCipher(const QString &name)
+ : d(new QSslCipherPrivate)
+{
+ foreach (const QSslCipher &cipher, QSslSocket::supportedCiphers()) {
+ if (cipher.name() == name) {
+ *this = cipher;
+ return;
+ }
+ }
+}
+
+/*!
+ Constructs a QSslCipher object for the cipher determined by \a
name and \a protocol. The constructor accepts only supported
ciphers (i.e., the \a name and \a protocol must identify a cipher
in the list of ciphers returned by
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
index e351d7949b..f6ca033ede 100644
--- a/src/network/ssl/qsslcipher.h
+++ b/src/network/ssl/qsslcipher.h
@@ -57,6 +57,7 @@ class Q_NETWORK_EXPORT QSslCipher
{
public:
QSslCipher();
+ explicit QSslCipher(const QString &name);
QSslCipher(const QString &name, QSsl::SslProtocol protocol);
QSslCipher(const QSslCipher &other);
~QSslCipher();
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 4aad7c04c5..14881931af 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -52,6 +53,9 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
|QSsl::SslOptionDisableCompression
|QSsl::SslOptionDisableSessionPersistence;
+const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
+const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
+
/*!
\class QSslConfiguration
\brief The QSslConfiguration class holds the configuration and state of an SSL connection
@@ -113,6 +117,33 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
*/
/*!
+ \enum QSslConfiguration::NextProtocolNegotiationStatus
+
+ Describes the status of the Next Protocol Negotiation (NPN).
+
+ \value NextProtocolNegotiationNone No application protocol
+ has been negotiated (yet).
+
+ \value NextProtocolNegotiationNegotiated A next protocol
+ has been negotiated (see nextNegotiatedProtocol()).
+
+ \value NextProtocolNegotiationUnsupported The client and
+ server could not agree on a common next application protocol.
+*/
+
+/*!
+ \variable QSslConfiguration::NextProtocolSpdy3_0
+ \brief The value used for negotiating SPDY 3.0 during the Next
+ Protocol Negotiation.
+*/
+
+/*!
+ \variable QSslConfiguration::NextProtocolHttp1_1
+ \brief The value used for negotiating HTTP 1.1 during the Next
+ Protocol Negotiation.
+*/
+
+/*!
Constructs an empty SSL configuration. This configuration contains
no valid settings and the state will be empty. isNull() will
return true after this constructor is called.
@@ -185,7 +216,10 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
d->sslOptions == other.d->sslOptions &&
d->sslSession == other.d->sslSession &&
- d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint;
+ d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint &&
+ d->nextAllowedProtocols == other.d->nextAllowedProtocols &&
+ d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
+ d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus;
}
/*!
@@ -221,7 +255,10 @@ bool QSslConfiguration::isNull() const
d->peerCertificateChain.count() == 0 &&
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
- d->sslSessionTicketLifeTimeHint == -1);
+ d->sslSessionTicketLifeTimeHint == -1 &&
+ d->nextAllowedProtocols.isEmpty() &&
+ d->nextNegotiatedProtocol.isNull() &&
+ d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone);
}
/*!
@@ -607,7 +644,7 @@ bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
used or QSsl::SslOptionDisableSessionPersistence was not turned off,
this function returns an empty QByteArray.
- \b{Note:} When persisting the session ticket to disk or similar, be
+ \note When persisting the session ticket 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.
@@ -653,6 +690,71 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const
}
/*!
+ \since 5.3
+
+ This function returns the protocol negotiated with the server
+ if the Next Protocol Negotiation (NPN) TLS extension was enabled.
+ In order for the NPN extension to be enabled, setAllowedNextProtocols()
+ needs to be called explicitly before connecting to the server.
+
+ If no protocol could be negotiated or the extension was not enabled,
+ this function returns a QByteArray which is null.
+
+ \sa setAllowedNextProtocols(), nextProtocolNegotiationStatus()
+ */
+QByteArray QSslConfiguration::nextNegotiatedProtocol() const
+{
+ return d->nextNegotiatedProtocol;
+}
+
+/*!
+ \since 5.3
+
+ This function sets the allowed \a protocols to be negotiated with the
+ server through the Next Protocol Negotiation (NPN) TLS extension; each
+ element in \a protocols must define one allowed protocol.
+ The function must be called explicitly before connecting to send the NPN
+ extension in the SSL handshake.
+ Whether or not the negotiation succeeded can be queried through
+ nextProtocolNegotiationStatus().
+
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ */
+void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
+{
+ d->nextAllowedProtocols = protocols;
+}
+
+/*!
+ \since 5.3
+
+ This function returns the allowed protocols to be negotiated with the
+ server through the Next Protocol Negotiation (NPN) TLS extension, as set
+ by setAllowedNextProtocols().
+
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ */
+QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
+{
+ return d->nextAllowedProtocols;
+}
+
+/*!
+ \since 5.3
+
+ This function returns the status of the Next Protocol Negotiation (NPN).
+ If the feature has not been enabled through setAllowedNextProtocols(),
+ this function returns NextProtocolNegotiationNone.
+ The status will be set before emitting the encrypted() signal.
+
+ \sa setAllowedNextProtocols(), allowedNextProtocols(), nextNegotiatedProtocol(), QSslConfiguration::NextProtocolNegotiationStatus
+ */
+QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocolNegotiationStatus() const
+{
+ return d->nextProtocolNegotiationStatus;
+}
+
+/*!
Returns the default SSL configuration to be used in new SSL
connections.
@@ -663,7 +765,7 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const
\li protocol SecureProtocols (meaning either TLS 1.0 or SSL 3 will be used)
\li the system's default CA certificate list
\li the cipher list equal to the list of the SSL libraries'
- supported SSL ciphers
+ supported SSL ciphers that are 128 bits or more
\endlist
\sa QSslSocket::supportedCiphers(), setDefaultConfiguration()
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index a48eceb63e..587187ca06 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -131,6 +132,21 @@ public:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
+ enum NextProtocolNegotiationStatus {
+ NextProtocolNegotiationNone,
+ NextProtocolNegotiationNegotiated,
+ NextProtocolNegotiationUnsupported
+ };
+
+ void setAllowedNextProtocols(QList<QByteArray> protocols);
+ QList<QByteArray> allowedNextProtocols() const;
+
+ QByteArray nextNegotiatedProtocol() const;
+ NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
+
+ static const char NextProtocolSpdy3_0[];
+ static const char NextProtocolHttp1_1[];
+
private:
friend class QSslSocket;
friend class QSslConfigurationPrivate;
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 71ee8d2bfe..d183c3335c 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -86,7 +87,8 @@ public:
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
sslOptions(QSslConfigurationPrivate::defaultSslOptions),
- sslSessionTicketLifeTimeHint(-1)
+ sslSessionTicketLifeTimeHint(-1),
+ nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone)
{ }
QSslCertificate peerCertificate;
@@ -114,6 +116,10 @@ public:
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
+ QList<QByteArray> nextAllowedProtocols;
+ QByteArray nextNegotiatedProtocol;
+ QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;
+
// 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 adf42fb79a..1634ba0649 100644
--- a/src/network/ssl/qsslcontext.cpp
+++ b/src/network/ssl/qsslcontext.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -263,6 +264,45 @@ init_context:
return sslContext;
}
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+
+static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+ QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg);
+
+ // comment out to debug:
+// QList<QByteArray> supportedVersions;
+// for (unsigned int i = 0; i < inlen; ) {
+// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]);
+// supportedVersions << version;
+// i += in[i] + 1;
+// }
+
+ int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
+ switch (proto) {
+ case OPENSSL_NPN_UNSUPPORTED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNone;
+ break;
+ case OPENSSL_NPN_NEGOTIATED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated;
+ break;
+ case OPENSSL_NPN_NO_OVERLAP:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported;
+ break;
+ default:
+ qWarning("OpenSSL sent unknown NPN status");
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+QSslContext::NPNContext QSslContext::npnContext() const
+{
+ return m_npnContext;
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
// Needs to be deleted by caller
SSL* QSslContext::createSsl()
{
@@ -283,13 +323,33 @@ SSL* QSslContext::createSsl()
session = 0;
}
}
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols;
+ if (!protocols.isEmpty()) {
+ m_supportedNPNVersions.clear();
+ for (int a = 0; a < protocols.count(); ++a) {
+ if (protocols.at(a).size() > 255) {
+ qWarning() << "TLS NPN extension" << protocols.at(a)
+ << "is too long and will be truncated to 255 characters.";
+ protocols[a] = protocols.at(a).left(255);
+ }
+ m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a));
+ }
+ m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
+ m_npnContext.len = m_supportedNPNVersions.count();
+ m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
+ q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
+ }
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
return ssl;
}
// We cache exactly one session here
bool QSslContext::cacheSession(SSL* ssl)
{
- // dont cache the same session again
+ // don't cache the same session again
if (session && session == q_SSL_get_session(ssl))
return true;
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
index 2b596798a6..20b27c1ce7 100644
--- a/src/network/ssl/qsslcontext_p.h
+++ b/src/network/ssl/qsslcontext_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -72,6 +73,21 @@ public:
QByteArray sessionASN1() const;
void setSessionASN1(const QByteArray &sessionASN1);
int sessionTicketLifeTimeHint() const;
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ // must be public because we want to use it from an OpenSSL callback
+ struct NPNContext {
+ NPNContext() : data(0),
+ len(0),
+ status(QSslConfiguration::NextProtocolNegotiationNone)
+ { }
+ unsigned char *data;
+ unsigned short len;
+ QSslConfiguration::NextProtocolNegotiationStatus status;
+ };
+ NPNContext npnContext() const;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
protected:
QSslContext();
@@ -84,6 +100,10 @@ private:
QSslError::SslError errorCode;
QString errorStr;
QSslConfiguration sslConfiguration;
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ QByteArray m_supportedNPNVersions;
+ NPNContext m_npnContext;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
};
#endif // QT_NO_SSL
diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp
index cf62f44855..95eed6e4b3 100644
--- a/src/network/ssl/qsslkey.cpp
+++ b/src/network/ssl/qsslkey.cpp
@@ -256,7 +256,7 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
a valid key.
*/
QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
- QSsl::KeyType type, const QByteArray &passPhrase)
+ QSsl::KeyType type, const QByteArray &passPhrase)
: d(new QSslKeyPrivate)
{
QByteArray encoded;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 38b493a769..c5ae517fb0 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -282,7 +283,7 @@
\a errors contains one or more errors that prevent QSslSocket from
verifying the identity of the peer.
- Note: You cannot use Qt::QueuedConnection when connecting to this signal,
+ \note You cannot use Qt::QueuedConnection when connecting to this signal,
or calling QSslSocket::ignoreSslErrors() will have no effect.
\sa peerVerifyError()
@@ -399,7 +400,7 @@ void QSslSocket::resume()
\snippet code/src_network_ssl_qsslsocket.cpp 3
- \b{Note:} The example above shows that text can be written to
+ \note The example above shows that text can be written to
the socket immediately after requesting the encrypted connection,
before the encrypted() signal has been emitted. In such cases, the
text is queued in the object and written to the socket \e after
@@ -469,7 +470,7 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port,
The socket is opened in the mode specified by \a openMode, and
enters the socket state specified by \a state.
- \b{Note:} It is not possible to initialize two sockets with the same
+ \note It is not possible to initialize two sockets with the same
native socket descriptor.
\sa socketDescriptor()
@@ -905,6 +906,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.sslOptions = configuration.d->sslOptions;
d->configuration.sslSession = configuration.sessionTicket();
d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
+ d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols();
+ d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol();
+ d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
@@ -1195,12 +1199,9 @@ void QSslSocket::setCiphers(const QString &ciphers)
Q_D(QSslSocket);
d->configuration.ciphers.clear();
foreach (const QString &cipherName, ciphers.split(QLatin1String(":"),QString::SkipEmptyParts)) {
- for (int i = 0; i < 3; ++i) {
- // ### Crude
- QSslCipher cipher(cipherName, QSsl::SslProtocol(i));
- if (!cipher.isNull())
- d->configuration.ciphers << cipher;
- }
+ QSslCipher cipher(cipherName);
+ if (!cipher.isNull())
+ d->configuration.ciphers << cipher;
}
}
@@ -1953,6 +1954,7 @@ void QSslSocketPrivate::init()
*/
QList<QSslCipher> QSslSocketPrivate::defaultCiphers()
{
+ QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
return globalData()->config->ciphers;
}
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 69b9e53884..3421154114 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -596,6 +596,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
SSL *mySsl = q_SSL_new(myCtx);
QList<QSslCipher> ciphers;
+ QList<QSslCipher> defaultCiphers;
STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl);
for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
@@ -603,8 +604,11 @@ void QSslSocketPrivate::resetDefaultCiphers()
if (cipher->valid) {
QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
if (!ciph.isNull()) {
+ // Unconditionally exclude ADH ciphers since they offer no MITM protection
if (!ciph.name().toLower().startsWith(QLatin1String("adh")))
ciphers << ciph;
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
}
}
}
@@ -614,7 +618,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
q_SSL_free(mySsl);
setDefaultSupportedCiphers(ciphers);
- setDefaultCiphers(ciphers);
+ setDefaultCiphers(defaultCiphers);
}
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
@@ -1482,6 +1486,15 @@ void QSslSocketBackendPrivate::continueHandshake()
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ const unsigned char *proto;
+ unsigned int proto_len;
+ q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
+ QByteArray nextProtocol(reinterpret_cast<const char *>(proto), proto_len);
+ configuration.nextNegotiatedProtocol = nextProtocol;
+ configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
connectionEncrypted = true;
emit q->encrypted();
if (autoStartHandshake && pendingClose) {
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index ddf53f18f4..79bce22b0d 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -346,6 +347,20 @@ 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)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
+ const unsigned char *in, in, unsigned int inlen, inlen,
+ const unsigned char *client, client, unsigned int client_len, client_len,
+ return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
+ const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
#define RESOLVEFUNC(func) \
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
@@ -815,6 +830,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSLeay_version)
RESOLVEFUNC(i2d_SSL_SESSION)
RESOLVEFUNC(d2i_SSL_SESSION)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ RESOLVEFUNC(SSL_select_next_proto)
+ RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
+ RESOLVEFUNC(SSL_get0_next_proto_negotiated)
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
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 7e1a1c983c..500fe9493b 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -79,13 +80,13 @@ QT_BEGIN_NAMESPACE
// **************** Shared declarations ******************
// ret func(arg)
-# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg) { \
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg) { \
if (Q_UNLIKELY(!_q_##func)) { \
qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
+ err; \
} \
funcret _q_##func(a); \
}
@@ -180,8 +181,8 @@ QT_BEGIN_NAMESPACE
// **************** Static declarations ******************
// ret func(arg)
-# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
- ret q_##func(arg) { funcret func(a); }
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ ret q_##func(arg) { funcret func(a); }
// ret func(arg1, arg2)
# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
@@ -384,7 +385,7 @@ int q_X509_cmp(X509 *a, X509 *b);
#ifdef SSLEAY_MACROS
void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x);
#define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \
- (d2i_of_void *)q_d2i_X509,(char *)x509)
+ (d2i_of_void *)q_d2i_X509,(char *)x509)
#else
X509 *q_X509_dup(X509 *a);
#endif
@@ -429,22 +430,22 @@ STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
#ifdef SSLEAY_MACROS
-int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp);
-int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp);
+int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp);
+int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp);
RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length);
DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
-#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \
+#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \
(RSA *)q_PEM_ASN1_read_bio( \
(void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u)
-#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \
+#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \
(DSA *)q_PEM_ASN1_read_bio( \
(void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u)
-#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
- PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\
- bp,(char *)x,enc,kstr,klen,cb,u)
-#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
- PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
- bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
#endif
#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
@@ -461,9 +462,9 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
#define q_X509_get_notAfter(x) X509_get_notAfter(x)
#define q_X509_get_notBefore(x) X509_get_notBefore(x)
#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
- (char *)(rsa))
+ (char *)(rsa))
#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
- (char *)(dsa))
+ (char *)(dsa))
#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf()
void q_OPENSSL_add_all_algorithms_noconf();
void q_OPENSSL_add_all_algorithms_conf();
@@ -473,6 +474,20 @@ 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);
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ const unsigned char *client, unsigned int client_len);
+void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg),
+ void *arg);
+void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
+ unsigned *len);
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
// Helper function
class QDateTime;
QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);