Handle informational HTTP replies (1xx) for HTTP/2
Make QHttp2ProtocolHandler discard all informational (1xx) replies with the exception of 101. According to RFC 9110: "A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses." Fixes: QTBUG-121755 Change-Id: I8b8d578f23d4fbe28929f8c54b3607bcaf85405f Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>bb10
parent
c89b1bbddc
commit
6d08db8666
|
|
@ -1101,6 +1101,13 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
|
|||
}
|
||||
}
|
||||
|
||||
// Discard all informational (1xx) replies with the exception of 101.
|
||||
// Also see RFC 9110 (Chapter 15.2)
|
||||
if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
|
||||
httpReplyPrivate->clearHttpLayerInformation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
|
||||
QHttpNetworkConnectionPrivate::ParseRedirectResult result =
|
||||
m_connection->d_func()->parseRedirectResponse(httpReply);
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ Http2Server::~Http2Server()
|
|||
{
|
||||
}
|
||||
|
||||
void Http2Server::setInformationalStatusCode(int code)
|
||||
{
|
||||
if (code == 100 || (102 <= code && code <= 199))
|
||||
informationalStatusCode = code;
|
||||
}
|
||||
|
||||
void Http2Server::enablePushPromise(bool pushEnabled, const QByteArray &path)
|
||||
{
|
||||
pushPromiseEnabled = pushEnabled;
|
||||
|
|
@ -837,6 +843,25 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
|
|||
// Now we'll continue with _normal_ response.
|
||||
}
|
||||
|
||||
// Create a header with an informational status code and some random header
|
||||
// fields. The setter ensures that the value is 100 or is between 102 and 199
|
||||
// (inclusive) if set - otherwise it is 0
|
||||
|
||||
if (informationalStatusCode > 0) {
|
||||
writer.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
|
||||
|
||||
HttpHeader informationalHeader;
|
||||
informationalHeader.push_back({":status", QByteArray::number(informationalStatusCode)});
|
||||
informationalHeader.push_back(HeaderField("a_random_header_field", "it_will_be_dropped"));
|
||||
informationalHeader.push_back(HeaderField("another_random_header_field", "drop_this_too"));
|
||||
|
||||
HPack::BitOStream ostream(writer.outboundFrame().buffer);
|
||||
const bool result = encoder.encodeResponse(ostream, informationalHeader);
|
||||
Q_ASSERT(result);
|
||||
|
||||
writer.writeHEADERS(*socket, maxFrameSize);
|
||||
}
|
||||
|
||||
writer.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
|
||||
if (emptyBody)
|
||||
writer.addFlag(FrameFlag::END_STREAM);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ public:
|
|||
~Http2Server();
|
||||
|
||||
|
||||
// To send responses with status code 1xx
|
||||
void setInformationalStatusCode(int code);
|
||||
// To be called before server started:
|
||||
void enablePushPromise(bool enabled, const QByteArray &path = QByteArray());
|
||||
void setResponseBody(const QByteArray &body);
|
||||
|
|
@ -210,6 +212,7 @@ private:
|
|||
int redirectCount = 0;
|
||||
|
||||
bool sendTrailingHEADERS = false;
|
||||
int informationalStatusCode = 0;
|
||||
protected slots:
|
||||
void ignoreErrorSlot();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ private slots:
|
|||
void defaultQnamHttp2Configuration();
|
||||
void singleRequest_data();
|
||||
void singleRequest();
|
||||
void informationalRequest_data();
|
||||
void informationalRequest();
|
||||
void multipleRequests();
|
||||
void flowControlClientSide();
|
||||
void flowControlServerSide();
|
||||
|
|
@ -300,6 +302,70 @@ void tst_Http2::singleRequest()
|
|||
#endif // QT_CONFIG(ssl)
|
||||
}
|
||||
|
||||
void tst_Http2::informationalRequest_data()
|
||||
{
|
||||
QTest::addColumn<int>("statusCode");
|
||||
|
||||
// 'Clear text' that should always work, either via the protocol upgrade
|
||||
// or as direct.
|
||||
QTest::addRow("statusCode-100") << 100;
|
||||
QTest::addRow("statusCode-125") << 125;
|
||||
QTest::addRow("statusCode-150") << 150;
|
||||
QTest::addRow("statusCode-175") << 175;
|
||||
}
|
||||
|
||||
void tst_Http2::informationalRequest()
|
||||
{
|
||||
clearHTTP2State();
|
||||
|
||||
serverPort = 0;
|
||||
nRequests = 1;
|
||||
|
||||
ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType()));
|
||||
|
||||
QFETCH(const int, statusCode);
|
||||
srv->setInformationalStatusCode(statusCode);
|
||||
|
||||
QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
|
||||
runEventLoop();
|
||||
|
||||
QVERIFY(serverPort != 0);
|
||||
|
||||
auto url = requestUrl(defaultConnectionType());
|
||||
url.setPath("/index.html");
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
|
||||
|
||||
auto reply = manager->get(request);
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
// Since we're using self-signed certificates,
|
||||
// ignore SSL errors:
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QCOMPARE(nRequests, 0);
|
||||
QVERIFY(prefaceOK);
|
||||
QVERIFY(serverGotSettingsACK);
|
||||
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QVERIFY(reply->isFinished());
|
||||
|
||||
const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
|
||||
|
||||
// We are discarding informational headers if the status code is in the range of
|
||||
// 102-199 or if it is 100. As these header fields were part of the informational
|
||||
// header used for this test case, we should not see them at this point and the
|
||||
// status code should be 200.
|
||||
|
||||
QCOMPARE(code.value<int>(), 200);
|
||||
QVERIFY(!reply->hasRawHeader("a_random_header_field"));
|
||||
QVERIFY(!reply->hasRawHeader("another_random_header_field"));
|
||||
}
|
||||
|
||||
void tst_Http2::multipleRequests()
|
||||
{
|
||||
clearHTTP2State();
|
||||
|
|
|
|||
Loading…
Reference in New Issue