5#include "access/http2/http2protocol_p.h"
6#include "access/qhttp2connection_p.h"
10#include "http2/http2frames_p.h"
12#include <private/qnoncontiguousbytedevice_p.h>
13#include <private/qsocketabstraction_p.h>
15#include <QtNetwork/qabstractsocket.h>
17#include <QtCore/qloggingcategory.h>
18#include <QtCore/qendian.h>
19#include <QtCore/qdebug.h>
20#include <QtCore/qlist.h>
21#include <QtCore/qnumeric.h>
22#include <QtCore/qurl.h>
24#include <qhttp2configuration.h>
26#ifndef QT_NO_NETWORKPROXY
27# include <QtNetwork/qnetworkproxy.h>
30#include <qcoreapplication.h>
38using namespace Qt::StringLiterals;
43HPack::HttpHeader build_headers(
const QHttpNetworkRequest &request, quint32 maxHeaderListSize,
46 using namespace HPack;
53 const auto auth = request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1();
54 header.emplace_back(
":authority", auth);
55 header.emplace_back(
":method", request.methodName());
56 header.emplace_back(
":path", request.uri(useProxy));
57 header.emplace_back(
":scheme", request.url().scheme().toLatin1());
59 HeaderSize size = header_size(header);
63 if (size.second > maxHeaderListSize)
66 const QHttpHeaders requestHeader = request.header();
67 for (qsizetype i = 0; i < requestHeader.size(); ++i) {
68 const auto name = requestHeader.nameAt(i);
69 const auto value = requestHeader.valueAt(i);
70 const HeaderSize delta = entry_size(name, value);
73 if (std::numeric_limits<quint32>::max() - delta.second < size.second)
75 size.second += delta.second;
76 if (size.second > maxHeaderListSize)
79 if (name ==
"connection"_L1 || name ==
"host"_L1 || name ==
"keep-alive"_L1
80 || name ==
"proxy-connection"_L1 || name ==
"transfer-encoding"_L1) {
88 header.emplace_back(
QByteArray{name.data(), name.size()},
95QUrl urlkey_from_request(
const QHttpNetworkRequest &request)
99 url.setScheme(request.url().scheme());
100 url.setAuthority(request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo));
101 url.setPath(QLatin1StringView(request.uri(
false)));
109using namespace Http2;
111QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
112 : QAbstractProtocolHandler(channel)
114 const auto h2Config = m_connection->http2Parameters();
117 && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
118 h2Connection = QHttp2Connection::createUpgradedConnection(channel->socket, h2Config);
121 QHttp2Stream *stream = h2Connection->getStream(1);
123 Q_ASSERT(channel->reply);
124 connectStream({ channel->request, channel->reply }, stream);
126 Q_ASSERT(QSocketAbstraction::socketState(channel->socket) == QAbstractSocket::ConnectedState);
127 h2Connection = QHttp2Connection::createDirectConnection(channel->socket, h2Config);
129 connect(h2Connection, &QHttp2Connection::receivedGOAWAY,
this,
130 &QHttp2ProtocolHandler::handleGOAWAY);
131 connect(h2Connection, &QHttp2Connection::errorOccurred,
this,
132 &QHttp2ProtocolHandler::connectionError);
133 connect(h2Connection, &QHttp2Connection::newIncomingStream,
this,
134 [
this](QHttp2Stream *stream){
138 if (!h2Connection->isGoingAway())
141 connect(h2Connection, &QHttp2Connection::connectionClosed,
this,
142 &QHttp2ProtocolHandler::closeSession);
145void QHttp2ProtocolHandler::handleConnectionClosure()
154 h2Connection->handleConnectionClosure();
157void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData)
159 QPointer<QHttp2Stream> stream = streamIDs.take(uploadData);
160 if (stream && stream->isActive())
161 stream->sendRST_STREAM(
CANCEL);
164void QHttp2ProtocolHandler::_q_readyRead()
169void QHttp2ProtocolHandler::_q_receiveReply()
174 Q_ASSERT(h2Connection);
175 h2Connection->handleReadyRead();
178bool QHttp2ProtocolHandler::sendRequest()
180 if (h2Connection->isGoingAway()) {
183 m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
184 "GOAWAY received, cannot start a request");
185 m_channel->h2RequestsToSend.clear();
191 auto &requests = m_channel->h2RequestsToSend;
192 for (
auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
193 const auto &pair = *it;
194 if (pair.first.isPreConnect()) {
195 m_connection->preConnectFinished();
196 emit pair.second->finished();
197 it = requests.erase(it);
198 if (requests.empty()) {
210 if (requests.empty())
213 m_channel->state = QHttpNetworkConnectionChannel::WritingState;
217 for (
auto it = requests.begin(), end = requests.end(); it != end;) {
218 HttpMessagePair &httpPair = *it;
220 QUrl promiseKey = urlkey_from_request(httpPair.first);
221 if (h2Connection->promisedStream(promiseKey) !=
nullptr) {
223 initReplyFromPushPromise(httpPair, promiseKey);
224 it = requests.erase(it);
228 QHttp2Stream *stream = createNewStream(httpPair);
231 if (httpPair.second->isFinished()) {
232 it = requests.erase(it);
238 QHttpNetworkRequest &request = requestReplyPairs[stream].first;
239 if (!sendHEADERS(stream, request)) {
240 finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
241 "failed to send HEADERS frame(s)"_L1);
244 if (request.uploadByteDevice()) {
245 if (!sendDATA(stream, httpPair.second)) {
246 finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
247 "failed to send DATA frame(s)"_L1);
251 it = requests.erase(it);
254 m_channel->state = QHttpNetworkConnectionChannel::IdleState;
260
261
262
263
264
265
266
267bool QHttp2ProtocolHandler::tryRemoveReply(QHttpNetworkReply *reply)
269 QHttp2Stream *stream = streamIDs.take(reply);
271 stream->sendRST_STREAM(stream->isUploadingDATA() ? Http2::CANCEL : Http2::HTTP2_NO_ERROR);
272 requestReplyPairs.remove(stream);
273 stream->deleteLater();
279bool QHttp2ProtocolHandler::sendHEADERS(QHttp2Stream *stream, QHttpNetworkRequest &request)
281 using namespace HPack;
283 bool useProxy =
false;
284#ifndef QT_NO_NETWORKPROXY
285 useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
287 if (request.withCredentials()) {
288 m_connection->d_func()->createAuthorization(m_socket, request);
289 request.d->needResendWithCredentials =
false;
291 const auto headers = build_headers(request, h2Connection->maxHeaderListSize(), useProxy);
295 bool mustUploadData = request.uploadByteDevice();
296 return stream->sendHEADERS(headers, !mustUploadData);
299bool QHttp2ProtocolHandler::sendDATA(QHttp2Stream *stream, QHttpNetworkReply *reply)
302 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
303 Q_ASSERT(replyPrivate);
304 QHttpNetworkRequest &request = replyPrivate->request;
305 Q_ASSERT(request.uploadByteDevice());
307 bool startedSending = stream->sendDATA(request.uploadByteDevice(),
true);
308 return startedSending && !stream->wasReset();
311void QHttp2ProtocolHandler::handleHeadersReceived(
const HPack::HttpHeader &headers,
bool endStream)
313 QHttp2Stream *stream = qobject_cast<QHttp2Stream *>(sender());
315 auto &requestPair = requestReplyPairs[stream];
316 auto *httpReply = requestPair.second;
317 auto &httpRequest = requestPair.first;
321 auto *httpReplyPrivate = httpReply->d_func();
329 for (
const auto &pair : headers) {
330 const auto &name = pair.name;
331 const auto value = QByteArrayView(pair.value);
337 if (name ==
":status") {
339 if (
int status = value.toInt(&ok); ok && status >= 0 && status <= 999) {
341 httpReply->setStatusCode(statusCode);
342 m_channel->lastStatus = statusCode;
344 finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
345 "invalid :status value"_L1);
348 }
else if (name ==
"content-length") {
350 const qlonglong length = value.toLongLong(&ok);
352 httpReply->setContentLength(length);
354 const auto binder = name ==
"set-cookie" ? QByteArrayView(
"\n") : QByteArrayView(
", ");
355 httpReply->appendHeaderField(name, QByteArray(pair.value).replace(
'\0', binder));
361 if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
362 httpReplyPrivate->clearHttpLayerInformation();
366 if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
369 if (result.errorCode != QNetworkReply::NoError) {
370 auto errorString = m_connection->d_func()->errorDetail(result.errorCode, m_socket);
371 finishStreamWithError(stream, result.errorCode, errorString);
372 stream->sendRST_STREAM(INTERNAL_ERROR);
376 if (result.redirectUrl.isValid())
377 httpReply->setRedirectUrl(result.redirectUrl);
380 if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
381 httpReplyPrivate->removeAutoDecompressHeader();
383 if (QHttpNetworkReply::isHttpRedirect(statusCode)) {
389 if (
auto *byteDevice = httpRequest.uploadByteDevice()) {
391 httpReplyPrivate->totallyUploadedData = 0;
395 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::headerChanged, Qt::QueuedConnection);
397 finishStream(stream, Qt::QueuedConnection);
400void QHttp2ProtocolHandler::handleDataReceived(
const QByteArray &data,
bool endStream)
402 QHttp2Stream *stream = qobject_cast<QHttp2Stream *>(sender());
403 auto &httpPair = requestReplyPairs[stream];
404 auto *httpReply = httpPair.second;
407 Q_ASSERT(!stream->isPromisedStream());
409 if (!data.isEmpty() && !httpPair.first.d->needResendWithCredentials) {
410 auto *replyPrivate = httpReply->d_func();
412 replyPrivate->totalProgress += data.size();
414 replyPrivate->responseData.append(data);
416 if (replyPrivate->shouldEmitSignals()) {
417 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::readyRead,
418 Qt::QueuedConnection);
419 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::dataReadProgress,
420 Qt::QueuedConnection, replyPrivate->totalProgress,
421 replyPrivate->bodyLength);
424 stream->clearDownloadBuffer();
426 finishStream(stream, Qt::QueuedConnection);
432void QHttp2ProtocolHandler::handleAuthorization(QHttp2Stream *stream)
434 auto &requestPair = requestReplyPairs[stream];
435 auto *httpReply = requestPair.second;
436 auto *httpReplyPrivate = httpReply->d_func();
437 auto &httpRequest = requestPair.first;
439 Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
441 const auto handleAuth = [&,
this](QByteArrayView authField,
bool isProxy) ->
bool {
443 const QByteArrayView auth = authField.trimmed();
444 if (auth.startsWith(
"Negotiate") || auth.startsWith(
"NTLM")) {
455 const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
456 m_socket, httpReply, isProxy, resend);
457 if (authenticateHandled) {
459 httpReply->d_func()->eraseData();
462 httpRequest.d->needResendWithCredentials =
true;
463 m_channel->h2RequestsToSend.insert(httpRequest.priority(), requestPair);
464 httpReply->d_func()->clearHeaders();
466 if (
auto *byteDevice = httpRequest.uploadByteDevice()) {
468 httpReplyPrivate->totallyUploadedData = 0;
482 emit httpReply->headerChanged();
483 emit httpReply->readyRead();
484 QNetworkReply::NetworkError error = httpReply->statusCode() == 401
485 ? QNetworkReply::AuthenticationRequiredError
486 : QNetworkReply::ProxyAuthenticationRequiredError;
487 finishStreamWithError(stream, QNetworkReply::AuthenticationRequiredError,
488 m_connection->d_func()->errorDetail(error, m_socket));
498 switch (httpReply->statusCode()) {
500 authOk = handleAuth(httpReply->headerField(
"www-authenticate"),
false);
503 authOk = handleAuth(httpReply->headerField(
"proxy-authenticate"),
true);
509 stream->sendRST_STREAM(CANCEL);
514void QHttp2ProtocolHandler::finishStream(QHttp2Stream *stream, Qt::ConnectionType connectionType)
516 if (stream->state() != QHttp2Stream::State::Closed)
517 stream->sendRST_STREAM(CANCEL);
519 auto &pair = requestReplyPairs[stream];
520 auto *httpReply = pair.second;
522 int statusCode = httpReply->statusCode();
523 if (statusCode == 401 || statusCode == 407) {
527 handleAuthorization(stream);
531 httpReply->disconnect(
this);
533 if (!pair.first.d->needResendWithCredentials) {
534 if (connectionType == Qt::DirectConnection)
535 emit httpReply->finished();
537 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::finished, connectionType);
541 qCDebug(QT_HTTP2) <<
"stream" << stream->streamID() <<
"closed";
542 stream->deleteLater();
545void QHttp2ProtocolHandler::handleGOAWAY(
Http2Error errorCode, quint32 lastStreamID)
547 qCDebug(QT_HTTP2) <<
"GOAWAY received, error code:" << errorCode <<
"last stream ID:"
552 m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
553 "GOAWAY received, cannot start a request");
555 m_channel->h2RequestsToSend.clear();
557 QNetworkReply::NetworkError error = QNetworkReply::NoError;
559 qt_error(errorCode, error, message);
565 error = QNetworkReply::ContentReSendError;
566 message =
"Server stopped accepting new streams before this stream was established"_L1;
570void QHttp2ProtocolHandler::finishStreamWithError(QHttp2Stream *stream, Http2Error errorCode)
572 QNetworkReply::NetworkError error = QNetworkReply::NoError;
574 qt_error(errorCode, error, message);
575 finishStreamWithError(stream, error, message);
578void QHttp2ProtocolHandler::finishStreamWithError(QHttp2Stream *stream,
579 QNetworkReply::NetworkError error,
const QString &message)
581 stream->sendRST_STREAM(CANCEL);
582 const HttpMessagePair &pair = requestReplyPairs.value(stream);
583 if (
auto *httpReply = pair.second) {
584 httpReply->disconnect(
this);
587 emit httpReply->finishedWithError(error, message);
590 qCWarning(QT_HTTP2) <<
"stream" << stream->streamID() <<
"finished with error:" << message;
594
595
596
597
598
599QHttp2Stream *QHttp2ProtocolHandler::createNewStream(
const HttpMessagePair &message,
602 QUrl streamKey = urlkey_from_request(message.first);
603 if (
auto promisedStream = h2Connection->promisedStream(streamKey)) {
604 Q_ASSERT(promisedStream->state() != QHttp2Stream::State::Closed);
605 return promisedStream;
608 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
609 streamResult = h2Connection->createStream();
610 if (!streamResult.ok()) {
611 if (streamResult.error()
612 == QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached) {
617 qCDebug(QT_HTTP2) <<
"failed to create new stream:" << streamResult.error();
618 auto *reply = message.second;
619 const char *cstr =
"Failed to initialize HTTP/2 stream with errorcode: %1";
620 const QString errorString = QCoreApplication::tr(
"QHttp", cstr)
621 .arg(QDebug::toString(streamResult.error()));
622 emit reply->finishedWithError(QNetworkReply::ProtocolFailure, errorString);
625 QHttp2Stream *stream = streamResult.unwrap();
628 if (
auto *src = message.first.uploadByteDevice()) {
629 connect(src, &QObject::destroyed,
this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed);
630 streamIDs.insert(src, stream);
634 auto *reply = message.second;
635 QMetaObject::invokeMethod(reply, &QHttpNetworkReply::requestSent, Qt::QueuedConnection);
637 connectStream(message, stream);
641void QHttp2ProtocolHandler::connectStream(
const HttpMessagePair &message, QHttp2Stream *stream)
643 auto *reply = message.second;
644 auto *replyPrivate = reply->d_func();
645 replyPrivate->connection = m_connection;
646 replyPrivate->connectionChannel = m_channel;
648 reply->setHttp2WasUsed(
true);
649 QPointer<QHttp2Stream> &oldStream = streamIDs[reply];
651 disconnect(oldStream,
nullptr,
this,
nullptr);
653 requestReplyPairs.emplace(stream, message);
655 QObject::connect(stream, &QHttp2Stream::headersReceived,
this,
656 &QHttp2ProtocolHandler::handleHeadersReceived);
657 QObject::connect(stream, &QHttp2Stream::dataReceived,
this,
658 &QHttp2ProtocolHandler::handleDataReceived);
659 QObject::connect(stream, &QHttp2Stream::errorOccurred,
this,
660 [
this, stream](Http2Error errorCode,
const QString &errorString) {
662 <<
"stream" << stream->streamID() <<
"error:" << errorString;
663 finishStreamWithError(stream, errorCode);
666 QObject::connect(stream, &QHttp2Stream::stateChanged,
this, [
this](QHttp2Stream::State state) {
667 if (state == QHttp2Stream::State::Closed) {
669 if (!m_channel->h2RequestsToSend.empty()) {
670 QMetaObject::invokeMethod(
this, &QHttp2ProtocolHandler::sendRequest,
671 Qt::QueuedConnection);
677void QHttp2ProtocolHandler::initReplyFromPushPromise(
const HttpMessagePair &message,
678 const QUrl &cacheKey)
680 QHttp2Stream *promise = h2Connection->promisedStream(cacheKey);
682 Q_ASSERT(message.second);
683 message.second->setHttp2WasUsed(
true);
685 qCDebug(QT_HTTP2) <<
"found cached/promised response on stream" << promise->streamID();
687 const bool replyFinished = promise->state() == QHttp2Stream::State::Closed;
689 connectStream(message, promise);
694 QByteDataBuffer downloadBuffer = promise->takeDownloadBuffer();
695 if (
const auto &headers = promise->receivedHeaders(); !headers.empty())
696 emit promise->headersReceived(headers, replyFinished && downloadBuffer.isEmpty());
698 if (!downloadBuffer.isEmpty()) {
699 for (qsizetype i = 0; i < downloadBuffer.bufferCount(); ++i) {
700 const bool streamEnded = replyFinished && i == downloadBuffer.bufferCount() - 1;
701 emit promise->dataReceived(downloadBuffer[i], streamEnded);
706void QHttp2ProtocolHandler::connectionError(
Http2::
Http2Error errorCode,
const QString &message)
708 Q_ASSERT(!message.isNull());
710 qCCritical(QT_HTTP2) <<
"connection error:" << message;
712 const auto error = qt_error(errorCode);
713 m_channel->emitFinishedWithError(error, qPrintable(message));
718void QHttp2ProtocolHandler::closeSession()
725#include "moc_qhttp2protocolhandler_p.cpp"
static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply)
Combined button and popup list for selecting options.