summaryrefslogtreecommitdiffstats
path: root/src/network/access/qhttpnetworkconnectionchannel.cpp
diff options
context:
space:
mode:
authorPeter Hartmann <phartmann@blackberry.com>2014-01-22 14:54:21 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-19 21:44:15 +0100
commit1de244ea65f1b40c488fe92b29170c1b1d447233 (patch)
treea01d233e33847cf8709b6130ad7fa4656ff348de /src/network/access/qhttpnetworkconnectionchannel.cpp
parent42789d04a3078652594b8e06f520d7dd5e8021c5 (diff)
network: add support for the SPDY protocol
Currently the only supported SPDY version is 3.0. The feature needs to be enabled explicitly via QNetworkRequest::SpdyAllowedAttribute. Whether SPDY actually was used can be determined via QNetworkRequest::SpdyWasUsedAttribute from a QNetworkReply once it has been started (i.e. after the encrypted() signal has been received). Whether SPDY can be used will be determined during the SSL handshake through the TLS NPN extension (see separate commit). The following things from SPDY have not been enabled currently: * server push is not implemented, it has never been seen in the wild; in that case we just reject a stream pushed by the server, which is legit. * settings are not persisted across SPDY sessions. In practice this means that the server sends a small message upon session start telling us e.g. the number of concurrent connections. * SSL client certificates are not supported. Task-number: QTBUG-18714 [ChangeLog][QtNetwork] Added support for the SPDY protocol (version 3.0). Change-Id: I81bbe0495c24ed84e9cf8af3a9dbd63ca1e93d0d Reviewed-by: Richard J. Moore <rich@kde.org>
Diffstat (limited to 'src/network/access/qhttpnetworkconnectionchannel.cpp')
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp130
1 files changed, 118 insertions, 12 deletions
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6f06c18732..fb40958178 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -50,6 +50,7 @@
#ifndef QT_NO_HTTP
#include <private/qhttpprotocolhandler_p.h>
+#include <private/qspdyprotocolhandler_p.h>
#ifndef QT_NO_SSL
# include <QtNetwork/qsslkey.h>
@@ -166,9 +167,12 @@ void QHttpNetworkConnectionChannel::init()
if (!sslConfiguration.isNull())
sslSocket->setSslConfiguration(sslConfiguration);
+ } else {
+#endif // QT_NO_SSL
+ protocolHandler.reset(new QHttpProtocolHandler(this));
+#ifndef QT_NO_SSL
}
#endif
- protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_NETWORKPROXY
if (proxy.type() != QNetworkProxy::NoProxy)
@@ -879,6 +883,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
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);
@@ -889,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
@@ -905,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)
@@ -927,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();
}