Revert "Implement protocol upgrade for HTTP/2 enabled requests"

This reverts commit 12d71f4ea2.
This change is breaking a build + incomplete as my test revealed.
Will have to re-try later.

Change-Id: I7ea089093a832aa5822caaaac56e62f5fda4df17
Reviewed-by: Liang Qi <liang.qi@qt.io>
This commit is contained in:
Timur Pocheptsov 2016-08-04 09:34:50 +02:00
parent 4a40c717f3
commit 2d1f137173
8 changed files with 28 additions and 149 deletions

View File

@ -37,12 +37,9 @@
**
****************************************************************************/
#include <QtCore/qbytearray.h>
#include <QtCore/qstring.h>
#include "private/qhttpnetworkrequest_p.h"
#include "http2protocol_p.h"
#include "http2frames_p.h"
QT_BEGIN_NAMESPACE
@ -60,37 +57,6 @@ const char Http2clientPreface[clientPrefaceLength] =
0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
QByteArray qt_default_SETTINGS_to_Base64()
{
FrameWriter frame(qt_default_SETTINGS_frame());
// SETTINGS frame's payload consists of pairs:
// 2-byte-identifier | 4-byte-value - multiple of 6.
// Also it's allowed to be empty.
Q_ASSERT(!(frame.payloadSize() % 6));
const char *src = reinterpret_cast<const char *>(&frame.rawFrameBuffer()[frameHeaderSize]);
const QByteArray wrapper(QByteArray::fromRawData(src, int(frame.payloadSize())));
// 3.2.1
// The content of the HTTP2-Settings header field is the payload
// of a SETTINGS frame (Section 6.5), encoded as a base64url string
// (that is, the URL- and filename-safe Base64 encoding described in
// Section 5 of [RFC4648], with any trailing '=' characters omitted).
return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
}
void qt_add_ProtocolUpgradeRequest(QHttpNetworkRequest &request)
{
// RFC 2616, 14.10
QByteArray value(request.headerField("Connection"));
if (value.size())
value += ", ";
value += "Upgrade, HTTP2-Settings";
request.setHeaderField("Connection", value);
// This we just (re)write.
request.setHeaderField("Upgrade", "h2c");
// This we just (re)write.
request.setHeaderField("HTTP2-Settings", qt_default_SETTINGS_to_Base64());
}
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
QString &errorMessage)
@ -185,19 +151,6 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
return error;
}
FrameWriter qt_default_SETTINGS_frame()
{
// 6.5 SETTINGS
FrameWriter frame(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
// MAX frame size (16 kb), disable PUSH
frame.append(Settings::MAX_FRAME_SIZE_ID);
frame.append(quint32(maxFrameSize));
frame.append(Settings::ENABLE_PUSH_ID);
frame.append(quint32(0));
return frame;
}
}
QT_END_NAMESPACE

View File

@ -59,8 +59,6 @@
QT_BEGIN_NAMESPACE
class QHttpNetworkRequest;
class QByteArray;
class QString;
namespace Http2
@ -130,7 +128,6 @@ enum Http2PredefinedParameters
};
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
void qt_add_ProtocolUpgradeRequest(QHttpNetworkRequest &request);
enum class FrameStatus
{
@ -169,8 +166,6 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &er
QString qt_error_string(quint32 errorCode);
QNetworkReply::NetworkError qt_error(quint32 errorCode);
class FrameWriter qt_default_SETTINGS_frame();
}
Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2)

View File

@ -40,7 +40,7 @@
#include "qhttpnetworkconnection_p.h"
#include "qhttp2protocolhandler_p.h"
#if !defined(QT_NO_HTTP)
#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
#include "http2/bitstreams_p.h"
@ -54,7 +54,6 @@
#include <QtCore/qurl.h>
#include <algorithm>
#include <utility>
#include <vector>
QT_BEGIN_NAMESPACE
@ -134,28 +133,6 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
continuedFrames.reserve(20);
}
QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel,
const HttpMessagePair &message)
: QAbstractProtocolHandler(channel),
prefaceSent(false),
waitingForSettingsACK(false),
decoder(HPack::FieldLookupTable::DefaultSize),
encoder(HPack::FieldLookupTable::DefaultSize, true)
{
// That's a protocol upgrade scenario - 3.2.
//
// We still have to send settings and the preface
// (though SETTINGS was a part of the first HTTP/1.1
// request "HTTP2-Settings" field).
//
// We pass 'false' for upload data, this was done by HTTP/1.1 protocol
// handler for us while sending the first request.
const quint32 initialStreamID = createNewStream(message, false);
Q_ASSERT(initialStreamID == 1);
Stream &stream = activeStreams[initialStreamID];
stream.state = Stream::halfClosedLocal;
}
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
{
auto data = qobject_cast<QNonContiguousByteDevice *>(sender());
@ -270,7 +247,7 @@ bool QHttp2ProtocolHandler::sendRequest()
auto it = requests.begin();
m_channel->state = QHttpNetworkConnectionChannel::WritingState;
for (quint32 i = 0; i < streamsToUse; ++i) {
const qint32 newStreamID = createNewStream(*it, true /* upload data */);
const qint32 newStreamID = createNewStream(*it);
if (!newStreamID) {
// TODO: actually we have to open a new connection.
qCCritical(QT_HTTP2, "sendRequest: out of stream IDs");
@ -301,6 +278,7 @@ bool QHttp2ProtocolHandler::sendRequest()
return true;
}
bool QHttp2ProtocolHandler::sendClientPreface()
{
// 3.5 HTTP/2 Connection Preface
@ -315,8 +293,12 @@ bool QHttp2ProtocolHandler::sendClientPreface()
return false;
// 6.5 SETTINGS
outboundFrame = Http2::qt_default_SETTINGS_frame();
Q_ASSERT(outboundFrame.payloadSize());
outboundFrame.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
// MAX frame size (16 kb), disable PUSH
outboundFrame.append(Settings::MAX_FRAME_SIZE_ID);
outboundFrame.append(quint32(Http2::maxFrameSize));
outboundFrame.append(Settings::ENABLE_PUSH_ID);
outboundFrame.append(quint32(0));
if (!outboundFrame.write(*m_socket))
return false;
@ -1040,8 +1022,7 @@ void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, QNetworkReply:
emit httpReply->finishedWithError(error, message);
}
quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message,
bool uploadData)
quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
{
const qint32 newStreamID = allocateStreamID();
if (!newStreamID)
@ -1062,12 +1043,10 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message,
streamInitialSendWindowSize,
streamInitialRecvWindowSize);
if (uploadData) {
if (auto src = newStream.data()) {
connect(src, SIGNAL(readyRead()), this,
SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
src->setProperty("HTTP2StreamID", newStreamID);
}
if (auto src = newStream.data()) {
connect(src, SIGNAL(readyRead()), this,
SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
src->setProperty("HTTP2StreamID", newStreamID);
}
activeStreams.insert(newStreamID, newStream);
@ -1235,4 +1214,4 @@ void QHttp2ProtocolHandler::closeSession()
QT_END_NAMESPACE
#endif // !defined(QT_NO_HTTP)
#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)

View File

@ -55,7 +55,7 @@
#include <private/qabstractprotocolhandler_p.h>
#include <private/qhttpnetworkrequest_p.h>
#if !defined(QT_NO_HTTP)
#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
#include "http2/http2protocol_p.h"
#include "http2/http2streams_p.h"
@ -81,15 +81,7 @@ class QHttp2ProtocolHandler : public QObject, public QAbstractProtocolHandler
Q_OBJECT
public:
// "TLS + ALPN/NPN" case:
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel);
// HTTP2 without TLS - the first request was sent as an HTTP/1.1 request
// with Upgrade:h2c header. That serves as an implicit HTTP/2 request
// on a stream with ID 1 (it will be created in this ctor in a
// 'half-closed-local' state); reply, if server supports HTTP/2,
// will be HTTP/2 frame(s):
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel,
const HttpMessagePair &message);
QHttp2ProtocolHandler(const QHttp2ProtocolHandler &rhs) = delete;
QHttp2ProtocolHandler(QHttp2ProtocolHandler &&rhs) = delete;
@ -141,7 +133,7 @@ private:
const QString &message);
// Stream's lifecycle management:
quint32 createNewStream(const HttpMessagePair &message, bool uploadData);
quint32 createNewStream(const HttpMessagePair &message);
void addToSuspended(Stream &stream);
void markAsReset(quint32 streamID);
quint32 popStreamToResume();
@ -210,6 +202,6 @@ private:
QT_END_NAMESPACE
#endif // !defined(QT_NO_HTTP)
#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
#endif

View File

@ -50,7 +50,6 @@
#include <private/qhttp2protocolhandler_p.h>
#include <private/qhttpprotocolhandler_p.h>
#include <private/qspdyprotocolhandler_p.h>
#include <private/http2protocol_p.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@ -181,9 +180,6 @@ void QHttpNetworkConnectionChannel::init()
sslSocket->setSslConfiguration(sslConfiguration);
} else {
#endif // QT_NO_SSL
// Even if connection->connectionType is ConnectionTypeHTTP2,
// we first start as HTTP/1.1, asking for a protocol upgrade
// in the first response.
protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_SSL
}
@ -839,16 +835,6 @@ void QHttpNetworkConnectionChannel::_q_connected()
#endif
} else {
state = QHttpNetworkConnectionChannel::IdleState;
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
Q_ASSERT(spdyRequestsToSend.size());
auto it = spdyRequestsToSend.begin();
// Let's inject some magic fields, requesting a protocol upgrade:
Http2::qt_add_ProtocolUpgradeRequest(it->first);
connection->d_func()->requeueRequest(*it);
// Remove it, we never send it again as HTTP/2.
spdyRequestsToSend.erase(it);
}
if (!reply)
connection->d_func()->dequeueRequest(socket);
if (reply)
@ -986,12 +972,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
} while (!connection->d_func()->highPriorityQueue.isEmpty()
|| !connection->d_func()->lowPriorityQueue.isEmpty());
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
#endif
) {
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
for (int a = 0; a < spdyPairs.count(); ++a) {
// emit error for all replies
@ -1000,6 +983,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
emit currentReply->finishedWithError(errorCode, errorString);
}
}
#endif // QT_NO_SSL
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
@ -1018,31 +1002,23 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
}
void QHttpNetworkConnectionChannel::_q_protocolSwitch()
{
Q_ASSERT(connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2);
Q_ASSERT(reply);
Q_ASSERT(reply->statusCode() == 101);
protocolHandler.reset(new QHttp2ProtocolHandler(this, HttpMessagePair(request, reply)));
protocolHandler->_q_receiveReply();
}
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
#endif
) {
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
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

View File

@ -121,14 +121,11 @@ public:
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
QScopedPointer<QAbstractProtocolHandler> protocolHandler;
// SPDY or HTTP/2 requests; SPDY is TLS-only, but
// HTTP/2 can be cleartext also, that's why it's
// outside of QT_NO_SSL section. Sorted by priority:
QMultiMap<int, HttpMessagePair> spdyRequestsToSend;
#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);
@ -195,7 +192,6 @@ public:
void _q_disconnected(); // disconnected from host
void _q_connected(); // start sending request
void _q_error(QAbstractSocket::SocketError); // error from socket
void _q_protocolSwitch(); // HTTP/2 was negotiated to replace HTTP/1.1
#ifndef QT_NO_NETWORKPROXY
void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
#endif

View File

@ -129,15 +129,6 @@ void QHttpProtocolHandler::_q_receiveReply()
} else {
replyPrivate->autoDecompress = false;
}
if (m_connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
if (replyPrivate->statusCode == 101) {
QMetaObject::invokeMethod(m_channel, "_q_protocolSwitch", Qt::QueuedConnection);
return;
}
// HTTP/2 is not supported? TODO - but can it be something else?
m_channel->requeueSpdyRequests();
}
if (replyPrivate->statusCode == 100) {
replyPrivate->clearHttpLayerInformation();
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;

View File

@ -286,12 +286,9 @@ void QHttpThreadDelegate::startRequest()
QHttpNetworkConnection::ConnectionType connectionType
= QHttpNetworkConnection::ConnectionTypeHTTP;
if (httpRequest.isHTTP2Allowed())
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2;
#ifndef QT_NO_SSL
if (httpRequest.isHTTP2Allowed() && ssl) {
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2;
QList<QByteArray> protocols;
protocols << QSslConfiguration::ALPNProtocolHTTP2
<< QSslConfiguration::NextProtocolHttp1_1;