46 #include <private/qnoncontiguousbytedevice_p.h>
48 #include <QtNetwork/qabstractsocket.h>
49 #include <QtCore/qloggingcategory.h>
50 #include <QtCore/qendian.h>
51 #include <QtCore/qdebug.h>
52 #include <QtCore/qlist.h>
53 #include <QtCore/qurl.h>
57 #ifndef QT_NO_NETWORKPROXY
58 #include <QtNetwork/qnetworkproxy.h>
74 using namespace HPack;
91 if (
size.second > maxHeaderListSize)
95 for (
const auto &field : requestHeader) {
99 if (std::numeric_limits<quint32>::max() - delta.second <
size.second)
101 size.second += delta.second;
102 if (
size.second > maxHeaderListSize)
124 std::vector<uchar> hpackBlock;
127 for (
const auto &
frame : frames)
128 total +=
frame.hpackBlockSize();
134 auto dst = hpackBlock.begin();
135 for (
const auto &
frame : frames) {
136 if (
const auto hpackBlockSize =
frame.hpackBlockSize()) {
138 std::copy(
src,
src + hpackBlockSize,
dst);
139 dst += hpackBlockSize;
160 return std::numeric_limits<qint32>::max() -
windowSize < delta;
161 return std::numeric_limits<qint32>::min() -
windowSize > delta;
167 using namespace Http2;
169 const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
170 const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
178 continuedFrames.reserve(20);
181 maxSessionReceiveWindowSize = h2Config.sessionReceiveWindowSize();
182 pushPromiseEnabled = h2Config.serverPushEnabled();
183 streamInitialReceiveWindowSize = h2Config.streamReceiveWindowSize();
215 for (
auto it = activeStreams.begin(), eIt = activeStreams.end();
it != eIt; ++
it)
219 activeStreams.clear();
229 void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
234 auto data = qobject_cast<QNonContiguousByteDevice *>(
sender());
238 Q_ASSERT(activeStreams.contains(streamID));
239 auto &
stream = activeStreams[streamID];
245 markAsReset(streamID);
246 deleteActiveStream(streamID);
250 void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *
reply)
253 if (activeStreams.contains(streamID)) {
254 sendRST_STREAM(streamID,
CANCEL);
255 markAsReset(streamID);
256 deleteActiveStream(streamID);
260 void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData)
262 streamIDs.
remove(uploadData);
265 void QHttp2ProtocolHandler::_q_readyRead()
267 if (!goingAway || activeStreams.size())
271 void QHttp2ProtocolHandler::_q_receiveReply()
276 if (goingAway && activeStreams.isEmpty()) {
281 while (!goingAway || activeStreams.size()) {
284 case FrameStatus::incompleteFrame:
286 case FrameStatus::protocolError:
288 case FrameStatus::sizeError:
298 const auto frameType = inboundFrame.
type();
299 if (continuationExpected && frameType != FrameType::CONTINUATION)
303 case FrameType::DATA:
306 case FrameType::HEADERS:
309 case FrameType::PRIORITY:
312 case FrameType::RST_STREAM:
315 case FrameType::SETTINGS:
318 case FrameType::PUSH_PROMISE:
319 handlePUSH_PROMISE();
321 case FrameType::PING:
324 case FrameType::GOAWAY:
327 case FrameType::WINDOW_UPDATE:
328 handleWINDOW_UPDATE();
330 case FrameType::CONTINUATION:
331 handleCONTINUATION();
333 case FrameType::LAST_FRAME_TYPE:
340 bool QHttp2ProtocolHandler::sendRequest()
346 "GOAWAY received, cannot start a request");
354 for (
auto it = requests.begin(), endIt = requests.end();
it != endIt;) {
355 const auto &pair = *
it;
356 const QString scheme(pair.first.url().scheme());
360 emit pair.second->finished();
361 it = requests.erase(
it);
362 if (!requests.size()) {
374 if (!prefaceSent && !sendClientPreface())
377 if (!requests.size())
384 for (
auto it = requests.begin(), endIt = requests.end();
it != endIt;) {
392 it = requests.erase(
it);
396 const auto streamsToUse = std::min<quint32>(maxConcurrentStreams >
quint32(activeStreams.size())
397 ? maxConcurrentStreams -
quint32(activeStreams.size()) : 0,
399 auto it = requests.begin();
401 const qint32 newStreamID = createNewStream(*
it);
404 qCCritical(QT_HTTP2,
"sendRequest: out of stream IDs");
408 it = requests.erase(
it);
410 Stream &newStream = activeStreams[newStreamID];
411 if (!sendHEADERS(newStream)) {
414 deleteActiveStream(newStreamID);
418 if (newStream.
data() && !sendDATA(newStream)) {
422 markAsReset(newStreamID);
423 deleteActiveStream(newStreamID);
433 bool QHttp2ProtocolHandler::sendClientPreface()
453 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
461 waitingForSettingsACK =
true;
466 bool QHttp2ProtocolHandler::sendSETTINGS_ACK()
470 if (!prefaceSent && !sendClientPreface())
480 using namespace HPack;
482 frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
486 frameWriter.addFlag(FrameFlag::END_STREAM);
493 frameWriter.append(
stream.weight());
495 bool useProxy =
false;
496 #ifndef QT_NO_NETWORKPROXY
499 if (
stream.request().withCredentials()) {
501 stream.request().d->needResendWithCredentials =
false;
508 BitOStream outputStream(frameWriter.outboundFrame().buffer);
509 if (!encoder.encodeRequest(outputStream, headers))
512 return frameWriter.writeHEADERS(*
m_socket, maxFrameSize);
524 const auto replyPrivate =
reply->d_func();
527 auto slot = std::min<qint32>(sessionSendWindowSize,
stream.sendWindow);
528 while (replyPrivate->totallyUploadedData <
request.contentLength() && slot) {
531 reinterpret_cast<const uchar *
>(
stream.data()->readPointer(slot, chunkSize));
536 if (!
src || !chunkSize) {
542 frameWriter.
start(FrameType::DATA, FrameFlag::EMPTY,
stream.streamID);
552 emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
554 slot = std::min(sessionSendWindowSize,
stream.sendWindow);
557 if (replyPrivate->totallyUploadedData ==
request.contentLength()) {
558 frameWriter.
start(FrameType::DATA, FrameFlag::END_STREAM,
stream.streamID);
562 stream.data()->disconnect(
this);
563 removeFromSuspended(
stream.streamID);
564 }
else if (!
stream.data()->atEnd()) {
571 bool QHttp2ProtocolHandler::sendWINDOW_UPDATE(
quint32 streamID,
quint32 delta)
575 frameWriter.
start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
576 frameWriter.
append(delta);
580 bool QHttp2ProtocolHandler::sendRST_STREAM(
quint32 streamID,
quint32 errorCode)
584 frameWriter.
start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
585 frameWriter.
append(errorCode);
589 bool QHttp2ProtocolHandler::sendGOAWAY(
quint32 errorCode)
595 frameWriter.
append(errorCode);
599 void QHttp2ProtocolHandler::handleDATA()
603 const auto streamID = inboundFrame.
streamID();
607 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
613 sessionReceiveWindowSize -= inboundFrame.
payloadSize();
615 if (activeStreams.contains(streamID)) {
616 auto &
stream = activeStreams[streamID];
622 markAsReset(streamID);
623 deleteActiveStream(streamID);
627 updateStream(
stream, inboundFrame);
629 if (inboundFrame.
flags().testFlag(FrameFlag::END_STREAM)) {
631 deleteActiveStream(
stream.streamID);
632 }
else if (
stream.recvWindow < streamInitialReceiveWindowSize / 2) {
636 stream.recvWindow = streamInitialReceiveWindowSize;
641 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
644 Q_ARG(
quint32, maxSessionReceiveWindowSize - sessionReceiveWindowSize));
645 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
649 void QHttp2ProtocolHandler::handleHEADERS()
653 const auto streamID = inboundFrame.
streamID();
657 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
661 if (
flags.testFlag(FrameFlag::PRIORITY)) {
667 const bool endHeaders =
flags.testFlag(FrameFlag::END_HEADERS);
668 continuedFrames.clear();
669 continuedFrames.push_back(std::move(inboundFrame));
671 continuationExpected =
true;
675 handleContinuedHEADERS();
678 void QHttp2ProtocolHandler::handlePRIORITY()
681 inboundFrame.
type() == FrameType::HEADERS);
683 const auto streamID = inboundFrame.
streamID();
687 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
692 const bool noErr = inboundFrame.
priority(&streamDependency, &
weight);
697 const bool exclusive = streamDependency & 0x80000000;
698 streamDependency &= ~0x80000000;
706 void QHttp2ProtocolHandler::handleRST_STREAM()
714 const auto streamID = inboundFrame.
streamID();
718 if (!(streamID & 0x1)) {
725 if (streamID >= nextID) {
729 return connectionError(
PROTOCOL_ERROR,
"RST_STREAM on idle stream");
732 if (!activeStreams.contains(streamID)) {
740 finishStreamWithError(
stream, qFromBigEndian<quint32>(inboundFrame.
dataBegin()));
741 markAsReset(
stream.streamID);
742 deleteActiveStream(
stream.streamID);
745 void QHttp2ProtocolHandler::handleSETTINGS()
751 return connectionError(
PROTOCOL_ERROR,
"SETTINGS on invalid stream");
753 if (inboundFrame.
flags().testFlag(FrameFlag::ACK)) {
754 if (!waitingForSettingsACK)
755 return connectionError(
PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
756 waitingForSettingsACK =
false;
764 const quint32 intVal = qFromBigEndian<quint32>(
src + 2);
765 if (!acceptSetting(identifier, intVal)) {
776 void QHttp2ProtocolHandler::handlePUSH_PROMISE()
779 Q_ASSERT(inboundFrame.
type() == FrameType::PUSH_PROMISE);
781 if (!pushPromiseEnabled && prefaceSent && !waitingForSettingsACK) {
784 return connectionError(
PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
787 const auto streamID = inboundFrame.
streamID();
790 "PUSH_PROMISE with invalid associated stream (0x0)");
793 if (!activeStreams.contains(streamID) && !streamWasReset(streamID)) {
795 "PUSH_PROMISE with invalid associated stream");
798 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.
dataBegin());
799 if ((reservedID & 1) || reservedID <= lastPromisedID ||
802 "PUSH_PROMISE with invalid promised stream ID");
805 lastPromisedID = reservedID;
807 if (!pushPromiseEnabled) {
813 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
814 continuedFrames.clear();
815 continuedFrames.push_back(std::move(inboundFrame));
818 continuationExpected =
true;
822 handleContinuedHEADERS();
825 void QHttp2ProtocolHandler::handlePING()
835 if (inboundFrame.
flags() & FrameFlag::ACK)
845 void QHttp2ProtocolHandler::handleGOAWAY()
853 return connectionError(
PROTOCOL_ERROR,
"GOAWAY on invalid stream");
856 quint32 lastStreamID = qFromBigEndian<quint32>(
src);
857 const quint32 errorCode = qFromBigEndian<quint32>(
src + 4);
863 }
else if (!(lastStreamID & 0x1)) {
865 return connectionError(
PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
866 }
else if (lastStreamID >= nextID) {
871 return connectionError(
PROTOCOL_ERROR,
"GOAWAY invalid stream/error code");
881 "GOAWAY received, cannot start a request");
894 message =
QLatin1String(
"Server stopped accepting new streams before this stream was established");
897 for (
quint32 id = lastStreamID;
id < nextID;
id += 2) {
898 const auto it = activeStreams.find(
id);
899 if (
it != activeStreams.end()) {
903 deleteActiveStream(
id);
905 removeFromSuspended(
id);
909 if (!activeStreams.size())
913 void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
915 Q_ASSERT(inboundFrame.
type() == FrameType::WINDOW_UPDATE);
919 const bool valid = delta && delta <=
quint32(std::numeric_limits<qint32>::max());
920 const auto streamID = inboundFrame.
streamID();
924 return connectionError(
PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
925 sessionSendWindowSize += delta;
927 if (!activeStreams.contains(streamID)) {
931 auto &
stream = activeStreams[streamID];
936 markAsReset(streamID);
937 deleteActiveStream(streamID);
940 stream.sendWindow += delta;
949 void QHttp2ProtocolHandler::handleCONTINUATION()
951 Q_ASSERT(inboundFrame.
type() == FrameType::CONTINUATION);
954 if (inboundFrame.
streamID() != continuedFrames.front().streamID())
955 return connectionError(
PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
957 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
958 continuedFrames.push_back(std::move(inboundFrame));
963 continuationExpected =
false;
964 handleContinuedHEADERS();
967 void QHttp2ProtocolHandler::handleContinuedHEADERS()
973 const auto firstFrameType = continuedFrames[0].type();
974 Q_ASSERT(firstFrameType == FrameType::HEADERS ||
975 firstFrameType == FrameType::PUSH_PROMISE);
977 const auto streamID = continuedFrames[0].streamID();
979 if (firstFrameType == FrameType::HEADERS) {
980 if (activeStreams.contains(streamID)) {
990 sendRST_STREAM(streamID,
CANCEL);
991 markAsReset(streamID);
992 deleteActiveStream(streamID);
995 }
else if (!streamWasReset(streamID)) {
996 return connectionError(
PROTOCOL_ERROR,
"HEADERS on invalid stream");
1004 if (!hpackBlock.size()) {
1013 if (firstFrameType == FrameType::PUSH_PROMISE)
1019 HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
1023 switch (firstFrameType) {
1024 case FrameType::HEADERS:
1025 if (activeStreams.contains(streamID)) {
1029 const bool needResend =
stream.request().d->needResendWithCredentials;
1031 if (continuedFrames[0].
flags() & FrameFlag::END_STREAM || needResend) {
1033 deleteActiveStream(
stream.streamID);
1037 case FrameType::PUSH_PROMISE:
1038 if (!tryReserveStream(continuedFrames[0], decoder.
decodedHeader()))
1048 if (identifier == Settings::HEADER_TABLE_SIZE_ID) {
1049 if (newValue > maxAcceptableTableSize) {
1056 if (identifier == Settings::INITIAL_WINDOW_SIZE_ID) {
1059 if (newValue >
quint32(std::numeric_limits<qint32>::max())) {
1064 const qint32 delta =
qint32(newValue) - streamInitialSendWindowSize;
1065 streamInitialSendWindowSize = newValue;
1067 std::vector<quint32> brokenStreams;
1068 brokenStreams.reserve(activeStreams.size());
1069 for (
auto &
stream : activeStreams) {
1071 brokenStreams.push_back(
stream.streamID);
1074 stream.sendWindow += delta;
1077 for (
auto id : brokenStreams) {
1083 deleteActiveStream(
id);
1089 if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID)
1090 maxConcurrentStreams = newValue;
1092 if (identifier == Settings::MAX_FRAME_SIZE_ID) {
1094 connectionError(
PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
1097 maxFrameSize = newValue;
1100 if (identifier == Settings::MAX_HEADER_LIST_SIZE_ID) {
1104 maxHeaderListSize = newValue;
1113 const auto httpReply =
stream.reply();
1114 auto &httpRequest =
stream.request();
1131 const auto httpReplyPrivate = httpReply->d_func();
1139 for (
const auto &pair : headers) {
1140 const auto &
name = pair.name;
1141 auto value = pair.value;
1147 if (
name ==
":status") {
1148 statusCode =
value.left(3).toInt();
1149 httpReply->setStatusCode(statusCode);
1152 }
else if (
name ==
":version") {
1153 httpReply->setMajorVersion(
value.at(5) -
'0');
1154 httpReply->setMinorVersion(
value.at(7) -
'0');
1155 }
else if (
name ==
"content-length") {
1159 httpReply->setContentLength(
length);
1162 if (
name ==
"set-cookie")
1164 httpReply->appendHeaderField(
name,
value.replace(
'\0', binder));
1168 const auto handleAuth = [&,
this](
const QByteArray &authField,
bool isProxy) ->
bool {
1170 const auto auth = authField.
trimmed();
1171 if (auth.startsWith(
"Negotiate") || auth.startsWith(
"NTLM")) {
1178 }
else if (!auth.isEmpty()) {
1180 bool resend =
false;
1181 const bool authenticateHandled =
m_connection->d_func()->handleAuthenticateChallenge(
1182 m_socket, httpReply, isProxy, resend);
1183 if (authenticateHandled && resend) {
1184 httpReply->d_func()->eraseData();
1187 httpRequest.d->needResendWithCredentials =
true;
1189 httpReply->d_func()->clearHeaders();
1193 httpReplyPrivate->totallyUploadedData = 0;
1206 if (httpReply->statusCode() == 401) {
1207 const auto wwwAuth = httpReply->headerField(
"www-authenticate");
1208 if (handleAuth(wwwAuth,
false)) {
1210 markAsReset(
stream.streamID);
1214 }
else if (httpReply->statusCode() == 407) {
1215 const auto proxyAuth = httpReply->headerField(
"proxy-authenticate");
1216 if (handleAuth(proxyAuth,
true)) {
1218 markAsReset(
stream.streamID);
1227 m_connection->d_func()->parseRedirectResponse(httpReply);
1230 finishStreamWithError(
stream,
result.errorCode, errorString);
1232 markAsReset(
stream.streamID);
1236 if (
result.redirectUrl.isValid())
1237 httpReply->setRedirectUrl(
result.redirectUrl);
1240 if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
1241 httpReplyPrivate->removeAutoDecompressHeader();
1251 httpReplyPrivate->totallyUploadedData = 0;
1256 emit httpReply->headerChanged();
1265 auto httpReply =
stream.reply();
1277 const char *
data =
reinterpret_cast<const char *
>(
frame.dataBegin());
1278 auto replyPrivate = httpReply->d_func();
1280 replyPrivate->totalProgress +=
length;
1283 replyPrivate->responseData.append(wrapped);
1285 if (replyPrivate->shouldEmitSignals()) {
1287 emit httpReply->readyRead();
1288 emit httpReply->dataReadProgress(replyPrivate->totalProgress,
1289 replyPrivate->bodyLength);
1305 auto httpReply =
stream.reply();
1307 httpReply->disconnect(
this);
1309 stream.data()->disconnect(
this);
1311 if (!
stream.request().d->needResendWithCredentials) {
1313 emit httpReply->finished();
1319 qCDebug(QT_HTTP2) <<
"stream" <<
stream.streamID <<
"closed";
1336 if (
auto httpReply =
stream.reply()) {
1337 httpReply->disconnect(
this);
1339 stream.data()->disconnect(
this);
1346 <<
"finished with error:" <<
message;
1351 const qint32 newStreamID = allocateStreamID();
1355 Q_ASSERT(!activeStreams.contains(newStreamID));
1358 const auto replyPrivate =
reply->d_func();
1360 replyPrivate->connectionChannel =
m_channel;
1361 reply->setHttp2WasUsed(
true);
1367 streamInitialSendWindowSize,
1368 streamInitialReceiveWindowSize);
1371 if (
auto src = newStream.
data()) {
1375 this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed);
1382 activeStreams.insert(newStreamID, newStream);
1387 void QHttp2ProtocolHandler::addToSuspended(
Stream &
stream)
1390 <<
"suspended by flow control";
1391 const auto priority =
stream.priority();
1392 Q_ASSERT(
int(priority) >= 0 &&
int(priority) < 3);
1393 suspendedStreams[priority].push_back(
stream.streamID);
1396 void QHttp2ProtocolHandler::markAsReset(
quint32 streamID)
1400 qCDebug(QT_HTTP2) <<
"stream" << streamID <<
"was reset";
1403 if (recycledStreams.size() > maxRecycledStreams) {
1405 recycledStreams.erase(recycledStreams.begin(),
1406 recycledStreams.begin() +
1407 recycledStreams.size() / 2);
1410 const auto it = std::lower_bound(recycledStreams.begin(), recycledStreams.end(),
1412 if (
it != recycledStreams.end() && *
it == streamID)
1415 recycledStreams.insert(
it, streamID);
1418 quint32 QHttp2ProtocolHandler::popStreamToResume()
1422 const QNR::Priority ranks[] = {QNR::HighPriority,
1423 QNR::NormalPriority,
1426 for (
const QNR::Priority rank : ranks) {
1427 auto &
queue = suspendedStreams[rank];
1430 if (!activeStreams.contains(*
it))
1432 if (activeStreams[*
it].sendWindow > 0)
1446 void QHttp2ProtocolHandler::removeFromSuspended(
quint32 streamID)
1448 for (
auto &
q : suspendedStreams) {
1453 void QHttp2ProtocolHandler::deleteActiveStream(
quint32 streamID)
1455 if (activeStreams.contains(streamID)) {
1456 auto &
stream = activeStreams[streamID];
1458 stream.reply()->disconnect(
this);
1462 stream.data()->disconnect(
this);
1465 activeStreams.remove(streamID);
1468 removeFromSuspended(streamID);
1473 bool QHttp2ProtocolHandler::streamWasReset(
quint32 streamID)
const
1475 const auto it = std::lower_bound(recycledStreams.begin(),
1476 recycledStreams.end(),
1478 return it != recycledStreams.end() && *
it == streamID;
1481 void QHttp2ProtocolHandler::resumeSuspendedStreams()
1483 while (sessionSendWindowSize > 0) {
1484 const auto streamID = popStreamToResume();
1488 if (!activeStreams.contains(streamID))
1496 markAsReset(streamID);
1497 deleteActiveStream(streamID);
1502 quint32 QHttp2ProtocolHandler::allocateStreamID()
1509 const quint32 streamID = nextID;
1515 bool QHttp2ProtocolHandler::tryReserveStream(
const Http2::Frame &pushPromiseFrame,
1518 Q_ASSERT(pushPromiseFrame.
type() == FrameType::PUSH_PROMISE);
1521 for (
const auto &field : requestHeader) {
1522 if (field.name ==
":scheme" || field.name ==
":path"
1523 || field.name ==
":authority" || field.name ==
":method") {
1524 if (field.value.isEmpty() || pseudoHeaders.
contains(field.name))
1526 pseudoHeaders[field.name] = field.
value;
1530 if (pseudoHeaders.
size() != 4) {
1549 const Stream &associatedStream = activeStreams[pushPromiseFrame.
streamID()];
1555 const auto urlKey =
url.toString();
1559 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.
dataBegin());
1562 Q_ASSERT(!activeStreams.contains(reservedID));
1563 Q_ASSERT(!streamWasReset(reservedID));
1565 auto &promise = promisedData[urlKey];
1569 activeStreams.insert(reservedID,
Stream(urlKey, reservedID, streamInitialReceiveWindowSize));
1573 void QHttp2ProtocolHandler::resetPromisedStream(
const Frame &pushPromiseFrame,
1576 Q_ASSERT(pushPromiseFrame.
type() == FrameType::PUSH_PROMISE);
1577 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.
dataBegin());
1578 sendRST_STREAM(reservedID, reason);
1579 markAsReset(reservedID);
1586 auto promise = promisedData.
take(cacheKey);
1588 message.second->setHttp2WasUsed(
true);
1590 qCDebug(QT_HTTP2) <<
"found cached/promised response on stream" << promise.
reservedID;
1593 Stream *promisedStream =
nullptr;
1594 if (activeStreams.contains(promise.
reservedID)) {
1595 promisedStream = &activeStreams[promise.
reservedID];
1602 streamInitialSendWindowSize,
1603 streamInitialReceiveWindowSize);
1605 activeStreams.insert(promise.
reservedID, closedStream);
1606 promisedStream = &activeStreams[promise.
reservedID];
1622 deleteActiveStream(promisedStream->
streamID);
1635 sendGOAWAY(errorCode);
1639 for (
auto &
stream: activeStreams)
1645 void QHttp2ProtocolHandler::closeSession()
1647 activeStreams.clear();
1648 for (
auto &
q: suspendedStreams)
1650 recycledStreams.clear();
small capitals from c petite p scientific i
[1]
bool decodeHeaderFields(class BitIStream &inputStream)
const HttpHeader & decodedHeader() const
void setCompressStrings(bool compress)
void setMaxDynamicTableSize(quint32 size)
FrameStatus read(QAbstractSocket &socket)
void setPayloadSize(quint32 size)
void append(ValueType val)
void setOutboundFrame(Frame &&newFrame)
bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit, const uchar *src, quint32 size)
void start(FrameType type, FrameFlags flags, quint32 streamID)
bool write(QAbstractSocket &socket) const
QAbstractSocket * m_socket
QHttpNetworkConnectionChannel * m_channel
QHttpNetworkConnection * m_connection
The QByteArray class provides an array of bytes.
QByteArray trimmed() const &
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
bool remove(const Key &key)
bool contains(const Key &key) const noexcept
T value(const Key &key) const noexcept
iterator insert(const Key &key, const T &value)
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
Q_INVOKABLE void handleConnectionClosure()
Q_INVOKABLE void ensureClientPrefaceSent()
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message)
QHttpNetworkReply * reply
QHttpNetworkRequest request
QMultiMap< int, HttpMessagePair > h2RequestsToSend
void preConnectFinished()
QHttp2Configuration http2Parameters() const
ConnectionType connectionType()
@ ConnectionTypeHTTP2Direct
static bool isHttpRedirect(int statusCode)
qint64 write(const char *data, qint64 len)
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
T value(const Key &key, const T &defaultValue=T()) const
bool contains(const Key &key) const
iterator insert(const Key &key, const T &value)
QVariant header(KnownHeaders header) const
Q_INVOKABLE QObject(QObject *parent=nullptr)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
void destroyed(QObject *=nullptr)
The QString class provides a Unicode character string.
static QString fromLatin1(QByteArrayView ba)
QUrl(const QString &url, ParsingMode parsingMode)
std::vector< HeaderField > HttpHeader
HeaderSize header_size(const HttpHeader &header)
HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
QPair< bool, quint32 > HeaderSize
Frame configurationToSettingsFrame(const QHttp2Configuration &config)
const char Http2clientPreface[clientPrefaceLength]
const quint32 lastValidStreamID((quint32(1)<< 31) - 1)
@ defaultSessionWindowSize
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorMessage)
HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxHeaderListSize, bool useProxy)
bool sum_will_overflow(qint32 windowSize, qint32 delta)
std::vector< uchar > assemble_hpack_block(const std::vector< Http2::Frame > &frames)
QUrl urlkey_from_request(const QHttpNetworkRequest &request)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
EGLOutputLayerEXT EGLint EGLAttrib value
QT_BEGIN_INCLUDE_NAMESPACE typedef unsigned char uchar
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_ARG(type, data)
GLenum GLuint GLenum GLsizei length
GLenum GLuint GLenum GLsizei const GLchar * message
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint GLfloat weight
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLdouble GLdouble GLdouble GLdouble q
settings remove("monkey")
QUrl url("http://www.example.com/List of holidays.xml")
[0]
false readyRead(responseheader) dataReadProgress(18300
QHttpRequestHeader header("GET", QUrl::toPercentEncoding("/index.html"))
[1]
QNetworkRequest request(url)
void replyFinished(QNetworkReply *reply)
[1]
const uchar * dataBegin() const
bool priority(quint32 *streamID=nullptr, uchar *weight=nullptr) const
quint32 payloadSize() const
std::vector< Frame > dataFrames
HPack::HttpHeader responseHeader
HPack::HttpHeader pushHeader
QNonContiguousByteDevice * data() const
const QHttpNetworkRequest & request() const