10#include <private/qnoncontiguousbytedevice_p.h>
12#include <QtNetwork/qabstractsocket.h>
14#include <QtCore/qloggingcategory.h>
15#include <QtCore/qendian.h>
16#include <QtCore/qdebug.h>
17#include <QtCore/qlist.h>
18#include <QtCore/qnumeric.h>
19#include <QtCore/qurl.h>
23#ifndef QT_NO_NETWORKPROXY
24#include <QtNetwork/qnetworkproxy.h>
43 using namespace HPack;
51 header.emplace_back(
":authority", auth);
60 if (
size.second > maxHeaderListSize)
65 const auto name = requestHeader.nameAt(
i);
66 const auto value = requestHeader.valueAt(
i);
70 if (std::numeric_limits<quint32>::max() - delta.second <
size.second)
72 size.second += delta.second;
73 if (
size.second > maxHeaderListSize)
76 if (
name ==
"connection"_L1 ||
name ==
"host"_L1 ||
name ==
"keep-alive"_L1
77 ||
name ==
"proxy-connection"_L1 ||
name ==
"transfer-encoding"_L1) {
106using namespace Http2;
114 continuedFrames.reserve(20);
117 maxSessionReceiveWindowSize = h2Config.sessionReceiveWindowSize();
118 pushPromiseEnabled = h2Config.serverPushEnabled();
119 streamInitialReceiveWindowSize = h2Config.streamReceiveWindowSize();
151 for (
auto it = activeStreams.begin(), eIt = activeStreams.end();
it != eIt; ++
it)
155 activeStreams.clear();
165void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
170 auto data = qobject_cast<QNonContiguousByteDevice *>(
sender());
174 Q_ASSERT(activeStreams.contains(streamID));
175 auto &
stream = activeStreams[streamID];
180 markAsReset(streamID);
181 deleteActiveStream(streamID);
185void QHttp2ProtocolHandler::_q_replyDestroyed(
QObject *
reply)
188 if (activeStreams.contains(streamID)) {
189 sendRST_STREAM(streamID,
CANCEL);
190 markAsReset(streamID);
191 deleteActiveStream(streamID);
195void QHttp2ProtocolHandler::_q_uploadDataDestroyed(
QObject *uploadData)
197 streamIDs.
remove(uploadData);
202 if (!goingAway || activeStreams.size())
211 if (goingAway && activeStreams.isEmpty()) {
216 while (!goingAway || activeStreams.size()) {
219 case FrameStatus::incompleteFrame:
221 case FrameStatus::protocolError:
223 case FrameStatus::sizeError:
233 const auto frameType = inboundFrame.
type();
234 if (continuationExpected && frameType != FrameType::CONTINUATION)
238 case FrameType::DATA:
241 case FrameType::HEADERS:
244 case FrameType::PRIORITY:
247 case FrameType::RST_STREAM:
250 case FrameType::SETTINGS:
253 case FrameType::PUSH_PROMISE:
254 handlePUSH_PROMISE();
256 case FrameType::PING:
259 case FrameType::GOAWAY:
262 case FrameType::WINDOW_UPDATE:
263 handleWINDOW_UPDATE();
265 case FrameType::CONTINUATION:
266 handleCONTINUATION();
268 case FrameType::LAST_FRAME_TYPE:
281 "GOAWAY received, cannot start a request");
290 const auto &pair = *
it;
291 if (pair.first.isPreConnect()) {
293 emit pair.second->finished();
307 if (!prefaceSent && !sendClientPreface())
318 const auto key = urlkey_from_request(
it->first).toString();
329 const auto isClientSide = [](
const auto &pair) ->
bool {
return (pair.first & 1) == 1; };
330 const auto activeClientSideStreams = std::count_if(
331 activeStreams.constKeyValueBegin(), activeStreams.constKeyValueEnd(), isClientSide);
332 const qint64 streamsToUse =
qBound(0,
qint64(maxConcurrentStreams) - activeClientSideStreams,
335 for (
qint64 i = 0;
i < streamsToUse; ++
i) {
336 const qint32 newStreamID = createNewStream(*
it);
339 qCCritical(QT_HTTP2,
"sendRequest: out of stream IDs");
345 Stream &newStream = activeStreams[newStreamID];
346 if (!sendHEADERS(newStream)) {
348 "failed to send HEADERS frame(s)"_L1);
349 deleteActiveStream(newStreamID);
353 if (newStream.data() && !sendDATA(newStream)) {
355 "failed to send DATA frame(s)"_L1);
357 markAsReset(newStreamID);
358 deleteActiveStream(newStreamID);
368bool QHttp2ProtocolHandler::sendClientPreface()
388 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
396 waitingForSettingsACK =
true;
401bool QHttp2ProtocolHandler::sendSETTINGS_ACK()
405 if (!prefaceSent && !sendClientPreface())
415 using namespace HPack;
417 frameWriter.
start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
421 frameWriter.
addFlag(FrameFlag::END_STREAM);
430 bool useProxy =
false;
431#ifndef QT_NO_NETWORKPROXY
434 if (
stream.request().withCredentials()) {
436 stream.request().d->needResendWithCredentials =
false;
438 const auto headers = build_headers(
stream.request(), maxHeaderListSize, useProxy);
459 const auto replyPrivate =
reply->d_func();
462 auto slot = std::min<qint32>(sessionSendWindowSize,
stream.sendWindow);
463 while (replyPrivate->totallyUploadedData <
request.contentLength() && slot) {
466 reinterpret_cast<const uchar *
>(
stream.data()->readPointer(slot, chunkSize));
471 if (!
src || !chunkSize) {
477 frameWriter.
start(FrameType::DATA, FrameFlag::EMPTY,
stream.streamID);
487 emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
489 slot = std::min(sessionSendWindowSize,
stream.sendWindow);
492 if (replyPrivate->totallyUploadedData ==
request.contentLength()) {
493 frameWriter.
start(FrameType::DATA, FrameFlag::END_STREAM,
stream.streamID);
497 stream.data()->disconnect(
this);
498 removeFromSuspended(
stream.streamID);
499 }
else if (!
stream.data()->atEnd()) {
506bool QHttp2ProtocolHandler::sendWINDOW_UPDATE(
quint32 streamID,
quint32 delta)
510 frameWriter.
start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
511 frameWriter.
append(delta);
515bool QHttp2ProtocolHandler::sendRST_STREAM(
quint32 streamID,
quint32 errorCode)
519 frameWriter.
start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
520 frameWriter.
append(errorCode);
524bool QHttp2ProtocolHandler::sendGOAWAY(
quint32 errorCode)
530 frameWriter.
append(errorCode);
534void QHttp2ProtocolHandler::handleDATA()
538 const auto streamID = inboundFrame.
streamID();
542 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
548 sessionReceiveWindowSize -= inboundFrame.
payloadSize();
550 auto it = activeStreams.find(streamID);
551 if (
it != activeStreams.end()) {
557 markAsReset(streamID);
558 deleteActiveStream(streamID);
562 updateStream(
stream, inboundFrame);
564 if (inboundFrame.
flags().testFlag(FrameFlag::END_STREAM)) {
566 deleteActiveStream(
stream.streamID);
567 }
else if (
stream.recvWindow < streamInitialReceiveWindowSize / 2) {
571 stream.recvWindow = streamInitialReceiveWindowSize;
576 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
579 Q_ARG(
quint32, maxSessionReceiveWindowSize - sessionReceiveWindowSize));
580 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
584void QHttp2ProtocolHandler::handleHEADERS()
588 const auto streamID = inboundFrame.
streamID();
592 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
596 if (
flags.testFlag(FrameFlag::PRIORITY)) {
602 const bool endHeaders =
flags.testFlag(FrameFlag::END_HEADERS);
603 continuedFrames.clear();
604 continuedFrames.push_back(std::move(inboundFrame));
606 continuationExpected =
true;
610 handleContinuedHEADERS();
613void QHttp2ProtocolHandler::handlePRIORITY()
616 inboundFrame.
type() == FrameType::HEADERS);
618 const auto streamID = inboundFrame.
streamID();
622 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
627 const bool noErr = inboundFrame.
priority(&streamDependency, &
weight);
632 const bool exclusive = streamDependency & 0x80000000;
633 streamDependency &= ~0x80000000;
641void QHttp2ProtocolHandler::handleRST_STREAM()
649 const auto streamID = inboundFrame.
streamID();
653 if (!(streamID & 0x1)) {
660 if (streamID >= nextID) {
664 return connectionError(
PROTOCOL_ERROR,
"RST_STREAM on idle stream");
667 if (!activeStreams.contains(streamID)) {
675 finishStreamWithError(
stream, qFromBigEndian<quint32>(inboundFrame.
dataBegin()));
676 markAsReset(
stream.streamID);
677 deleteActiveStream(
stream.streamID);
680void QHttp2ProtocolHandler::handleSETTINGS()
686 return connectionError(
PROTOCOL_ERROR,
"SETTINGS on invalid stream");
688 if (inboundFrame.
flags().testFlag(FrameFlag::ACK)) {
689 if (!waitingForSettingsACK)
690 return connectionError(
PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
691 waitingForSettingsACK =
false;
699 const quint32 intVal = qFromBigEndian<quint32>(
src + 2);
700 if (!acceptSetting(identifier, intVal)) {
711void QHttp2ProtocolHandler::handlePUSH_PROMISE()
714 Q_ASSERT(inboundFrame.
type() == FrameType::PUSH_PROMISE);
716 if (!pushPromiseEnabled && prefaceSent && !waitingForSettingsACK) {
719 return connectionError(
PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
722 const auto streamID = inboundFrame.
streamID();
725 "PUSH_PROMISE with invalid associated stream (0x0)");
728 if (!activeStreams.contains(streamID) && !streamWasReset(streamID)) {
730 "PUSH_PROMISE with invalid associated stream");
733 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.
dataBegin());
734 if ((reservedID & 1) || reservedID <= lastPromisedID ||
737 "PUSH_PROMISE with invalid promised stream ID");
740 lastPromisedID = reservedID;
742 if (!pushPromiseEnabled) {
748 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
749 continuedFrames.clear();
750 continuedFrames.push_back(std::move(inboundFrame));
753 continuationExpected =
true;
757 handleContinuedHEADERS();
760void QHttp2ProtocolHandler::handlePING()
770 if (inboundFrame.
flags() & FrameFlag::ACK)
780void QHttp2ProtocolHandler::handleGOAWAY()
788 return connectionError(
PROTOCOL_ERROR,
"GOAWAY on invalid stream");
791 quint32 lastStreamID = qFromBigEndian<quint32>(
src);
792 const quint32 errorCode = qFromBigEndian<quint32>(
src + 4);
798 }
else if (!(lastStreamID & 0x1)) {
800 return connectionError(
PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
801 }
else if (lastStreamID >= nextID) {
806 return connectionError(
PROTOCOL_ERROR,
"GOAWAY invalid stream/error code");
816 "GOAWAY received, cannot start a request");
829 message =
"Server stopped accepting new streams before this stream was established"_L1;
832 for (
quint32 id = lastStreamID;
id < nextID;
id += 2) {
833 const auto it = activeStreams.find(
id);
834 if (
it != activeStreams.end()) {
838 deleteActiveStream(
id);
840 removeFromSuspended(
id);
844 if (!activeStreams.size())
848void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
850 Q_ASSERT(inboundFrame.
type() == FrameType::WINDOW_UPDATE);
854 const bool valid = delta && delta <=
quint32(std::numeric_limits<qint32>::max());
855 const auto streamID = inboundFrame.
streamID();
860 return connectionError(
PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
861 sessionSendWindowSize =
sum;
863 auto it = activeStreams.find(streamID);
864 if (
it == activeStreams.end()) {
872 "invalid WINDOW_UPDATE delta"_L1);
874 markAsReset(streamID);
875 deleteActiveStream(streamID);
887void QHttp2ProtocolHandler::handleCONTINUATION()
889 Q_ASSERT(inboundFrame.
type() == FrameType::CONTINUATION);
892 if (inboundFrame.
streamID() != continuedFrames.front().streamID())
893 return connectionError(
PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
895 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
896 continuedFrames.push_back(std::move(inboundFrame));
901 continuationExpected =
false;
902 handleContinuedHEADERS();
905void QHttp2ProtocolHandler::handleContinuedHEADERS()
911 const auto firstFrameType = continuedFrames[0].type();
912 Q_ASSERT(firstFrameType == FrameType::HEADERS ||
913 firstFrameType == FrameType::PUSH_PROMISE);
915 const auto streamID = continuedFrames[0].streamID();
917 const auto streamIt = activeStreams.find(streamID);
918 if (firstFrameType == FrameType::HEADERS) {
919 if (streamIt != activeStreams.end()) {
928 "HEADERS on invalid stream"_L1);
929 sendRST_STREAM(streamID,
CANCEL);
930 markAsReset(streamID);
931 deleteActiveStream(streamID);
934 }
else if (!streamWasReset(streamID)) {
935 return connectionError(
PROTOCOL_ERROR,
"HEADERS on invalid stream");
943 const bool hasHeaderFields = !hpackBlock.empty();
944 if (hasHeaderFields) {
945 HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
948 }
else if (firstFrameType == FrameType::PUSH_PROMISE) {
961 switch (firstFrameType) {
962 case FrameType::HEADERS:
963 if (streamIt != activeStreams.end()) {
968 const bool needResend =
stream.request().d->needResendWithCredentials;
970 if (continuedFrames[0].
flags() & FrameFlag::END_STREAM || needResend) {
972 deleteActiveStream(
stream.streamID);
976 case FrameType::PUSH_PROMISE:
977 if (!tryReserveStream(continuedFrames[0], decoder.
decodedHeader()))
987 if (identifier == Settings::HEADER_TABLE_SIZE_ID) {
988 if (newValue > maxAcceptableTableSize) {
995 if (identifier == Settings::INITIAL_WINDOW_SIZE_ID) {
998 if (newValue >
quint32(std::numeric_limits<qint32>::max())) {
1003 const qint32 delta =
qint32(newValue) - streamInitialSendWindowSize;
1004 streamInitialSendWindowSize = newValue;
1006 std::vector<quint32> brokenStreams;
1007 brokenStreams.reserve(activeStreams.size());
1008 for (
auto &
stream : activeStreams) {
1011 brokenStreams.push_back(
stream.streamID);
1017 for (
auto id : brokenStreams) {
1020 "SETTINGS window overflow"_L1);
1023 deleteActiveStream(
id);
1029 if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID)
1030 maxConcurrentStreams = newValue;
1032 if (identifier == Settings::MAX_FRAME_SIZE_ID) {
1034 connectionError(
PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
1037 maxFrameSize = newValue;
1040 if (identifier == Settings::MAX_HEADER_LIST_SIZE_ID) {
1044 maxHeaderListSize = newValue;
1053 const auto httpReply =
stream.reply();
1054 auto &httpRequest =
stream.request();
1071 const auto httpReplyPrivate = httpReply->d_func();
1079 for (
const auto &pair : headers) {
1080 const auto &
name = pair.name;
1087 if (
name ==
":status") {
1088 statusCode =
value.left(3).toInt();
1089 httpReply->setStatusCode(statusCode);
1092 }
else if (
name ==
":version") {
1093 httpReply->setMajorVersion(
value.at(5) -
'0');
1094 httpReply->setMinorVersion(
value.at(7) -
'0');
1095 }
else if (
name ==
"content-length") {
1099 httpReply->setContentLength(
length);
1102 httpReply->appendHeaderField(
name,
QByteArray(pair.value).replace(
'\0', binder));
1108 if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
1109 httpReplyPrivate->clearHttpLayerInformation();
1115 m_connection->d_func()->parseRedirectResponse(httpReply);
1118 finishStreamWithError(
stream,
result.errorCode, errorString);
1120 markAsReset(
stream.streamID);
1124 if (
result.redirectUrl.isValid())
1125 httpReply->setRedirectUrl(
result.redirectUrl);
1128 if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
1129 httpReplyPrivate->removeAutoDecompressHeader();
1139 httpReplyPrivate->totallyUploadedData = 0;
1144 emit httpReply->headerChanged();
1153 auto httpReply =
stream.reply();
1165 const char *
data =
reinterpret_cast<const char *
>(
frame.dataBegin());
1166 auto replyPrivate = httpReply->d_func();
1168 replyPrivate->totalProgress +=
length;
1172 if (replyPrivate->shouldEmitSignals()) {
1174 emit httpReply->readyRead();
1175 emit httpReply->dataReadProgress(replyPrivate->totalProgress,
1176 replyPrivate->bodyLength);
1190void QHttp2ProtocolHandler::handleAuthorization(
Stream &
stream)
1192 auto *httpReply =
stream.reply();
1193 auto *httpReplyPrivate = httpReply->d_func();
1194 auto &httpRequest =
stream.request();
1196 Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
1198 const auto handleAuth = [&,
this](
QByteArrayView authField,
bool isProxy) ->
bool {
1201 if (auth.startsWith(
"Negotiate") || auth.startsWith(
"NTLM")) {
1211 bool resend =
false;
1212 const bool authenticateHandled =
m_connection->d_func()->handleAuthenticateChallenge(
1213 m_socket, httpReply, isProxy, resend);
1214 if (authenticateHandled) {
1216 httpReply->d_func()->eraseData();
1219 httpRequest.d->needResendWithCredentials =
true;
1221 httpReply->d_func()->clearHeaders();
1225 httpReplyPrivate->totallyUploadedData = 0;
1239 emit httpReply->headerChanged();
1240 emit httpReply->readyRead();
1255 switch (httpReply->statusCode()) {
1257 authOk = handleAuth(httpReply->headerField(
"www-authenticate"),
false);
1260 authOk = handleAuth(httpReply->headerField(
"proxy-authenticate"),
true);
1266 markAsReset(
stream.streamID);
1267 deleteActiveStream(
stream.streamID);
1277 auto httpReply =
stream.reply();
1279 int statusCode = httpReply->statusCode();
1280 if (statusCode == 401 || statusCode == 407) {
1284 handleAuthorization(
stream);
1288 httpReply->disconnect(
this);
1290 stream.data()->disconnect(
this);
1292 if (!
stream.request().d->needResendWithCredentials) {
1294 emit httpReply->finished();
1300 qCDebug(QT_HTTP2) <<
"stream" <<
stream.streamID <<
"closed";
1317 if (
auto httpReply =
stream.reply()) {
1318 httpReply->disconnect(
this);
1320 stream.data()->disconnect(
this);
1327 <<
"finished with error:" <<
message;
1332 const qint32 newStreamID = allocateStreamID();
1336 Q_ASSERT(!activeStreams.contains(newStreamID));
1339 const auto replyPrivate =
reply->d_func();
1341 replyPrivate->connectionChannel =
m_channel;
1342 reply->setHttp2WasUsed(
true);
1348 streamInitialSendWindowSize,
1349 streamInitialReceiveWindowSize);
1352 if (
auto src = newStream.data()) {
1356 this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed);
1363 activeStreams.insert(newStreamID, newStream);
1368void QHttp2ProtocolHandler::addToSuspended(
Stream &
stream)
1371 <<
"suspended by flow control";
1372 const auto priority =
stream.priority();
1373 Q_ASSERT(
int(priority) >= 0 &&
int(priority) < 3);
1374 suspendedStreams[priority].push_back(
stream.streamID);
1377void QHttp2ProtocolHandler::markAsReset(
quint32 streamID)
1381 qCDebug(QT_HTTP2) <<
"stream" << streamID <<
"was reset";
1384 if (recycledStreams.size() > maxRecycledStreams) {
1386 recycledStreams.erase(recycledStreams.begin(),
1387 recycledStreams.begin() +
1388 recycledStreams.size() / 2);
1391 const auto it = std::lower_bound(recycledStreams.begin(), recycledStreams.end(),
1393 if (
it != recycledStreams.end() && *
it == streamID)
1396 recycledStreams.insert(
it, streamID);
1399quint32 QHttp2ProtocolHandler::popStreamToResume()
1403 const QNR::Priority ranks[] = {QNR::HighPriority,
1404 QNR::NormalPriority,
1407 for (
const QNR::Priority rank : ranks) {
1408 auto &
queue = suspendedStreams[rank];
1411 auto stream = activeStreams.constFind(*
it);
1412 if (
stream == activeStreams.cend())
1414 if (
stream->sendWindow > 0)
1428void QHttp2ProtocolHandler::removeFromSuspended(
quint32 streamID)
1430 for (
auto &
q : suspendedStreams) {
1431 q.erase(std::remove(
q.begin(),
q.end(), streamID),
q.end());
1435void QHttp2ProtocolHandler::deleteActiveStream(
quint32 streamID)
1437 if (
const auto it = activeStreams.constFind(streamID);
it != activeStreams.cend()) {
1440 stream.reply()->disconnect(
this);
1444 stream.data()->disconnect(
this);
1447 activeStreams.erase(
it);
1450 removeFromSuspended(streamID);
1455bool QHttp2ProtocolHandler::streamWasReset(
quint32 streamID)
const
1457 const auto it = std::lower_bound(recycledStreams.begin(),
1458 recycledStreams.end(),
1460 return it != recycledStreams.end() && *
it == streamID;
1463void QHttp2ProtocolHandler::resumeSuspendedStreams()
1465 while (sessionSendWindowSize > 0) {
1466 const auto streamID = popStreamToResume();
1470 auto it = activeStreams.find(streamID);
1471 if (
it == activeStreams.end())
1477 "failed to send DATA"_L1);
1479 markAsReset(streamID);
1480 deleteActiveStream(streamID);
1485quint32 QHttp2ProtocolHandler::allocateStreamID()
1492 const quint32 streamID = nextID;
1498bool QHttp2ProtocolHandler::tryReserveStream(
const Http2::Frame &pushPromiseFrame,
1501 Q_ASSERT(pushPromiseFrame.type() == FrameType::PUSH_PROMISE);
1504 if (!
url.has_value())
1507 Q_ASSERT(activeStreams.contains(pushPromiseFrame.streamID()));
1508 const Stream &associatedStream = activeStreams[pushPromiseFrame.streamID()];
1510 const auto associatedUrl = urlkey_from_request(associatedStream.request());
1518 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.dataBegin());
1521 Q_ASSERT(!activeStreams.contains(reservedID));
1522 Q_ASSERT(!streamWasReset(reservedID));
1524 auto &promise = promisedData[urlKey];
1528 activeStreams.insert(reservedID,
Stream(urlKey, reservedID, streamInitialReceiveWindowSize));
1532void QHttp2ProtocolHandler::resetPromisedStream(
const Frame &pushPromiseFrame,
1535 Q_ASSERT(pushPromiseFrame.type() == FrameType::PUSH_PROMISE);
1536 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.dataBegin());
1537 sendRST_STREAM(reservedID, reason);
1538 markAsReset(reservedID);
1547 message.second->setHttp2WasUsed(
true);
1549 qCDebug(QT_HTTP2) <<
"found cached/promised response on stream" << promise.
reservedID;
1552 Stream *promisedStream =
nullptr;
1553 if (
auto it = activeStreams.find(promise.
reservedID);
it != activeStreams.end()) {
1554 promisedStream = &
it.value();
1561 streamInitialSendWindowSize,
1562 streamInitialReceiveWindowSize);
1564 it = activeStreams.insert(promise.
reservedID, closedStream);
1565 promisedStream = &
it.value();
1574 for (
const auto &
frame : promise.dataFrames)
1581 deleteActiveStream(promisedStream->
streamID);
1594 sendGOAWAY(errorCode);
1598 for (
auto &
stream: activeStreams)
1604void QHttp2ProtocolHandler::closeSession()
1606 activeStreams.clear();
1607 for (
auto &
q: suspendedStreams)
1609 recycledStreams.clear();
1616#include "moc_qhttp2protocolhandler_p.cpp"
DarwinBluetooth::RequestQueue requests
IOBluetoothL2CAPChannel * channel
const HttpHeader & decodedHeader() const
bool decodeHeaderFields(class BitIStream &inputStream)
void setCompressStrings(bool compress)
void setMaxDynamicTableSize(quint32 size)
bool encodeRequest(class BitOStream &outputStream, const HttpHeader &header)
FrameStatus read(QIODevice &socket)
void setPayloadSize(quint32 size)
bool writeHEADERS(QIODevice &socket, quint32 sizeLimit)
void addFlag(FrameFlag flag)
void append(ValueType val)
void setOutboundFrame(Frame &&newFrame)
bool writeDATA(QIODevice &socket, quint32 sizeLimit, const uchar *src, quint32 size)
void start(FrameType type, FrameFlags flags, quint32 streamID)
bool write(QIODevice &socket) const
QHttpNetworkConnectionChannel * m_channel
QHttpNetworkConnection * m_connection
QByteArrayView trimmed() const noexcept
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
bool remove(const Key &key)
Removes the item that has the key from the hash.
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
T value(const Key &key) const noexcept
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
Q_INVOKABLE void handleConnectionClosure()
Q_INVOKABLE void _q_receiveReply() override
Q_INVOKABLE bool sendRequest() override
void _q_readyRead() override
Q_INVOKABLE void ensureClientPrefaceSent()
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message)
QMultiMap< int, HttpMessagePair > h2RequestsToSend
void preConnectFinished()
QHttp2Configuration http2Parameters() const
ConnectionType connectionType() const
@ ConnectionTypeHTTP2Direct
static bool isHttpRedirect(int statusCode)
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
iterator insert(const Key &key, const T &value)
NetworkError
Indicates all possible error conditions found during the processing of the request.
@ ProxyAuthenticationRequiredError
@ AuthenticationRequiredError
QVariant header(KnownHeaders header) const
Returns the value of the known network header header if it is present in this request.
QUrl url() const
Returns the URL this network request is referring to.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
\macro QT_RESTRICTED_CAST_FROM_ASCII
QByteArray toLatin1() const &
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString authority(ComponentFormattingOptions options=PrettyDecoded) const
Returns the authority of the URL if it is defined; otherwise an empty string is returned.
QUrl adjusted(FormattingOptions options) const
QString scheme() const
Returns the scheme of the URL.
void setScheme(const QString &scheme)
Sets the scheme of the URL to scheme.
void setAuthority(const QString &authority, ParsingMode mode=TolerantMode)
Sets the authority of the URL to authority.
void setPath(const QString &path, ParsingMode mode=DecodedMode)
Sets the path of the URL to path.
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
QSet< QString >::iterator it
QPair< bool, quint32 > HeaderSize
std::vector< HeaderField > HttpHeader
HeaderSize header_size(const HttpHeader &header)
std::optional< QUrl > makePromiseKeyUrl(const HttpHeader &requestHeader)
HeaderSize entry_size(QByteArrayView name, QByteArrayView value)
std::vector< uchar > assemble_hpack_block(const std::vector< Frame > &frames)
const char Http2clientPreface[clientPrefaceLength]
const quint32 lastValidStreamID((quint32(1)<< 31) - 1)
@ defaultSessionWindowSize
Frame configurationToSettingsFrame(const QHttp2Configuration &config)
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorMessage)
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
static QByteArray cacheKey(Args &&...args)
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
#define Q_ARG(Type, data)
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLuint GLfloat weight
GLuint GLsizei const GLchar * message
GLdouble GLdouble GLdouble GLdouble q
QUrl url("example.com")
[constructor-url-reference]
QNetworkRequest request(url)
void replyFinished(QNetworkReply *reply)
[1]
bool priority(quint32 *streamID=nullptr, uchar *weight=nullptr) const
const uchar * dataBegin() const
quint32 payloadSize() const
std::vector< uchar > buffer
std::vector< Frame > dataFrames
HPack::HttpHeader responseHeader
HPack::HttpHeader pushHeader