10#include <private/qnoncontiguousbytedevice_p.h>
12#include <QtNetwork/qabstractsocket.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qendian.h>
15#include <QtCore/qdebug.h>
16#include <QtCore/qlist.h>
17#include <QtCore/qurl.h>
21#ifndef QT_NO_NETWORKPROXY
22#include <QtNetwork/qnetworkproxy.h>
40 using namespace HPack;
48 header.emplace_back(
":authority", auth);
57 if (
size.second > maxHeaderListSize)
61 for (
const auto &field : requestHeader) {
65 if (std::numeric_limits<quint32>::max() - delta.second <
size.second)
67 size.second += delta.second;
68 if (
size.second > maxHeaderListSize)
82 header.emplace_back(field.first.toLower(), field.second);
88std::vector<uchar> assemble_hpack_block(
const std::vector<Http2::Frame> &frames)
90 std::vector<uchar> hpackBlock;
93 for (
const auto &
frame : frames)
94 total +=
frame.hpackBlockSize();
100 auto dst = hpackBlock.begin();
101 for (
const auto &
frame : frames) {
102 if (
const auto hpackBlockSize =
frame.hpackBlockSize()) {
104 std::copy(
src,
src + hpackBlockSize,
dst);
105 dst += hpackBlockSize;
126using namespace Http2;
134 continuedFrames.reserve(20);
137 maxSessionReceiveWindowSize = h2Config.sessionReceiveWindowSize();
138 pushPromiseEnabled = h2Config.serverPushEnabled();
139 streamInitialReceiveWindowSize = h2Config.streamReceiveWindowSize();
171 for (
auto it = activeStreams.begin(), eIt = activeStreams.end();
it != eIt; ++
it)
175 activeStreams.clear();
185void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
190 auto data = qobject_cast<QNonContiguousByteDevice *>(
sender());
194 Q_ASSERT(activeStreams.contains(streamID));
195 auto &
stream = activeStreams[streamID];
200 markAsReset(streamID);
201 deleteActiveStream(streamID);
205void QHttp2ProtocolHandler::_q_replyDestroyed(
QObject *
reply)
208 if (activeStreams.contains(streamID)) {
209 sendRST_STREAM(streamID,
CANCEL);
210 markAsReset(streamID);
211 deleteActiveStream(streamID);
215void QHttp2ProtocolHandler::_q_uploadDataDestroyed(
QObject *uploadData)
217 streamIDs.
remove(uploadData);
222 if (!goingAway || activeStreams.size())
231 if (goingAway && activeStreams.isEmpty()) {
236 while (!goingAway || activeStreams.size()) {
239 case FrameStatus::incompleteFrame:
241 case FrameStatus::protocolError:
243 case FrameStatus::sizeError:
253 const auto frameType = inboundFrame.
type();
254 if (continuationExpected && frameType != FrameType::CONTINUATION)
258 case FrameType::DATA:
261 case FrameType::HEADERS:
264 case FrameType::PRIORITY:
267 case FrameType::RST_STREAM:
270 case FrameType::SETTINGS:
273 case FrameType::PUSH_PROMISE:
274 handlePUSH_PROMISE();
276 case FrameType::PING:
279 case FrameType::GOAWAY:
282 case FrameType::WINDOW_UPDATE:
283 handleWINDOW_UPDATE();
285 case FrameType::CONTINUATION:
286 handleCONTINUATION();
288 case FrameType::LAST_FRAME_TYPE:
301 "GOAWAY received, cannot start a request");
310 const auto &pair = *
it;
311 const QString scheme(pair.first.url().scheme());
312 if (scheme ==
"preconnect-http"_L1 || scheme ==
"preconnect-https"_L1) {
314 emit pair.second->finished();
328 if (!prefaceSent && !sendClientPreface())
339 const auto key = urlkey_from_request(
it->first).toString();
350 const auto streamsToUse = std::min<quint32>(maxConcurrentStreams >
quint32(activeStreams.size())
351 ? maxConcurrentStreams -
quint32(activeStreams.size()) : 0,
355 const qint32 newStreamID = createNewStream(*
it);
358 qCCritical(QT_HTTP2,
"sendRequest: out of stream IDs");
364 Stream &newStream = activeStreams[newStreamID];
365 if (!sendHEADERS(newStream)) {
367 "failed to send HEADERS frame(s)"_L1);
368 deleteActiveStream(newStreamID);
372 if (newStream.
data() && !sendDATA(newStream)) {
374 "failed to send DATA frame(s)"_L1);
376 markAsReset(newStreamID);
377 deleteActiveStream(newStreamID);
387bool QHttp2ProtocolHandler::sendClientPreface()
407 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
415 waitingForSettingsACK =
true;
420bool QHttp2ProtocolHandler::sendSETTINGS_ACK()
424 if (!prefaceSent && !sendClientPreface())
434 using namespace HPack;
436 frameWriter.
start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
440 frameWriter.
addFlag(FrameFlag::END_STREAM);
449 bool useProxy =
false;
450#ifndef QT_NO_NETWORKPROXY
453 if (
stream.request().withCredentials()) {
455 stream.request().d->needResendWithCredentials =
false;
457 const auto headers = build_headers(
stream.request(), maxHeaderListSize, useProxy);
478 const auto replyPrivate =
reply->d_func();
481 auto slot = std::min<qint32>(sessionSendWindowSize,
stream.sendWindow);
482 while (replyPrivate->totallyUploadedData <
request.contentLength() && slot) {
485 reinterpret_cast<const uchar *
>(
stream.data()->readPointer(slot, chunkSize));
490 if (!
src || !chunkSize) {
496 frameWriter.
start(FrameType::DATA, FrameFlag::EMPTY,
stream.streamID);
506 emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
508 slot = std::min(sessionSendWindowSize,
stream.sendWindow);
511 if (replyPrivate->totallyUploadedData ==
request.contentLength()) {
512 frameWriter.
start(FrameType::DATA, FrameFlag::END_STREAM,
stream.streamID);
516 stream.data()->disconnect(
this);
517 removeFromSuspended(
stream.streamID);
518 }
else if (!
stream.data()->atEnd()) {
525bool QHttp2ProtocolHandler::sendWINDOW_UPDATE(
quint32 streamID,
quint32 delta)
529 frameWriter.
start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
530 frameWriter.
append(delta);
534bool QHttp2ProtocolHandler::sendRST_STREAM(
quint32 streamID,
quint32 errorCode)
538 frameWriter.
start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
539 frameWriter.
append(errorCode);
543bool QHttp2ProtocolHandler::sendGOAWAY(
quint32 errorCode)
549 frameWriter.
append(errorCode);
553void QHttp2ProtocolHandler::handleDATA()
557 const auto streamID = inboundFrame.
streamID();
561 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
567 sessionReceiveWindowSize -= inboundFrame.
payloadSize();
569 auto it = activeStreams.find(streamID);
570 if (
it != activeStreams.end()) {
576 markAsReset(streamID);
577 deleteActiveStream(streamID);
581 updateStream(
stream, inboundFrame);
583 if (inboundFrame.
flags().testFlag(FrameFlag::END_STREAM)) {
585 deleteActiveStream(
stream.streamID);
586 }
else if (
stream.recvWindow < streamInitialReceiveWindowSize / 2) {
590 stream.recvWindow = streamInitialReceiveWindowSize;
595 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
598 Q_ARG(
quint32, maxSessionReceiveWindowSize - sessionReceiveWindowSize));
599 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
603void QHttp2ProtocolHandler::handleHEADERS()
607 const auto streamID = inboundFrame.
streamID();
611 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
615 if (
flags.testFlag(FrameFlag::PRIORITY)) {
621 const bool endHeaders =
flags.testFlag(FrameFlag::END_HEADERS);
622 continuedFrames.clear();
623 continuedFrames.push_back(std::move(inboundFrame));
625 continuationExpected =
true;
629 handleContinuedHEADERS();
632void QHttp2ProtocolHandler::handlePRIORITY()
635 inboundFrame.
type() == FrameType::HEADERS);
637 const auto streamID = inboundFrame.
streamID();
641 if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
646 const bool noErr = inboundFrame.
priority(&streamDependency, &
weight);
651 const bool exclusive = streamDependency & 0x80000000;
652 streamDependency &= ~0x80000000;
660void QHttp2ProtocolHandler::handleRST_STREAM()
668 const auto streamID = inboundFrame.
streamID();
672 if (!(streamID & 0x1)) {
679 if (streamID >= nextID) {
683 return connectionError(
PROTOCOL_ERROR,
"RST_STREAM on idle stream");
686 if (!activeStreams.contains(streamID)) {
694 finishStreamWithError(
stream, qFromBigEndian<quint32>(inboundFrame.
dataBegin()));
695 markAsReset(
stream.streamID);
696 deleteActiveStream(
stream.streamID);
699void QHttp2ProtocolHandler::handleSETTINGS()
705 return connectionError(
PROTOCOL_ERROR,
"SETTINGS on invalid stream");
707 if (inboundFrame.
flags().testFlag(FrameFlag::ACK)) {
708 if (!waitingForSettingsACK)
709 return connectionError(
PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
710 waitingForSettingsACK =
false;
718 const quint32 intVal = qFromBigEndian<quint32>(
src + 2);
719 if (!acceptSetting(identifier, intVal)) {
730void QHttp2ProtocolHandler::handlePUSH_PROMISE()
733 Q_ASSERT(inboundFrame.
type() == FrameType::PUSH_PROMISE);
735 if (!pushPromiseEnabled && prefaceSent && !waitingForSettingsACK) {
738 return connectionError(
PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
741 const auto streamID = inboundFrame.
streamID();
744 "PUSH_PROMISE with invalid associated stream (0x0)");
747 if (!activeStreams.contains(streamID) && !streamWasReset(streamID)) {
749 "PUSH_PROMISE with invalid associated stream");
752 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.
dataBegin());
753 if ((reservedID & 1) || reservedID <= lastPromisedID ||
756 "PUSH_PROMISE with invalid promised stream ID");
759 lastPromisedID = reservedID;
761 if (!pushPromiseEnabled) {
767 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
768 continuedFrames.clear();
769 continuedFrames.push_back(std::move(inboundFrame));
772 continuationExpected =
true;
776 handleContinuedHEADERS();
779void QHttp2ProtocolHandler::handlePING()
789 if (inboundFrame.
flags() & FrameFlag::ACK)
799void QHttp2ProtocolHandler::handleGOAWAY()
807 return connectionError(
PROTOCOL_ERROR,
"GOAWAY on invalid stream");
810 quint32 lastStreamID = qFromBigEndian<quint32>(
src);
811 const quint32 errorCode = qFromBigEndian<quint32>(
src + 4);
817 }
else if (!(lastStreamID & 0x1)) {
819 return connectionError(
PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
820 }
else if (lastStreamID >= nextID) {
825 return connectionError(
PROTOCOL_ERROR,
"GOAWAY invalid stream/error code");
835 "GOAWAY received, cannot start a request");
848 message =
"Server stopped accepting new streams before this stream was established"_L1;
851 for (
quint32 id = lastStreamID;
id < nextID;
id += 2) {
852 const auto it = activeStreams.find(
id);
853 if (
it != activeStreams.end()) {
857 deleteActiveStream(
id);
859 removeFromSuspended(
id);
863 if (!activeStreams.size())
867void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
869 Q_ASSERT(inboundFrame.
type() == FrameType::WINDOW_UPDATE);
873 const bool valid = delta && delta <=
quint32(std::numeric_limits<qint32>::max());
874 const auto streamID = inboundFrame.
streamID();
879 return connectionError(
PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
880 sessionSendWindowSize =
sum;
882 auto it = activeStreams.find(streamID);
883 if (
it == activeStreams.end()) {
891 "invalid WINDOW_UPDATE delta"_L1);
893 markAsReset(streamID);
894 deleteActiveStream(streamID);
906void QHttp2ProtocolHandler::handleCONTINUATION()
908 Q_ASSERT(inboundFrame.
type() == FrameType::CONTINUATION);
911 if (inboundFrame.
streamID() != continuedFrames.front().streamID())
912 return connectionError(
PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
914 const bool endHeaders = inboundFrame.
flags().testFlag(FrameFlag::END_HEADERS);
915 continuedFrames.push_back(std::move(inboundFrame));
920 continuationExpected =
false;
921 handleContinuedHEADERS();
924void QHttp2ProtocolHandler::handleContinuedHEADERS()
930 const auto firstFrameType = continuedFrames[0].type();
931 Q_ASSERT(firstFrameType == FrameType::HEADERS ||
932 firstFrameType == FrameType::PUSH_PROMISE);
934 const auto streamID = continuedFrames[0].streamID();
936 const auto streamIt = activeStreams.find(streamID);
937 if (firstFrameType == FrameType::HEADERS) {
938 if (streamIt != activeStreams.end()) {
947 "HEADERS on invalid stream"_L1);
948 sendRST_STREAM(streamID,
CANCEL);
949 markAsReset(streamID);
950 deleteActiveStream(streamID);
953 }
else if (!streamWasReset(streamID)) {
954 return connectionError(
PROTOCOL_ERROR,
"HEADERS on invalid stream");
961 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
962 const bool hasHeaderFields = !hpackBlock.empty();
963 if (hasHeaderFields) {
964 HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
967 }
else if (firstFrameType == FrameType::PUSH_PROMISE) {
980 switch (firstFrameType) {
981 case FrameType::HEADERS:
982 if (streamIt != activeStreams.end()) {
987 const bool needResend =
stream.request().d->needResendWithCredentials;
989 if (continuedFrames[0].
flags() & FrameFlag::END_STREAM || needResend) {
991 deleteActiveStream(
stream.streamID);
995 case FrameType::PUSH_PROMISE:
996 if (!tryReserveStream(continuedFrames[0], decoder.
decodedHeader()))
1006 if (identifier == Settings::HEADER_TABLE_SIZE_ID) {
1007 if (newValue > maxAcceptableTableSize) {
1014 if (identifier == Settings::INITIAL_WINDOW_SIZE_ID) {
1017 if (newValue >
quint32(std::numeric_limits<qint32>::max())) {
1022 const qint32 delta =
qint32(newValue) - streamInitialSendWindowSize;
1023 streamInitialSendWindowSize = newValue;
1025 std::vector<quint32> brokenStreams;
1026 brokenStreams.reserve(activeStreams.size());
1027 for (
auto &
stream : activeStreams) {
1030 brokenStreams.push_back(
stream.streamID);
1036 for (
auto id : brokenStreams) {
1039 "SETTINGS window overflow"_L1);
1042 deleteActiveStream(
id);
1048 if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID)
1049 maxConcurrentStreams = newValue;
1051 if (identifier == Settings::MAX_FRAME_SIZE_ID) {
1053 connectionError(
PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
1056 maxFrameSize = newValue;
1059 if (identifier == Settings::MAX_HEADER_LIST_SIZE_ID) {
1063 maxHeaderListSize = newValue;
1072 const auto httpReply =
stream.reply();
1073 auto &httpRequest =
stream.request();
1090 const auto httpReplyPrivate = httpReply->d_func();
1098 for (
const auto &pair : headers) {
1099 const auto &
name = pair.name;
1100 auto value = pair.value;
1106 if (
name ==
":status") {
1107 statusCode =
value.left(3).toInt();
1108 httpReply->setStatusCode(statusCode);
1111 }
else if (
name ==
":version") {
1112 httpReply->setMajorVersion(
value.at(5) -
'0');
1113 httpReply->setMinorVersion(
value.at(7) -
'0');
1114 }
else if (
name ==
"content-length") {
1118 httpReply->setContentLength(
length);
1121 if (
name ==
"set-cookie")
1123 httpReply->appendHeaderField(
name,
value.replace(
'\0', binder));
1127 const auto handleAuth = [&,
this](
QByteArrayView authField,
bool isProxy) ->
bool {
1139 bool resend =
false;
1140 const bool authenticateHandled =
m_connection->d_func()->handleAuthenticateChallenge(
1141 m_socket, httpReply, isProxy, resend);
1142 if (authenticateHandled && resend) {
1143 httpReply->d_func()->eraseData();
1146 httpRequest.d->needResendWithCredentials =
true;
1148 httpReply->d_func()->clearHeaders();
1152 httpReplyPrivate->totallyUploadedData = 0;
1165 if (httpReply->statusCode() == 401) {
1166 const auto wwwAuth = httpReply->headerField(
"www-authenticate");
1167 if (handleAuth(wwwAuth,
false)) {
1169 markAsReset(
stream.streamID);
1173 }
else if (httpReply->statusCode() == 407) {
1174 const auto proxyAuth = httpReply->headerField(
"proxy-authenticate");
1175 if (handleAuth(proxyAuth,
true)) {
1177 markAsReset(
stream.streamID);
1186 m_connection->d_func()->parseRedirectResponse(httpReply);
1189 finishStreamWithError(
stream,
result.errorCode, errorString);
1191 markAsReset(
stream.streamID);
1195 if (
result.redirectUrl.isValid())
1196 httpReply->setRedirectUrl(
result.redirectUrl);
1199 if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
1200 httpReplyPrivate->removeAutoDecompressHeader();
1210 httpReplyPrivate->totallyUploadedData = 0;
1215 emit httpReply->headerChanged();
1224 auto httpReply =
stream.reply();
1236 const char *
data =
reinterpret_cast<const char *
>(
frame.dataBegin());
1237 auto replyPrivate = httpReply->d_func();
1239 replyPrivate->totalProgress +=
length;
1243 if (replyPrivate->shouldEmitSignals()) {
1245 emit httpReply->readyRead();
1246 emit httpReply->dataReadProgress(replyPrivate->totalProgress,
1247 replyPrivate->bodyLength);
1263 auto httpReply =
stream.reply();
1265 httpReply->disconnect(
this);
1267 stream.data()->disconnect(
this);
1269 if (!
stream.request().d->needResendWithCredentials) {
1271 emit httpReply->finished();
1277 qCDebug(QT_HTTP2) <<
"stream" <<
stream.streamID <<
"closed";
1294 if (
auto httpReply =
stream.reply()) {
1295 httpReply->disconnect(
this);
1297 stream.data()->disconnect(
this);
1304 <<
"finished with error:" <<
message;
1309 const qint32 newStreamID = allocateStreamID();
1313 Q_ASSERT(!activeStreams.contains(newStreamID));
1316 const auto replyPrivate =
reply->d_func();
1318 replyPrivate->connectionChannel =
m_channel;
1319 reply->setHttp2WasUsed(
true);
1325 streamInitialSendWindowSize,
1326 streamInitialReceiveWindowSize);
1329 if (
auto src = newStream.data()) {
1333 this, &QHttp2ProtocolHandler::_q_uploadDataDestroyed);
1340 activeStreams.insert(newStreamID, newStream);
1345void QHttp2ProtocolHandler::addToSuspended(
Stream &
stream)
1348 <<
"suspended by flow control";
1349 const auto priority =
stream.priority();
1350 Q_ASSERT(
int(priority) >= 0 &&
int(priority) < 3);
1351 suspendedStreams[priority].push_back(
stream.streamID);
1354void QHttp2ProtocolHandler::markAsReset(
quint32 streamID)
1358 qCDebug(QT_HTTP2) <<
"stream" << streamID <<
"was reset";
1361 if (recycledStreams.size() > maxRecycledStreams) {
1363 recycledStreams.erase(recycledStreams.begin(),
1364 recycledStreams.begin() +
1365 recycledStreams.size() / 2);
1368 const auto it = std::lower_bound(recycledStreams.begin(), recycledStreams.end(),
1370 if (
it != recycledStreams.end() && *
it == streamID)
1373 recycledStreams.insert(
it, streamID);
1376quint32 QHttp2ProtocolHandler::popStreamToResume()
1380 const QNR::Priority ranks[] = {QNR::HighPriority,
1381 QNR::NormalPriority,
1384 for (
const QNR::Priority rank : ranks) {
1385 auto &
queue = suspendedStreams[rank];
1388 auto stream = activeStreams.constFind(*
it);
1389 if (
stream == activeStreams.cend())
1391 if (
stream->sendWindow > 0)
1405void QHttp2ProtocolHandler::removeFromSuspended(
quint32 streamID)
1407 for (
auto &
q : suspendedStreams) {
1408 q.erase(std::remove(
q.begin(),
q.end(), streamID),
q.end());
1412void QHttp2ProtocolHandler::deleteActiveStream(
quint32 streamID)
1414 if (
const auto it = activeStreams.constFind(streamID);
it != activeStreams.cend()) {
1417 stream.reply()->disconnect(
this);
1421 stream.data()->disconnect(
this);
1424 activeStreams.erase(
it);
1427 removeFromSuspended(streamID);
1432bool QHttp2ProtocolHandler::streamWasReset(
quint32 streamID)
const
1434 const auto it = std::lower_bound(recycledStreams.begin(),
1435 recycledStreams.end(),
1437 return it != recycledStreams.end() && *
it == streamID;
1440void QHttp2ProtocolHandler::resumeSuspendedStreams()
1442 while (sessionSendWindowSize > 0) {
1443 const auto streamID = popStreamToResume();
1447 auto it = activeStreams.find(streamID);
1448 if (
it == activeStreams.end())
1454 "failed to send DATA"_L1);
1456 markAsReset(streamID);
1457 deleteActiveStream(streamID);
1462quint32 QHttp2ProtocolHandler::allocateStreamID()
1469 const quint32 streamID = nextID;
1475bool QHttp2ProtocolHandler::tryReserveStream(
const Http2::Frame &pushPromiseFrame,
1478 Q_ASSERT(pushPromiseFrame.
type() == FrameType::PUSH_PROMISE);
1481 for (
const auto &field : requestHeader) {
1482 if (field.name ==
":scheme" || field.name ==
":path"
1483 || field.name ==
":authority" || field.name ==
":method") {
1484 if (field.value.isEmpty() || pseudoHeaders.
contains(field.name))
1486 pseudoHeaders[field.name] = field.
value;
1490 if (pseudoHeaders.
size() != 4) {
1509 const Stream &associatedStream = activeStreams[pushPromiseFrame.
streamID()];
1511 const auto associatedUrl = urlkey_from_request(associatedStream.
request());
1519 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.
dataBegin());
1522 Q_ASSERT(!activeStreams.contains(reservedID));
1523 Q_ASSERT(!streamWasReset(reservedID));
1525 auto &promise = promisedData[urlKey];
1529 activeStreams.insert(reservedID,
Stream(urlKey, reservedID, streamInitialReceiveWindowSize));
1533void QHttp2ProtocolHandler::resetPromisedStream(
const Frame &pushPromiseFrame,
1536 Q_ASSERT(pushPromiseFrame.
type() == FrameType::PUSH_PROMISE);
1537 const auto reservedID = qFromBigEndian<quint32>(pushPromiseFrame.
dataBegin());
1538 sendRST_STREAM(reservedID, reason);
1539 markAsReset(reservedID);
1546 auto promise = promisedData.
take(cacheKey);
1548 message.second->setHttp2WasUsed(
true);
1550 qCDebug(QT_HTTP2) <<
"found cached/promised response on stream" << promise.
reservedID;
1553 Stream *promisedStream =
nullptr;
1554 if (
auto it = activeStreams.find(promise.
reservedID);
it != activeStreams.end()) {
1555 promisedStream = &
it.value();
1562 streamInitialSendWindowSize,
1563 streamInitialReceiveWindowSize);
1565 it = activeStreams.insert(promise.
reservedID, closedStream);
1566 promisedStream = &
it.value();
1575 for (
const auto &
frame : promise.dataFrames)
1582 deleteActiveStream(promisedStream->
streamID);
1595 sendGOAWAY(errorCode);
1599 for (
auto &
stream: activeStreams)
1605void QHttp2ProtocolHandler::closeSession()
1607 activeStreams.clear();
1608 for (
auto &
q: suspendedStreams)
1610 recycledStreams.clear();
1617#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(QAbstractSocket &socket)
void setPayloadSize(quint32 size)
void addFlag(FrameFlag flag)
void append(ValueType val)
void setOutboundFrame(Frame &&newFrame)
bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit, const uchar *src, quint32 size)
bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
void start(FrameType type, FrameFlags flags, quint32 streamID)
bool write(QAbstractSocket &socket) const
QAbstractSocket * m_socket
QHttpNetworkConnectionChannel * m_channel
QHttpNetworkConnection * m_connection
bool startsWith(QByteArrayView other) const noexcept
constexpr bool isEmpty() const noexcept
QByteArrayView trimmed() const noexcept
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()
@ 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.
qsizetype size() const noexcept
iterator erase(const_iterator begin, const_iterator end)
T value(const Key &key, const T &defaultValue=T()) const
bool contains(const Key &key) const
iterator insert(const Key &key, const T &value)
NetworkError
Indicates all possible error conditions found during the processing of the request.
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 &
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
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
std::vector< HeaderField > HttpHeader
HeaderSize header_size(const HttpHeader &header)
HeaderSize entry_size(QByteArrayView name, QByteArrayView 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)
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
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
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QPair< QHttpNetworkRequest, QHttpNetworkReply * > HttpMessagePair
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
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
GLuint GLuint GLfloat weight
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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
QNonContiguousByteDevice * data() const
const QHttpNetworkRequest & request() const