7#include <private/bitstreams_p.h>
9#include <QtCore/private/qnumeric_p.h>
10#include <QtCore/private/qiodevice_p.h>
11#include <QtCore/private/qnoncontiguousbytedevice_p.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/QRandomGenerator>
14#include <QtCore/qloggingcategory.h>
24using namespace Qt::StringLiterals;
28
29
30
31
32
33
34
35
36
39
40
41
42
43
44
45
46
47
48
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
67QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID,
68 Configuration configuration)
noexcept
69 : QObject(connection), m_streamID(streamID), m_configuration(configuration)
73 qCDebug(qHttp2ConnectionLog,
"[%p] new stream %u", connection, streamID);
76QHttp2Stream::~QHttp2Stream()
noexcept {
77 if (
auto *connection = getConnection()) {
78 if (m_state != State::Idle && m_state != State::Closed) {
79 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, destroyed while still open", connection,
82 if (connection->getSocket()) {
83 if (isUploadingDATA())
84 sendRST_STREAM(CANCEL);
86 sendRST_STREAM(HTTP2_NO_ERROR);
90 connection->m_streams.remove(streamID());
95
96
97
98
101
102
103
104
105
106
107
108
109
110
111
112
113
116
117
118
119
120
121
122
125
126
127
128
129
130
131
132
135
136
137
138
139
140
141
142
143
144
145
149
150
151
152
153
154
155
158
159
160
161
162
163
164
165
168
169
170
171
172
173
174
175
178
179
180
181
182
185
186
187
188
189
190
193
194
195
196
197
198
199
200
203
204
205
206
209
210
211
212
213
214
216
217
218
219
221
222
223
224
226
227
228
229
231
232
233
234
235
237
238
239
240
242
243
244
245
248
249
250
251
253void QHttp2Stream::finishWithError(Http2::Http2Error errorCode,
const QString &message)
255 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
256 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
257 transitionState(StateTransition::RST);
258 emit errorOccurred(errorCode, message);
261void QHttp2Stream::finishWithError(Http2::Http2Error errorCode)
263 QNetworkReply::NetworkError ignored = QNetworkReply::NoError;
265 qt_error(errorCode, ignored, message);
266 finishWithError(errorCode, message);
270void QHttp2Stream::streamError(Http2::Http2Error errorCode,
271 QLatin1StringView message)
273 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
274 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
276 sendRST_STREAM(errorCode);
277 emit errorOccurred(errorCode, message);
281
282
283
284
285
286
287bool QHttp2Stream::sendRST_STREAM(Http2::Http2Error errorCode)
289 if (m_state == State::Closed || m_state == State::Idle) {
290 qCDebug(qHttp2ConnectionLog,
"[%p] could not send RST_STREAM on %s stream %u",
291 getConnection(), QDebug::toBytes(m_state).constData(), m_streamID);
295 if (m_RST_STREAM_received.has_value())
298 getConnection()->registerStreamAsResetLocally(streamID());
300 m_RST_STREAM_sent = errorCode;
301 qCDebug(qHttp2ConnectionLog,
"[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
302 m_streamID, errorCode);
303 transitionState(StateTransition::RST);
305 QHttp2Connection *connection = getConnection();
306 FrameWriter &frameWriter = connection->frameWriter;
307 frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
308 frameWriter.append(quint32(errorCode));
309 return frameWriter.write(*connection->getSocket());
313
314
315
316
317
318
319
320
321
322
323
324bool QHttp2Stream::sendDATA(
const QByteArray &payload,
bool endStream)
326 Q_ASSERT(!m_uploadByteDevice);
327 if (m_state != State::Open && m_state != State::HalfClosedRemote)
330 auto *byteDevice = QNonContiguousByteDeviceFactory::create(payload);
331 m_owningByteDevice =
true;
332 byteDevice->setParent(
this);
333 return sendDATA(byteDevice, endStream);
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351bool QHttp2Stream::sendDATA(QIODevice *device,
bool endStream)
353 Q_ASSERT(!m_uploadDevice);
354 Q_ASSERT(!m_uploadByteDevice);
356 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
357 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
359 getConnection(), m_streamID, device);
363 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
364 getConnection(), m_streamID, device);
365 auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
366 m_owningByteDevice =
true;
367 byteDevice->setParent(
this);
368 m_uploadDevice = device;
369 return sendDATA(byteDevice, endStream);
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387bool QHttp2Stream::sendDATA(QNonContiguousByteDevice *device,
bool endStream)
389 Q_ASSERT(!m_uploadByteDevice);
391 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
392 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
394 getConnection(), m_streamID, device);
398 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
399 getConnection(), m_streamID, device);
400 m_uploadByteDevice = device;
401 m_endStreamAfterDATA = endStream;
402 connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead,
this,
403 &QHttp2Stream::maybeResumeUpload);
404 connect(m_uploadByteDevice, &QObject::destroyed,
this, &QHttp2Stream::uploadDeviceDestroyed);
412void QHttp2Stream::internalSendDATA()
414 Q_ASSERT(m_uploadByteDevice);
415 QHttp2Connection *connection = getConnection();
416 Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
417 QIODevice *socket = connection->getSocket();
419 qCDebug(qHttp2ConnectionLog,
420 "[%p] stream %u, about to write to socket, current session window size: %d, stream "
421 "window size: %d, bytes available: %lld",
422 connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
423 m_uploadByteDevice->size() - m_uploadByteDevice->pos());
425 qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
426 FrameWriter &frameWriter = connection->frameWriter;
427 qint64 totalBytesWritten = 0;
428 const auto deviceCanRead = [
this, connection] {
433 const qint64 requestSize = connection->maxFrameSize * 10ll;
435 return m_uploadByteDevice->readPointer(requestSize, tmp) !=
nullptr && tmp > 0;
438 bool sentEND_STREAM =
false;
439 while (remainingWindowSize && deviceCanRead()) {
440 quint32 bytesWritten = 0;
441 qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
442 frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
444 while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
445 const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
447 qint64 outBytesAvail = 0;
448 const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
449 if (!readPointer || outBytesAvail <= 0) {
450 qCDebug(qHttp2ConnectionLog,
451 "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
452 connection, m_streamID, m_uploadByteDevice, outBytesAvail);
455 const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
456 frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
457 m_uploadByteDevice->advanceReadPointer(bytesToWrite);
459 bytesWritten += bytesToWrite;
461 m_sendWindow -= bytesToWrite;
462 Q_ASSERT(m_sendWindow >= 0);
463 connection->sessionSendWindowSize -= bytesToWrite;
464 Q_ASSERT(connection->sessionSendWindowSize >= 0);
465 remainingBytesInFrame -= bytesToWrite;
466 Q_ASSERT(remainingBytesInFrame >= 0);
467 remainingWindowSize -= bytesToWrite;
468 Q_ASSERT(remainingWindowSize >= 0);
471 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, writing %u bytes to socket", connection,
472 m_streamID, bytesWritten);
473 if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
474 sentEND_STREAM =
true;
475 frameWriter.addFlag(FrameFlag::END_STREAM);
477 if (!frameWriter.write(*socket)) {
478 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, failed to write to socket", connection,
480 return finishWithError(INTERNAL_ERROR,
"failed to write to socket"_L1);
483 totalBytesWritten += bytesWritten;
486 qCDebug(qHttp2ConnectionLog,
487 "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
488 "more later. Remaining window size: %d",
489 connection, m_streamID, totalBytesWritten, remainingWindowSize);
491 emit bytesWritten(totalBytesWritten);
492 if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
493 qCDebug(qHttp2ConnectionLog,
494 "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
496 connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
497 !sentEND_STREAM && m_endStreamAfterDATA ?
"" :
"not ");
498 if (!sentEND_STREAM && m_endStreamAfterDATA) {
503 frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
504 frameWriter.write(*socket);
507 }
else if (isUploadBlocked()) {
508 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, upload blocked", connection, m_streamID);
509 emit uploadBlocked();
513void QHttp2Stream::finishSendDATA()
515 if (m_endStreamAfterDATA)
516 transitionState(StateTransition::CloseLocal);
518 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
519 m_uploadDevice =
nullptr;
520 if (m_owningByteDevice) {
521 m_owningByteDevice =
false;
522 delete m_uploadByteDevice;
524 m_uploadByteDevice =
nullptr;
525 emit uploadFinished();
528void QHttp2Stream::maybeResumeUpload()
530 qCDebug(qHttp2ConnectionLog,
531 "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
533 getConnection(), m_streamID, m_uploadByteDevice,
534 !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
536 if (isUploadingDATA() && !isUploadBlocked())
539 getConnection()->m_blockedStreams.insert(streamID());
543
544
545
546bool QHttp2Stream::isUploadBlocked()
const noexcept
548 constexpr auto MinFrameSize = Http2::frameHeaderSize + 1;
549 return isUploadingDATA()
550 && (m_sendWindow <= MinFrameSize
551 || getConnection()->sessionSendWindowSize <= MinFrameSize);
554void QHttp2Stream::uploadDeviceReadChannelFinished()
560
561
562
563
564
565
566bool QHttp2Stream::sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream, quint8 priority)
568 using namespace HPack;
569 if (
auto hs = header_size(headers);
570 !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
574 transitionState(StateTransition::Open);
576 Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
578 QHttp2Connection *connection = getConnection();
580 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending HEADERS frame with %u entries",
581 connection, streamID(), uint(headers.size()));
583 QIODevice *socket = connection->getSocket();
584 FrameWriter &frameWriter = connection->frameWriter;
586 frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
588 frameWriter.addFlag(FrameFlag::END_STREAM);
590 frameWriter.append(quint32());
591 frameWriter.append(priority);
594 BitOStream outputStream(frameWriter.outboundFrame().buffer);
597 for (
auto &maybePendingTableSizeUpdate : connection->pendingTableSizeUpdates) {
598 if (!maybePendingTableSizeUpdate)
600 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending dynamic table size update of size %u",
601 connection, streamID(), *maybePendingTableSizeUpdate);
602 connection->encoder.setMaxDynamicTableSize(*maybePendingTableSizeUpdate);
603 connection->encoder.encodeSizeUpdate(outputStream, *maybePendingTableSizeUpdate);
604 maybePendingTableSizeUpdate.reset();
607 if (connection->m_connectionType == QHttp2Connection::Type::Client) {
608 if (!connection->encoder.encodeRequest(outputStream, headers))
611 if (!connection->encoder.encodeResponse(outputStream, headers))
615 bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
617 transitionState(StateTransition::CloseLocal);
623
624
625
626
627void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
629 QHttp2Connection *connection = getConnection();
630 m_recvWindow += qint32(delta);
631 connection->sendWINDOW_UPDATE(streamID(), delta);
634void QHttp2Stream::uploadDeviceDestroyed()
636 if (isUploadingDATA()) {
639 streamError(CANCEL, QLatin1String(
"Upload device destroyed while uploading"));
640 emit uploadDeviceError(
"Upload device destroyed while uploading"_L1);
642 m_uploadDevice =
nullptr;
645void QHttp2Stream::setState(State newState)
647 if (m_state == newState)
649 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, state changed from %d to %d", getConnection(),
650 streamID(),
int(m_state),
int(newState));
652 emit stateChanged(newState);
653 if (m_state == State::Closed)
654 getConnection()->maybeCloseOnGoingAway();
660void QHttp2Stream::transitionState(StateTransition transition)
664 if (transition == StateTransition::Open)
665 setState(State::Open);
670 switch (transition) {
671 case StateTransition::CloseLocal:
672 setState(State::HalfClosedLocal);
674 case StateTransition::CloseRemote:
675 setState(State::HalfClosedRemote);
677 case StateTransition::RST:
678 setState(State::Closed);
680 case StateTransition::Open:
684 case State::HalfClosedLocal:
685 if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
686 setState(State::Closed);
688 case State::HalfClosedRemote:
689 if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
690 setState(State::Closed);
692 case State::ReservedRemote:
693 if (transition == StateTransition::RST) {
694 setState(State::Closed);
695 }
else if (transition == StateTransition::CloseLocal) {
696 setState(State::HalfClosedLocal);
704void QHttp2Stream::handleDATA(
const Frame &inboundFrame)
706 QHttp2Connection *connection = getConnection();
708 qCDebug(qHttp2ConnectionLog,
709 "[%p] stream %u, received DATA frame with payload of %u bytes, closing stream? %s",
710 connection, m_streamID, inboundFrame.payloadSize(),
711 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
717 Q_ASSERT(state() != State::HalfClosedRemote && state() != State::Closed);
719 if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
720 qCDebug(qHttp2ConnectionLog,
721 "[%p] stream %u, received DATA frame with payload size %u, "
722 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
723 connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
724 return streamError(FLOW_CONTROL_ERROR, QLatin1String(
"data bigger than window size"));
730 Q_ASSERT(inboundFrame.buffer.size() >= frameHeaderSize);
731 Q_ASSERT(inboundFrame.payloadSize() + frameHeaderSize == inboundFrame.buffer.size());
733 m_recvWindow -= qint32(inboundFrame.payloadSize());
734 const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
735 const bool ignoreData = connection->streamIsIgnored(m_streamID);
737 if ((inboundFrame.dataSize() > 0 || endStream) && !ignoreData) {
738 QByteArray fragment(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()),
739 inboundFrame.dataSize());
741 transitionState(StateTransition::CloseRemote);
742 const auto shouldBuffer = m_configuration.useDownloadBuffer && !fragment.isEmpty();
745 m_downloadBuffer.append(std::move(fragment));
746 emit dataReceived(m_downloadBuffer.last(), endStream);
748 emit dataReceived(fragment, endStream);
752 if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
754 sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
758void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers)
760 if (m_state == State::Idle)
761 transitionState(StateTransition::Open);
762 const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
764 transitionState(StateTransition::CloseRemote);
765 if (!headers.empty()) {
766 m_headers.insert(m_headers.end(), headers.begin(), headers.end());
767 emit headersUpdated();
769 emit headersReceived(headers, endStream);
772void QHttp2Stream::handleRST_STREAM(
const Frame &inboundFrame)
774 if (m_state == State::Closed)
777 transitionState(StateTransition::RST);
778 m_RST_STREAM_received = qFromBigEndian<quint32>(inboundFrame.dataBegin());
779 if (isUploadingDATA()) {
780 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
781 m_uploadDevice =
nullptr;
782 m_uploadByteDevice =
nullptr;
784 finishWithError(Http2Error(*m_RST_STREAM_received));
787void QHttp2Stream::handleWINDOW_UPDATE(
const Frame &inboundFrame)
789 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
790 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
792 if (!valid || qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
793 qCDebug(qHttp2ConnectionLog,
794 "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
796 getConnection(), m_streamID, delta);
797 return streamError(PROTOCOL_ERROR,
"invalid WINDOW_UPDATE delta"_L1);
801 if (isUploadingDATA())
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
824
825
826
827
828
831
832
833
834
837
838
839
840
843
844
845
846
849
850
851
852
855
856
857
858
859
860
863
864
865
866
867
868
869
870
873
874
875
876
877
878
879QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
880 const QHttp2Configuration &config)
884 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
885 connection->setH2Configuration(config);
886 connection->m_connectionType = QHttp2Connection::Type::Client;
887 connection->m_upgradedConnection =
true;
890 QHttp2Stream *stream = connection->createLocalStreamInternal().unwrap();
891 Q_ASSERT(stream->streamID() == 1);
892 stream->setState(QHttp2Stream::State::HalfClosedLocal);
894 if (!connection->m_prefaceSent)
897 return connection.release();
901
902
903
904
905
906QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
907 const QHttp2Configuration &config)
909 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
910 connection->setH2Configuration(config);
911 connection->m_connectionType = QHttp2Connection::Type::Client;
913 return connection.release();
917
918
919
920
921QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
922 const QHttp2Configuration &config)
924 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
925 connection->setH2Configuration(config);
926 connection->m_connectionType = QHttp2Connection::Type::Server;
928 connection->m_nextStreamID = 2;
930 connection->m_waitingForClientPreface =
true;
932 return connection.release();
936
937
938
939
940
941
942
943
944
945
946
947
950
951
952
953
954QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
955QHttp2Connection::createStream(QHttp2Stream::Configuration configuration)
957 Q_ASSERT(m_connectionType == Type::Client);
958 if (m_nextStreamID > lastValidStreamID)
959 return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
960 return createLocalStreamInternal(configuration);
963QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
964QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf)
967 return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
968 const quint32 streamID = m_nextStreamID;
969 if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams()))
970 return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
972 if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) {
977 return { QHttp2Connection::CreateStreamError::UnknownError };
980QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID,
981 QHttp2Stream::Configuration conf)
983 Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID);
985 if (m_connectionType == Type::Client && !m_prefaceSent && !sendClientPreface()) {
986 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send client preface",
this);
990 auto result = m_streams.tryEmplace(streamID,
nullptr);
991 if (!result.inserted)
993 QPointer<QHttp2Stream> &stream = result.iterator.value();
994 stream =
new QHttp2Stream(
this, streamID, conf);
995 stream->m_recvWindow = streamInitialReceiveWindowSize;
996 stream->m_sendWindow = streamInitialSendWindowSize;
998 connect(stream, &QHttp2Stream::uploadBlocked,
this, [
this, stream] {
999 m_blockedStreams.insert(stream->streamID());
1001 *result.iterator = stream;
1002 return *result.iterator;
1005qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask)
const noexcept
1007 const auto shouldCount = [mask](
const QPointer<QHttp2Stream> &stream) ->
bool {
1008 return stream && (stream->streamID() & 1) == mask && stream->isActive();
1010 return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
1014
1015
1016
1017qsizetype QHttp2Connection::numActiveRemoteStreams()
const noexcept
1019 const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
1020 return numActiveStreamsImpl(RemoteMask);
1024
1025
1026
1027qsizetype QHttp2Connection::numActiveLocalStreams()
const noexcept
1029 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
1030 return numActiveStreamsImpl(LocalMask);
1034
1035
1036
1037QHttp2Stream *QHttp2Connection::getStream(quint32 streamID)
const
1039 return m_streams.value(streamID,
nullptr).get();
1043
1044
1045
1046
1047void QHttp2Connection::close(Http2::Http2Error errorCode)
1049 if (m_connectionAborted)
1052 if (errorCode == Http2::HTTP2_NO_ERROR) {
1053 if (m_connectionType == Type::Server)
1054 sendInitialServerGracefulShutdownGoaway();
1056 sendClientGracefulShutdownGoaway();
1060 connectionError(errorCode,
"Connection closed with error",
false);
1065
1066
1067
1068
1069
1072
1073
1074
1075
1076
1079
1080
1081
1082
1083
1084
1087
1088
1089
1090
1093
1094
1095
1096
1097
1099QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
1102 Q_ASSERT(socket->isOpen());
1103 Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
1110QHttp2Connection::~QHttp2Connection()
1114 for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
1115 delete stream.get();
1118bool QHttp2Connection::serverCheckClientPreface()
1120 if (!m_waitingForClientPreface)
1122 auto *socket = getSocket();
1123 if (socket->bytesAvailable() < Http2::clientPrefaceLength)
1125 if (!readClientPreface()) {
1127 emit errorOccurred(Http2Error::PROTOCOL_ERROR,
"invalid client preface"_L1);
1128 qCDebug(qHttp2ConnectionLog,
"[%p] Invalid client preface",
this);
1131 qCDebug(qHttp2ConnectionLog,
"[%p] Peer sent valid client preface",
this);
1132 m_waitingForClientPreface =
false;
1133 if (!sendServerPreface()) {
1134 connectionError(INTERNAL_ERROR,
"Failed to send server preface");
1140bool QHttp2Connection::sendPing()
1142 std::array<
char, 8> data;
1144 QRandomGenerator gen;
1145 gen.generate(data.begin(), data.end());
1146 return sendPing(data);
1149bool QHttp2Connection::sendPing(QByteArrayView data)
1151 frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
1153 Q_ASSERT(data.length() == 8);
1154 if (!m_lastPingSignature) {
1155 m_lastPingSignature = data.toByteArray();
1157 qCWarning(qHttp2ConnectionLog,
"[%p] No PING is sent while waiting for the previous PING.",
this);
1161 frameWriter.append((uchar*)data.data(), (uchar*)data.end());
1162 frameWriter.write(*getSocket());
1167
1168
1169
1170
1171void QHttp2Connection::handleReadyRead()
1174 if (m_connectionType == Type::Server && !serverCheckClientPreface())
1177 QIODevice *socket = getSocket();
1179 qCDebug(qHttp2ConnectionLog,
"[%p] Receiving data, %lld bytes available",
this,
1180 socket->bytesAvailable());
1182 using namespace Http2;
1186 while (!m_connectionAborted) {
1187 const auto result = frameReader.read(*socket);
1188 if (result != FrameStatus::goodFrame)
1189 qCDebug(qHttp2ConnectionLog,
"[%p] Tried to read frame, got %d",
this,
int(result));
1191 case FrameStatus::incompleteFrame:
1193 case FrameStatus::protocolError:
1194 return connectionError(PROTOCOL_ERROR,
"invalid frame");
1195 case FrameStatus::sizeError: {
1196 const auto streamID = frameReader.inboundFrame().streamID();
1197 const auto frameType = frameReader.inboundFrame().type();
1198 auto stream = getStream(streamID);
1204 if (frameType == FrameType::HEADERS ||
1205 frameType == FrameType::SETTINGS ||
1206 frameType == FrameType::PUSH_PROMISE ||
1207 frameType == FrameType::CONTINUATION ||
1209 frameType == FrameType::RST_STREAM ||
1210 streamID == connectionStreamID)
1211 return connectionError(FRAME_SIZE_ERROR,
"invalid frame size");
1214 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1215 QLatin1String(
"invalid frame size"));
1223 Q_ASSERT(result == FrameStatus::goodFrame);
1225 inboundFrame = std::move(frameReader.inboundFrame());
1227 const auto frameType = inboundFrame.type();
1228 qCDebug(qHttp2ConnectionLog,
"[%p] Successfully read a frame, with type: %d",
this,
1235 if (continuationExpected && frameType != FrameType::CONTINUATION)
1236 return connectionError(PROTOCOL_ERROR,
"CONTINUATION expected");
1238 switch (frameType) {
1239 case FrameType::DATA:
1242 case FrameType::HEADERS:
1245 case FrameType::PRIORITY:
1248 case FrameType::RST_STREAM:
1251 case FrameType::SETTINGS:
1254 case FrameType::PUSH_PROMISE:
1255 handlePUSH_PROMISE();
1257 case FrameType::PING:
1260 case FrameType::GOAWAY:
1263 case FrameType::WINDOW_UPDATE:
1264 handleWINDOW_UPDATE();
1266 case FrameType::CONTINUATION:
1267 handleCONTINUATION();
1269 case FrameType::LAST_FRAME_TYPE:
1276bool QHttp2Connection::readClientPreface()
1278 auto *socket = getSocket();
1279 Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
1280 char buffer[Http2::clientPrefaceLength];
1281 const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
1282 if (read != Http2::clientPrefaceLength)
1284 return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
1288
1289
1290
1291void QHttp2Connection::handleConnectionClosure()
1293 const auto errorString = QCoreApplication::translate(
"QHttp",
"Connection closed");
1294 for (
auto it = m_streams.cbegin(), end = m_streams.cend(); it != end; ++it) {
1295 const QPointer<QHttp2Stream> &stream = it.value();
1296 if (stream && stream->isActive())
1297 stream->finishWithError(PROTOCOL_ERROR, errorString);
1301void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
1303 m_config = std::move(config);
1306 maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
1307 pushPromiseEnabled = m_config.serverPushEnabled();
1308 streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
1309 m_maxConcurrentStreams = m_config.maxConcurrentStreams();
1310 encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
1313void QHttp2Connection::connectionError(Http2Error errorCode,
const char *message,
bool logAsError)
1316 if (m_connectionAborted)
1318 m_connectionAborted =
true;
1321 qCCritical(qHttp2ConnectionLog,
"[%p] Connection error: %s (%d)",
this, message,
1324 qCDebug(qHttp2ConnectionLog,
"[%p] Closing connection: %s (%d)",
this, message,
1332 m_lastStreamToProcess = std::min(m_lastIncomingStreamID, m_lastStreamToProcess);
1333 sendGOAWAYFrame(errorCode, m_lastStreamToProcess);
1334 auto messageView = QLatin1StringView(message);
1336 for (QHttp2Stream *stream : std::as_const(m_streams)) {
1337 if (stream && stream->isActive())
1338 stream->finishWithError(errorCode, messageView);
1345void QHttp2Connection::closeSession()
1347 emit connectionClosed();
1350bool QHttp2Connection::streamWasResetLocally(quint32 streamID)
noexcept
1352 return m_resetStreamIDs.contains(streamID);
1355void QHttp2Connection::registerStreamAsResetLocally(quint32 streamID)
1363 m_resetStreamIDs.append(streamID);
1364 while (m_resetStreamIDs.size() > 100)
1365 m_resetStreamIDs.takeFirst();
1368bool QHttp2Connection::isInvalidStream(quint32 streamID)
noexcept
1370 auto stream = m_streams.value(streamID,
nullptr);
1371 return (!stream || stream->wasResetbyPeer()) && !streamWasResetLocally(streamID);
1375
1376
1377
1378
1379
1380
1381
1382bool QHttp2Connection::streamIsIgnored(quint32 streamID)
const noexcept
1384 const bool streamIsRemote = (streamID & 1) == (m_connectionType == Type::Client ? 0 : 1);
1385 return Q_UNLIKELY(streamIsRemote && m_lastStreamToProcess < streamID);
1388bool QHttp2Connection::sendClientPreface()
1390 QIODevice *socket = getSocket();
1392 const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
1393 if (written != clientPrefaceLength)
1396 if (!sendSETTINGS()) {
1397 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1400 m_prefaceSent =
true;
1401 if (socket->bytesAvailable())
1402 QMetaObject::invokeMethod(
this, &QHttp2Connection::handleReadyRead, Qt::QueuedConnection);
1406bool QHttp2Connection::sendServerPreface()
1410 if (!sendSETTINGS()) {
1411 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1414 m_prefaceSent =
true;
1418bool QHttp2Connection::sendSETTINGS()
1420 QIODevice *socket = getSocket();
1422 frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
1423 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS frame, %d bytes",
this,
1424 frameWriter.outboundFrame().payloadSize());
1425 Q_ASSERT(frameWriter.outboundFrame().payloadSize());
1427 if (!frameWriter.write(*socket))
1430 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1433 const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
1434 if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
1437 waitingForSettingsACK =
true;
1441bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
1443 qCDebug(qHttp2ConnectionLog,
"[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u",
this,
1445 frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
1446 frameWriter.append(delta);
1447 return frameWriter.write(*getSocket());
1450void QHttp2Connection::sendClientGracefulShutdownGoaway()
1453 Q_ASSERT(m_connectionType == Type::Client);
1455 if (m_connectionAborted || m_goingAway) {
1456 qCWarning(qHttp2ConnectionLog,
"[%p] Client graceful shutdown already in progress",
this);
1461 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1462 m_lastStreamToProcess = m_lastIncomingStreamID;
1463 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1465 maybeCloseOnGoingAway();
1468void QHttp2Connection::sendInitialServerGracefulShutdownGoaway()
1470 Q_ASSERT(m_connectionType == Type::Server);
1474 if (m_connectionAborted || m_goingAway) {
1475 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown already in progress",
this);
1480 m_goawayGraceTimer.setRemainingTime(GoawayGracePeriod);
1481 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, Http2::lastValidStreamID);
1487 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
1489 m_gracefulShutdownState = GracefulShutdownState::AwaitingPriorPing;
1492void QHttp2Connection::sendFinalServerGracefulShutdownGoaway()
1494 if (m_connectionAborted || !m_goingAway) {
1495 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown not in progress",
this);
1498 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1499 m_lastStreamToProcess = m_lastIncomingStreamID;
1500 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1501 maybeCloseOnGoingAway();
1504bool QHttp2Connection::sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastStreamID)
1506 QIODevice *socket = getSocket();
1507 if (!socket || !socket->isOpen())
1510 qCDebug(qHttp2ConnectionLog,
"[%p] Sending GOAWAY frame, error code %u, last stream %u",
this,
1511 errorCode, lastStreamID);
1513 frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
1514 Http2PredefinedParameters::connectionStreamID);
1515 frameWriter.append(lastStreamID);
1516 frameWriter.append(quint32(errorCode));
1517 return frameWriter.write(*socket);
1520void QHttp2Connection::maybeCloseOnGoingAway()
1525 if (m_connectionAborted || !m_goingAway) {
1526 qCDebug(qHttp2ConnectionLog,
"[%p] Connection close deferred, graceful shutdown not active",
1532 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing)
1535 const auto streamIsActive = [](
const QPointer<QHttp2Stream> &stream) {
1536 return stream && stream->isActive();
1539 if (std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
1540 qCDebug(qHttp2ConnectionLog,
"[%p] All streams closed, closing connection",
this);
1545bool QHttp2Connection::sendSETTINGS_ACK()
1547 frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
1548 return frameWriter.write(*getSocket());
1551void QHttp2Connection::handleDATA()
1553 Q_ASSERT(inboundFrame.type() == FrameType::DATA);
1555 const auto streamID = inboundFrame.streamID();
1559 if (streamID == connectionStreamID)
1560 return connectionError(PROTOCOL_ERROR,
"DATA on the connection stream");
1562 if (isInvalidStream(streamID))
1563 return connectionError(ENHANCE_YOUR_CALM,
"DATA on invalid stream");
1565 QHttp2Stream *stream =
nullptr;
1566 if (!streamWasResetLocally(streamID)) {
1567 stream = getStream(streamID);
1570 if (stream->state() == QHttp2Stream::State::HalfClosedRemote
1571 || stream->state() == QHttp2Stream::State::Closed) {
1572 return stream->streamError(Http2Error::STREAM_CLOSED,
1573 QLatin1String(
"Data on closed stream"));
1577 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1578 qCDebug(qHttp2ConnectionLog,
1579 "[%p] Received DATA frame with payload size %u, "
1580 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1581 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1583 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1584 QLatin1String(
"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"));
1585 return connectionError(FRAME_SIZE_ERROR,
"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE");
1588 if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1589 qCDebug(qHttp2ConnectionLog,
1590 "[%p] Received DATA frame with payload size %u, "
1591 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1592 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1593 return connectionError(FLOW_CONTROL_ERROR,
"Flow control error");
1596 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1599 stream->handleDATA(inboundFrame);
1602 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
1603 const bool ignoreData = stream && streamIsIgnored(stream->streamID());
1605 emit receivedEND_STREAM(streamID);
1611 stream->setState(QHttp2Stream::State::Closed);
1616 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1618 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1619 quint32(connectionStreamID),
1620 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1621 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1625void QHttp2Connection::handleHEADERS()
1627 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1629 const auto streamID = inboundFrame.streamID();
1630 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1631 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1635 if (streamID == connectionStreamID)
1636 return connectionError(PROTOCOL_ERROR,
"HEADERS on 0x0 stream");
1638 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1639 qCDebug(qHttp2ConnectionLog,
1640 "[%p] Received HEADERS frame with payload size %u, "
1641 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1642 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1643 return connectionError(Http2Error::FRAME_SIZE_ERROR,
1644 "HEADERS payload size exceeds SETTINGS_MAX_FRAME_SIZE");
1647 const bool isClient = m_connectionType == Type::Client;
1648 const bool isClientInitiatedStream = !!(streamID & 1);
1649 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1651 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1652 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1653 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1654 Q_ASSERT(newStream);
1655 m_lastIncomingStreamID = streamID;
1657 if (!streamCountIsOk) {
1658 newStream->setState(QHttp2Stream::State::Open);
1659 newStream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1661 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1665 qCDebug(qHttp2ConnectionLog,
"[%p] New incoming stream %d",
this, streamID);
1666 if (!streamIsIgnored(newStream->streamID())) {
1667 emit newIncomingStream(newStream);
1668 }
else if (m_goawayGraceTimer.hasExpired()) {
1671 connectionError(Http2Error::PROTOCOL_ERROR,
"Peer refused to GOAWAY.");
1674 }
else if (streamWasResetLocally(streamID)) {
1675 qCDebug(qHttp2ConnectionLog,
1676 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1679 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1682 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1684 return connectionError(PROTOCOL_ERROR,
"HEADERS on invalid stream");
1685 }
else if (isInvalidStream(streamID)) {
1688 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1689 return connectionError(ENHANCE_YOUR_CALM,
"HEADERS on invalid stream");
1692 const auto flags = inboundFrame.flags();
1693 if (flags.testFlag(FrameFlag::PRIORITY)) {
1694 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1699 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1700 continuedFrames.clear();
1701 continuedFrames.push_back(std::move(inboundFrame));
1703 continuationExpected =
true;
1707 handleContinuedHEADERS();
1710void QHttp2Connection::handlePRIORITY()
1712 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1713 || inboundFrame.type() == FrameType::HEADERS);
1715 const auto streamID = inboundFrame.streamID();
1716 if (streamIsIgnored(streamID))
1721 if (streamID == connectionStreamID)
1722 return connectionError(PROTOCOL_ERROR,
"PRIORITY on 0x0 stream");
1726 if (isInvalidStream(streamID))
1727 return connectionError(ENHANCE_YOUR_CALM,
"PRIORITY on invalid stream");
1732 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1734 quint32 streamDependency = 0;
1736 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1740 const bool exclusive = streamDependency & 0x80000000;
1741 streamDependency &= ~0x80000000;
1745 Q_UNUSED(exclusive);
1749void QHttp2Connection::handleRST_STREAM()
1751 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1753 const auto streamID = inboundFrame.streamID();
1754 if (streamIsIgnored(streamID))
1761 if (streamID == connectionStreamID)
1762 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on 0x0");
1767 Q_ASSERT(inboundFrame.payloadSize() == 4);
1769 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1770 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1771 emit stream->rstFrameReceived(error);
1774 const quint32 lastRelevantStreamID = [
this, streamID]() {
1775 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1776 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1778 if (streamID > lastRelevantStreamID) {
1782 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on idle stream");
1785 Q_ASSERT(inboundFrame.dataSize() == 4);
1787 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1788 stream->handleRST_STREAM(inboundFrame);
1791void QHttp2Connection::handleSETTINGS()
1794 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1798 if (inboundFrame.streamID() != connectionStreamID)
1799 return connectionError(PROTOCOL_ERROR,
"SETTINGS on invalid stream");
1801 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1804 if (inboundFrame.payloadSize())
1805 return connectionError(FRAME_SIZE_ERROR,
"SETTINGS ACK with data");
1806 if (!waitingForSettingsACK)
1807 return connectionError(PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
1808 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1809 waitingForSettingsACK =
false;
1812 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1814 if (inboundFrame.dataSize()) {
1818 Q_ASSERT(inboundFrame.payloadSize() % 6 == 0);
1820 auto src = inboundFrame.dataBegin();
1821 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1822 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1823 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1824 if (!acceptSetting(identifier, intVal)) {
1826 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1827 quint32(identifier), intVal);
1833 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1835 emit settingsFrameReceived();
1838void QHttp2Connection::handlePUSH_PROMISE()
1841 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1846 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1849 return connectionError(PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
1854 const auto streamID = inboundFrame.streamID();
1855 if (streamID == connectionStreamID)
1856 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid associated stream (0x0)");
1858 auto it = m_streams.constFind(streamID);
1860 if (it != m_streams.constEnd()) {
1861 QHttp2Stream *associatedStream = it->get();
1862 if (associatedStream->state() != QHttp2Stream::State::Open
1863 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1865 it = m_streams.constEnd();
1875 if (it == m_streams.constEnd())
1876 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1877 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1878 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1879 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1881 if ((*it)->state() != QHttp2Stream::State::Open &&
1882 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1883 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1888 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1889 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1890 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid promised stream ID");
1892 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1895 auto *stream = createStreamInternal_impl(reservedID);
1897 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with already active stream ID");
1898 m_lastIncomingStreamID = reservedID;
1899 stream->setState(QHttp2Stream::State::ReservedRemote);
1901 if (!streamCountIsOk) {
1902 stream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1903 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1909 if (!pushPromiseEnabled) {
1910 return stream->streamError(REFUSE_STREAM,
1911 QLatin1String(
"PUSH_PROMISE not enabled but ignored"));
1918 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1919 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1920 continuedFrames.clear();
1921 continuedFrames.push_back(std::move(inboundFrame));
1924 continuationExpected =
true;
1928 handleContinuedHEADERS();
1931void QHttp2Connection::handlePING()
1933 Q_ASSERT(inboundFrame.type() == FrameType::PING);
1938 if (inboundFrame.streamID() != connectionStreamID)
1939 return connectionError(PROTOCOL_ERROR,
"PING on invalid stream");
1944 Q_ASSERT(inboundFrame.payloadSize() == 8);
1946 if (inboundFrame.flags() & FrameFlag::ACK) {
1947 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
1948 if (!m_lastPingSignature.has_value()) {
1949 emit pingFrameReceived(PingState::PongNoPingSent);
1950 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
1951 }
else if (pingSignature != m_lastPingSignature) {
1952 emit pingFrameReceived(PingState::PongSignatureChanged);
1953 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
1955 emit pingFrameReceived(PingState::PongSignatureIdentical);
1957 m_lastPingSignature.reset();
1960 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing) {
1961 sendFinalServerGracefulShutdownGoaway();
1962 }
else if (m_gracefulShutdownState == GracefulShutdownState::AwaitingPriorPing) {
1964 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
1965 [[maybe_unused]]
const bool ok = sendPing();
1971 emit pingFrameReceived(PingState::Ping);
1976 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
1977 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
1978 frameWriter.write(*getSocket());
1981void QHttp2Connection::handleGOAWAY()
1985 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
1988 if (inboundFrame.streamID() != connectionStreamID)
1989 return connectionError(PROTOCOL_ERROR,
"GOAWAY on invalid stream");
1994 Q_ASSERT(inboundFrame.payloadSize() >= 8);
1996 const uchar *
const src = inboundFrame.dataBegin();
1998 const quint32 lastStreamID = qFromBigEndian<quint32>(src) & lastValidStreamID;
1999 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
2005 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
2008 if (lastStreamID != 0 && (lastStreamID & 0x1) != LocalMask)
2009 return connectionError(PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
2014 if (m_lastGoAwayLastStreamID && lastStreamID > *m_lastGoAwayLastStreamID)
2015 return connectionError(PROTOCOL_ERROR,
"Repeated GOAWAY with invalid last stream ID");
2016 m_lastGoAwayLastStreamID = lastStreamID;
2018 qCDebug(qHttp2ConnectionLog,
"[%p] Received GOAWAY frame, error code %u, last stream %u",
2019 this, errorCode, lastStreamID);
2022 emit receivedGOAWAY(errorCode, lastStreamID);
2024 if (errorCode == HTTP2_NO_ERROR) {
2028 const quint32 firstPossibleStream = m_connectionType == Type::Client ? 1 : 2;
2029 const quint32 firstCancelledStream = lastStreamID ? lastStreamID + 2 : firstPossibleStream;
2030 Q_ASSERT((firstCancelledStream & 0x1) == LocalMask);
2031 for (quint32 id = firstCancelledStream; id < m_nextStreamID; id += 2) {
2032 QHttp2Stream *stream = m_streams.value(id,
nullptr);
2033 if (stream && stream->isActive())
2034 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2036 maybeCloseOnGoingAway();
2042 m_connectionAborted =
true;
2043 for (QHttp2Stream *stream : std::as_const(m_streams)) {
2044 if (stream && stream->isActive())
2045 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2051void QHttp2Connection::handleWINDOW_UPDATE()
2053 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
2055 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
2059 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
2060 const auto streamID = inboundFrame.streamID();
2061 if (streamIsIgnored(streamID))
2067 Q_ASSERT(inboundFrame.payloadSize() == 4);
2069 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
2071 if (streamID == connectionStreamID) {
2073 if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
2074 return connectionError(PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
2075 sessionSendWindowSize = sum;
2078 const auto blockedStreams = std::exchange(m_blockedStreams, {});
2079 for (quint32 blockedStreamID : blockedStreams) {
2080 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
2081 if (!stream || !stream->isActive() || !stream->isUploadingDATA())
2083 if (stream->isUploadBlocked()) {
2084 m_blockedStreams.insert(blockedStreamID);
2088 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2089 Qt::QueuedConnection);
2093 QHttp2Stream *stream = m_streams.value(streamID);
2094 if (!stream || !stream->isActive()) {
2096 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
2099 }
else if (!valid) {
2100 return stream->streamError(PROTOCOL_ERROR,
2101 QLatin1String(
"WINDOW_UPDATE invalid delta"));
2103 stream->handleWINDOW_UPDATE(inboundFrame);
2107void QHttp2Connection::handleCONTINUATION()
2109 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
2110 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
2111 qCDebug(qHttp2ConnectionLog,
2112 "[%p] Received CONTINUATION frame with payload size %u, "
2113 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
2114 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
2115 return connectionError(Http2Error::FRAME_SIZE_ERROR,
2116 "CONTINUATION payload size exceeds SETTINGS_MAX_FRAME_SIZE");
2118 auto streamID = inboundFrame.streamID();
2119 qCDebug(qHttp2ConnectionLog,
2120 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
2121 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
2122 if (continuedFrames.empty())
2123 return connectionError(PROTOCOL_ERROR,
2124 "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
2125 if (!continuationExpected)
2126 return connectionError(PROTOCOL_ERROR,
2127 "CONTINUATION after a frame with the END_HEADERS flag set");
2129 if (inboundFrame.streamID() != continuedFrames.front().streamID())
2130 return connectionError(PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
2132 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
2133 continuedFrames.push_back(std::move(inboundFrame));
2138 continuationExpected =
false;
2139 handleContinuedHEADERS();
2142void QHttp2Connection::handleContinuedHEADERS()
2147 Q_ASSERT(!continuedFrames.empty());
2148 const auto firstFrameType = continuedFrames[0].type();
2149 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
2151 const auto streamID = continuedFrames[0].streamID();
2153 const auto streamIt = m_streams.constFind(streamID);
2154 if (firstFrameType == FrameType::HEADERS) {
2155 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
2156 QHttp2Stream *stream = streamIt.value();
2157 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
2158 && stream->state() != QHttp2Stream::State::ReservedRemote
2159 && stream->state() != QHttp2Stream::State::Idle
2160 && stream->state() != QHttp2Stream::State::Open) {
2164 return stream->streamError(PROTOCOL_ERROR,
"HEADERS on invalid stream"_L1);
2172 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
2173 const bool hasHeaderFields = !hpackBlock.empty();
2174 if (hasHeaderFields) {
2175 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
2176 if (!decoder.decodeHeaderFields(inputStream))
2177 return connectionError(COMPRESSION_ERROR,
"HPACK decompression failed");
2179 if (firstFrameType == FrameType::PUSH_PROMISE) {
2188 if (streamIt != m_streams.cend()) {
2189 (*streamIt)->streamError(PROTOCOL_ERROR,
2190 QLatin1String(
"PUSH_PROMISE with incomplete headers"));
2196 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
2197 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
2198 hpackBlockHasContent);
2200 return connectionError(FRAME_SIZE_ERROR,
"HEADERS frame too large");
2203 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
2205 if (streamIsIgnored(streamID)) {
2209 if (continuedFrames[0].flags().testFlag(Http2::FrameFlag::END_STREAM)) {
2210 if (QHttp2Stream *stream = streamIt.value()) {
2211 stream->setState(QHttp2Stream::State::Closed);
2218 switch (firstFrameType) {
2219 case FrameType::HEADERS:
2220 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2222 case FrameType::PUSH_PROMISE: {
2223 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
2226 if (m_promisedStreams.contains(*promiseKey))
2228 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
2229 QHttp2Stream *stream = m_streams.value(promiseID);
2230 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
2231 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2232 emit newPromisedStream(stream);
2233 m_promisedStreams.emplace(*promiseKey, promiseID);
2241bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
2243 switch (identifier) {
2244 case Settings::HEADER_TABLE_SIZE_ID: {
2245 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
2246 if (newValue > maxAcceptableTableSize) {
2247 connectionError(PROTOCOL_ERROR,
"SETTINGS invalid table size");
2250 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
2251 qCDebug(qHttp2ConnectionLog,
2252 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
2257 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
2258 pendingTableSizeUpdates[0] = newValue;
2259 pendingTableSizeUpdates[1].reset();
2260 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
2262 pendingTableSizeUpdates[1] = newValue;
2263 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
2264 this, newValue, *pendingTableSizeUpdates[0]);
2268 case Settings::INITIAL_WINDOW_SIZE_ID: {
2269 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
2273 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
2274 connectionError(FLOW_CONTROL_ERROR,
"SETTINGS invalid initial window size");
2278 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2279 streamInitialSendWindowSize = qint32(newValue);
2281 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2282 this, size_t(m_streams.size()), delta);
2283 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2287 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2288 stream->streamError(PROTOCOL_ERROR,
"SETTINGS window overflow"_L1);
2291 stream->m_sendWindow = sum;
2292 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2293 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2294 Qt::QueuedConnection);
2299 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2300 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2302 m_peerMaxConcurrentStreams = newValue;
2305 case Settings::MAX_FRAME_SIZE_ID: {
2306 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2307 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2308 connectionError(PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
2311 maxFrameSize = newValue;
2314 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2315 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2320 m_maxHeaderListSize = newValue;
2323 case Http2::Settings::ENABLE_PUSH_ID:
2324 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2325 if (newValue != 0 && newValue != 1) {
2326 connectionError(PROTOCOL_ERROR,
"SETTINGS peer sent illegal value for ENABLE_PUSH");
2329 if (m_connectionType == Type::Client) {
2330 if (newValue == 1) {
2331 connectionError(PROTOCOL_ERROR,
"SETTINGS server sent ENABLE_PUSH=1");
2335 pushPromiseEnabled = newValue;
2345#include "moc_qhttp2connection_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)