diff options
author | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2017-07-27 14:34:39 +0200 |
---|---|---|
committer | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2017-08-27 04:54:55 +0000 |
commit | 53357f01561d7c2b50e0a656ca250f5e3c1af923 (patch) | |
tree | 28ac5a34217efb50a4844d498794baedc72d66ae /src/network/access/qhttpnetworkconnection.cpp | |
parent | fabedd399ebe4d28d6eb62c8a863f1bbcce78d3a (diff) |
HTTP/2 - implement the proper 'h2c' (protocol upgrade)
Without TLS (and thus ALPN/NPN negotiation) HTTP/2 requires
a protocol upgrade procedure, as described in RFC 7540, 3.2.
We start as HTTP/1.1 (and thus we create QHttpProtocolHandler first),
augmenting the headers we send with 'Upgrade: h2c'. In case
we receive HTTP/1.1 response with status code 101 ('Switching
Protocols'), we continue as HTTP/2 session, creating QHttp2ProtocolHandler
and pretending the first request we sent was HTTP/2 request
on a real HTTP/2 stream. If the first response is something different
from 101, we continue as HTTP/1.1. This change also required
auto-test update: our toy-server now has to respond to
the initial HTTP/1.1 request on a platform without ALPN/NPN.
As a bonus a subtle flakyness in 'goaway' auto-test went
away (well, it was fixed).
[ChangeLog][QtNetwork][HTTP/2] In case of clear text HTTP/2 we
now initiate a required protocol upgrade procedure instead of
'H2Direct' connection.
Task-number: QTBUG-61397
Change-Id: I573fa304fdaf661490159037dc47775d97c8ea5b
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 35 |
1 files changed, 30 insertions, 5 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index ae30d3a8cf..0b474ba116 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -627,7 +627,8 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor if (request.isPreConnect()) preConnectRequests++; - if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP) { + if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP + || (!encrypt && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 && !channels[0].switchedToHttp2)) { switch (request.priority()) { case QHttpNetworkRequest::HighPriority: highPriorityQueue.prepend(pair); @@ -638,7 +639,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor break; } } - else { // SPDY, HTTP/2 + else { // SPDY, HTTP/2 ('h2' mode) if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair); @@ -672,6 +673,25 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor return reply; } +void QHttpNetworkConnectionPrivate::fillHttp2Queue() +{ + for (auto &pair : highPriorityQueue) { + if (!pair.second->d_func()->requestIsPrepared) + prepareRequest(pair); + channels[0].spdyRequestsToSend.insertMulti(QHttpNetworkRequest::HighPriority, pair); + } + + highPriorityQueue.clear(); + + for (auto &pair : lowPriorityQueue) { + if (!pair.second->d_func()->requestIsPrepared) + prepareRequest(pair); + channels[0].spdyRequestsToSend.insertMulti(pair.first.priority(), pair); + } + + lowPriorityQueue.clear(); +} + void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair) { Q_Q(QHttpNetworkConnection); @@ -1047,8 +1067,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() } case QHttpNetworkConnection::ConnectionTypeHTTP2: case QHttpNetworkConnection::ConnectionTypeSPDY: { - - if (channels[0].spdyRequestsToSend.isEmpty()) + if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2) return; if (networkLayerState == IPv4) @@ -1057,7 +1076,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol; channels[0].ensureConnection(); if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState - && !channels[0].pendingEncrypt) + && !channels[0].pendingEncrypt && channels[0].spdyRequestsToSend.size()) channels[0].sendRequest(); break; } @@ -1355,6 +1374,12 @@ QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest return d->queueRequest(request); } +void QHttpNetworkConnection::fillHttp2Queue() +{ + Q_D(QHttpNetworkConnection); + d->fillHttp2Queue(); +} + bool QHttpNetworkConnection::isSsl() const { Q_D(const QHttpNetworkConnection); |