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 using WK = QHttpHeaders::WellKnownHeader;
68 for (qsizetype i = 0; i < requestHeader.size(); ++i) {
69 const auto name = requestHeader.nameAt(i);
70 const auto value = requestHeader.valueAt(i);
71 const HeaderSize delta = entry_size(name, value);
74 if (std::numeric_limits<quint32>::max() - delta.second < size.second)
76 size.second += delta.second;
77 if (size.second > maxHeaderListSize)
80 if (name == QHttpHeaders::wellKnownHeaderName(WK::Connection)
81 || name == QHttpHeaders::wellKnownHeaderName(WK::Host)
82 || name == QHttpHeaders::wellKnownHeaderName(WK::KeepAlive)
83 || name.compare(
"proxy-connection"_L1, Qt::CaseInsensitive) == 0
84 || name == QHttpHeaders::wellKnownHeaderName(WK::TransferEncoding)) {
91 header.emplace_back(
QByteArray{name.data(), name.size()}.toLower(),
98QUrl urlkey_from_request(
const QHttpNetworkRequest &request)
102 url.setScheme(request.url().scheme());
103 url.setAuthority(request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo));
104 url.setPath(QLatin1StringView(request.uri(
false)));
112using namespace Http2;
117 const auto h2Config = m_connection->http2Parameters();
120 && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
121 h2Connection = QHttp2Connection::createUpgradedConnection(channel->socket, h2Config);
124 QHttp2Stream *stream = h2Connection->getStream(1);
126 Q_ASSERT(channel->reply);
127 connectStream({ channel->request, channel->reply }, stream);
129 Q_ASSERT(QSocketAbstraction::socketState(channel->socket) == QAbstractSocket::ConnectedState);
130 h2Connection = QHttp2Connection::createDirectConnection(channel->socket, h2Config);
132 connect(h2Connection, &QHttp2Connection::receivedGOAWAY,
this,
133 &QHttp2ProtocolHandler::handleGOAWAY);
134 connect(h2Connection, &QHttp2Connection::errorOccurred,
this,
136 connect(h2Connection, &QHttp2Connection::newIncomingStream,
this,
137 [
this](QHttp2Stream *stream){
141 if (!h2Connection->isGoingAway())
144 connect(h2Connection, &QHttp2Connection::connectionClosed,
this,
157 h2Connection->handleConnectionClosure();
162 QPointer<QHttp2Stream> stream = streamIDs.take(uploadData);
163 if (stream && stream->isActive())
164 stream->sendRST_STREAM(
CANCEL);
177 Q_ASSERT(h2Connection);
178 h2Connection->handleReadyRead();
183 if (h2Connection->isGoingAway()) {
186 m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
187 "GOAWAY received, cannot start a request");
188 m_channel->h2RequestsToSend.clear();
194 auto &requests = m_channel->h2RequestsToSend;
195 for (
auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
196 const auto &pair = *it;
197 if (pair.first.isPreConnect()) {
198 m_connection->preConnectFinished();
199 emit pair.second->finished();
200 it = requests.erase(it);
201 if (requests.empty()) {
213 if (requests.empty())
216 m_channel->state = QHttpNetworkConnectionChannel::WritingState;
220 for (
auto it = requests.begin(), end = requests.end(); it != end;) {
223 QUrl promiseKey = urlkey_from_request(httpPair.first);
224 if (h2Connection->promisedStream(promiseKey) !=
nullptr) {
226 initReplyFromPushPromise(httpPair, promiseKey);
227 it = requests.erase(it);
231 QHttp2Stream *stream = createNewStream(httpPair);
234 if (httpPair.second->isFinished()) {
235 it = requests.erase(it);
241 QHttpNetworkRequest &request = requestReplyPairs[stream].first;
242 if (!sendHEADERS(stream, request)) {
243 finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
244 "failed to send HEADERS frame(s)"_L1);
247 if (request.uploadByteDevice()) {
248 if (!sendDATA(stream, httpPair.second)) {
249 finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
250 "failed to send DATA frame(s)"_L1);
254 it = requests.erase(it);
257 m_channel->state = QHttpNetworkConnectionChannel::IdleState;
263
264
265
266
267
268
269
272 QHttp2Stream *stream = streamIDs.take(reply);
274 stream->sendRST_STREAM(stream->isUploadingDATA() ? Http2::CANCEL : Http2::HTTP2_NO_ERROR);
275 clearStreamState(stream);
276 stream->deleteLater();
284 using namespace HPack;
286 bool useProxy =
false;
287#ifndef QT_NO_NETWORKPROXY
288 useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
290 if (request.withCredentials()) {
291 m_connection->d_func()->createAuthorization(m_socket, request);
292 request.d->needResendWithCredentials =
false;
294 const auto headers = build_headers(request, h2Connection->maxHeaderListSize(), useProxy);
298 bool mustUploadData = request.uploadByteDevice();
299 return stream->sendHEADERS(headers, !mustUploadData);
305 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
306 Q_ASSERT(replyPrivate);
307 QHttpNetworkRequest &request = replyPrivate->request;
308 Q_ASSERT(request.uploadByteDevice());
310 bool startedSending = stream->sendDATA(request.uploadByteDevice(),
true);
311 return startedSending && !stream->wasReset();
316 QHttp2Stream *stream = qobject_cast<QHttp2Stream *>(sender());
318 auto &requestPair = requestReplyPairs[stream];
319 auto *httpReply = requestPair.second;
320 auto &httpRequest = requestPair.first;
324 auto *httpReplyPrivate = httpReply->d_func();
332 for (
const auto &pair : headers) {
333 const auto &name = pair.name;
334 const auto value = QByteArrayView(pair.value);
340 if (name ==
":status") {
342 if (
int status = value.toInt(&ok); ok && status >= 0 && status <= 999) {
344 httpReply->setStatusCode(statusCode);
345 m_channel->lastStatus = statusCode;
347 finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
348 "invalid :status value"_L1);
351 }
else if (name ==
"content-length") {
353 const qlonglong length = value.toLongLong(&ok);
355 httpReply->setContentLength(length);
357 const auto binder = name ==
"set-cookie" ? QByteArrayView(
"\n") : QByteArrayView(
", ");
358 httpReply->appendHeaderField(name, QByteArray(pair.value).replace(
'\0', binder));
364 if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
365 httpReplyPrivate->clearHttpLayerInformation();
369 if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
372 if (result.errorCode != QNetworkReply::NoError) {
373 auto errorString = m_connection->d_func()->errorDetail(result.errorCode, m_socket);
374 finishStreamWithError(stream, result.errorCode, errorString);
375 stream->sendRST_STREAM(INTERNAL_ERROR);
379 if (result.redirectUrl.isValid())
380 httpReply->setRedirectUrl(result.redirectUrl);
383 if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
384 httpReplyPrivate->removeAutoDecompressHeader();
386 if (QHttpNetworkReply::isHttpRedirect(statusCode)) {
392 if (
auto *byteDevice = httpRequest.uploadByteDevice()) {
394 httpReplyPrivate->totallyUploadedData = 0;
398 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::headerChanged, Qt::QueuedConnection);
400 finishStream(stream, Qt::QueuedConnection);
405 QHttp2Stream *stream = qobject_cast<QHttp2Stream *>(sender());
406 auto &httpPair = requestReplyPairs[stream];
407 auto *httpReply = httpPair.second;
410 Q_ASSERT(!stream->isPromisedStream());
412 if (!data.isEmpty() && !httpPair.first.d->needResendWithCredentials) {
413 auto *replyPrivate = httpReply->d_func();
415 replyPrivate->totalProgress += data.size();
417 replyPrivate->responseData.append(data);
419 if (replyPrivate->shouldEmitSignals()) {
420 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::readyRead,
421 Qt::QueuedConnection);
422 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::dataReadProgress,
423 Qt::QueuedConnection, replyPrivate->totalProgress,
424 replyPrivate->bodyLength);
427 stream->clearDownloadBuffer();
429 finishStream(stream, Qt::QueuedConnection);
437 auto &requestPair = requestReplyPairs[stream];
438 auto *httpReply = requestPair.second;
439 auto *httpReplyPrivate = httpReply->d_func();
440 auto &httpRequest = requestPair.first;
442 Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
444 const auto handleAuth = [&,
this](QByteArrayView authField,
bool isProxy) ->
bool {
446 const QByteArrayView auth = authField.trimmed();
447 if (auth.startsWith(
"Negotiate") || auth.startsWith(
"NTLM")) {
451 emit httpReply->headerChanged();
452 emit httpReply->readyRead();
453 const QNetworkReply::NetworkError error = isProxy
454 ? QNetworkReply::ProxyAuthenticationRequiredError
455 : QNetworkReply::AuthenticationRequiredError;
456 finishStreamWithError(stream, error,
457 m_connection->d_func()->errorDetail(error, m_socket));
462 const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
463 m_socket, httpReply, isProxy, resend);
464 if (authenticateHandled) {
466 httpReply->d_func()->eraseData();
469 httpRequest.d->needResendWithCredentials =
true;
470 m_channel->h2RequestsToSend.insert(httpRequest.priority(), requestPair);
471 httpReply->d_func()->clearHeaders();
473 if (
auto *byteDevice = httpRequest.uploadByteDevice()) {
475 httpReplyPrivate->totallyUploadedData = 0;
489 emit httpReply->headerChanged();
490 emit httpReply->readyRead();
491 QNetworkReply::NetworkError error = httpReply->statusCode() == 401
492 ? QNetworkReply::AuthenticationRequiredError
493 : QNetworkReply::ProxyAuthenticationRequiredError;
494 finishStreamWithError(stream, QNetworkReply::AuthenticationRequiredError,
495 m_connection->d_func()->errorDetail(error, m_socket));
505 switch (httpReply->statusCode()) {
507 authOk = handleAuth(httpReply->headerField(
"www-authenticate"),
false);
510 authOk = handleAuth(httpReply->headerField(
"proxy-authenticate"),
true);
516 stream->sendRST_STREAM(CANCEL);
517 clearStreamState(stream);
518 stream->deleteLater();
525 if (stream->state() != QHttp2Stream::State::Closed)
526 stream->sendRST_STREAM(CANCEL);
528 auto &pair = requestReplyPairs[stream];
529 auto *httpReply = pair.second;
531 int statusCode = httpReply->statusCode();
532 if (statusCode == 401 || statusCode == 407) {
536 handleAuthorization(stream);
540 httpReply->disconnect(
this);
542 if (!pair.first.d->needResendWithCredentials) {
543 if (connectionType == Qt::DirectConnection)
544 emit httpReply->finished();
546 QMetaObject::invokeMethod(httpReply, &QHttpNetworkReply::finished, connectionType);
550 clearStreamState(stream);
551 qCDebug(QT_HTTP2) <<
"stream" << stream->streamID() <<
"closed";
557 if (!httpReply || !tryRemoveReply(httpReply)) {
558 requestReplyPairs.remove(stream);
559 stream->deleteLater();
565 qCDebug(QT_HTTP2) <<
"GOAWAY received, error code:" << errorCode <<
"last stream ID:"
570 m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
571 "GOAWAY received, cannot start a request");
573 m_channel->h2RequestsToSend.clear();
575 QNetworkReply::NetworkError error = QNetworkReply::NoError;
577 qt_error(errorCode, error, message);
583 error = QNetworkReply::ContentReSendError;
584 message =
"Server stopped accepting new streams before this stream was established"_L1;
590 QNetworkReply::NetworkError error = QNetworkReply::NoError;
592 qt_error(errorCode, error, message);
593 finishStreamWithError(stream, error, message);
597 QNetworkReply::NetworkError error,
const QString &message)
599 stream->sendRST_STREAM(CANCEL);
600 const HttpMessagePair &pair = requestReplyPairs.value(stream);
601 if (
auto *httpReply = pair.second) {
602 httpReply->disconnect(
this);
605 emit httpReply->finishedWithError(error, message);
608 clearStreamState(stream);
609 stream->deleteLater();
610 qCWarning(QT_HTTP2) <<
"stream" << stream->streamID() <<
"finished with error:" << message;
614
615
616
617
618
622 QUrl streamKey = urlkey_from_request(message.first);
623 if (
auto promisedStream = h2Connection->promisedStream(streamKey)) {
624 Q_ASSERT(promisedStream->state() != QHttp2Stream::State::Closed);
625 return promisedStream;
628 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
629 streamResult = h2Connection->createStream();
630 if (!streamResult.ok()) {
631 if (streamResult.error()
632 == QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached) {
637 qCDebug(QT_HTTP2) <<
"failed to create new stream:" << streamResult.error();
638 auto *reply = message.second;
639 const char *cstr =
"Failed to initialize HTTP/2 stream with errorcode: %1";
640 const QString errorString = QCoreApplication::tr(
"QHttp", cstr)
641 .arg(QDebug::toString(streamResult.error()));
642 emit reply->finishedWithError(QNetworkReply::ProtocolFailure, errorString);
645 QHttp2Stream *stream = streamResult.unwrap();
648 if (
auto *src = message.first.uploadByteDevice()) {
649 connect(src, &QObject::destroyed,
this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed);
650 streamIDs.insert(src, stream);
654 auto *reply = message.second;
655 QMetaObject::invokeMethod(reply, &QHttpNetworkReply::requestSent, Qt::QueuedConnection);
657 connectStream(message, stream);
663 auto *reply = message.second;
664 auto *replyPrivate = reply->d_func();
665 replyPrivate->connection = m_connection;
666 replyPrivate->connectionChannel = m_channel;
668 reply->setHttp2WasUsed(
true);
669 QPointer<QHttp2Stream> &oldStream = streamIDs[reply];
671 disconnect(oldStream,
nullptr,
this,
nullptr);
672 clearStreamState(oldStream);
675 requestReplyPairs.emplace(stream, message);
677 QObject::connect(stream, &QHttp2Stream::headersReceived,
this,
678 &QHttp2ProtocolHandler::handleHeadersReceived);
679 QObject::connect(stream, &QHttp2Stream::dataReceived,
this,
680 &QHttp2ProtocolHandler::handleDataReceived);
681 QObject::connect(stream, &QHttp2Stream::errorOccurred,
this,
682 [
this, stream](Http2Error errorCode,
const QString &errorString) {
684 <<
"stream" << stream->streamID() <<
"error:" << errorString;
685 finishStreamWithError(stream, errorCode);
688 QObject::connect(stream, &QHttp2Stream::stateChanged,
this, [
this](QHttp2Stream::State state) {
689 if (state == QHttp2Stream::State::Closed) {
691 if (!m_channel->h2RequestsToSend.empty()) {
692 QMetaObject::invokeMethod(
this, &QHttp2ProtocolHandler::sendRequest,
693 Qt::QueuedConnection);
701 auto it = requestReplyPairs.find(stream);
702 if (it == requestReplyPairs.end())
705 if (
auto *reply = it->second)
706 streamIDs.remove(reply);
707 if (
auto *uploadDevice = it->first.uploadByteDevice())
708 streamIDs.remove(uploadDevice);
710 requestReplyPairs.erase(it);
714 const QUrl &cacheKey)
716 QHttp2Stream *promise = h2Connection->promisedStream(cacheKey);
718 Q_ASSERT(message.second);
719 message.second->setHttp2WasUsed(
true);
721 qCDebug(QT_HTTP2) <<
"found cached/promised response on stream" << promise->streamID();
723 const bool replyFinished = promise->state() == QHttp2Stream::State::Closed;
725 connectStream(message, promise);
730 QByteDataBuffer downloadBuffer = promise->takeDownloadBuffer();
731 if (
const auto &headers = promise->receivedHeaders(); !headers.empty())
732 emit promise->headersReceived(headers, replyFinished && downloadBuffer.isEmpty());
734 if (!downloadBuffer.isEmpty()) {
735 for (qsizetype i = 0; i < downloadBuffer.bufferCount(); ++i) {
736 const bool streamEnded = replyFinished && i == downloadBuffer.bufferCount() - 1;
737 emit promise->dataReceived(downloadBuffer[i], streamEnded);
744 Q_ASSERT(!message.isNull());
746 qCCritical(QT_HTTP2) <<
"connection error:" << message;
748 const auto error = qt_error(errorCode);
749 m_channel->emitFinishedWithError(error, qPrintable(message));
761#include "moc_qhttp2protocolhandler_p.cpp"
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
Q_INVOKABLE void handleConnectionClosure()
Q_INVOKABLE void _q_receiveReply() override
Q_INVOKABLE bool sendRequest() override
void _q_readyRead() override
bool tryRemoveReply(QHttpNetworkReply *reply) override
static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply)
Combined button and popup list for selecting options.
std::pair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair