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 (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1578 qCDebug(qHttp2ConnectionLog,
1579 "[%p] Received DATA frame with payload size %u, "
1580 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1581 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1582 return connectionError(FLOW_CONTROL_ERROR,
"Flow control error");
1585 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1588 stream->handleDATA(inboundFrame);
1591 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
1592 const bool ignoreData = stream && streamIsIgnored(stream->streamID());
1594 emit receivedEND_STREAM(streamID);
1600 stream->setState(QHttp2Stream::State::Closed);
1605 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1607 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1608 quint32(connectionStreamID),
1609 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1610 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1614void QHttp2Connection::handleHEADERS()
1616 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1618 const auto streamID = inboundFrame.streamID();
1619 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1620 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1624 if (streamID == connectionStreamID)
1625 return connectionError(PROTOCOL_ERROR,
"HEADERS on 0x0 stream");
1627 const bool isClient = m_connectionType == Type::Client;
1628 const bool isClientInitiatedStream = !!(streamID & 1);
1629 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1631 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1632 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1633 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1634 Q_ASSERT(newStream);
1635 m_lastIncomingStreamID = streamID;
1637 if (!streamCountIsOk) {
1638 newStream->setState(QHttp2Stream::State::Open);
1639 newStream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1641 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1645 qCDebug(qHttp2ConnectionLog,
"[%p] New incoming stream %d",
this, streamID);
1646 if (!streamIsIgnored(newStream->streamID())) {
1647 emit newIncomingStream(newStream);
1648 }
else if (m_goawayGraceTimer.hasExpired()) {
1651 connectionError(Http2Error::PROTOCOL_ERROR,
"Peer refused to GOAWAY.");
1654 }
else if (streamWasResetLocally(streamID)) {
1655 qCDebug(qHttp2ConnectionLog,
1656 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1659 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1662 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1664 return connectionError(PROTOCOL_ERROR,
"HEADERS on invalid stream");
1665 }
else if (isInvalidStream(streamID)) {
1668 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1669 return connectionError(ENHANCE_YOUR_CALM,
"HEADERS on invalid stream");
1672 const auto flags = inboundFrame.flags();
1673 if (flags.testFlag(FrameFlag::PRIORITY)) {
1674 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1679 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1680 continuedFrames.clear();
1681 continuedFrames.push_back(std::move(inboundFrame));
1683 continuationExpected =
true;
1687 handleContinuedHEADERS();
1690void QHttp2Connection::handlePRIORITY()
1692 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1693 || inboundFrame.type() == FrameType::HEADERS);
1695 const auto streamID = inboundFrame.streamID();
1696 if (streamIsIgnored(streamID))
1701 if (streamID == connectionStreamID)
1702 return connectionError(PROTOCOL_ERROR,
"PRIORITY on 0x0 stream");
1706 if (isInvalidStream(streamID))
1707 return connectionError(ENHANCE_YOUR_CALM,
"PRIORITY on invalid stream");
1712 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1714 quint32 streamDependency = 0;
1716 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1720 const bool exclusive = streamDependency & 0x80000000;
1721 streamDependency &= ~0x80000000;
1725 Q_UNUSED(exclusive);
1729void QHttp2Connection::handleRST_STREAM()
1731 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1733 const auto streamID = inboundFrame.streamID();
1734 if (streamIsIgnored(streamID))
1741 if (streamID == connectionStreamID)
1742 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on 0x0");
1747 Q_ASSERT(inboundFrame.payloadSize() == 4);
1749 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1750 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1751 emit stream->rstFrameReceived(error);
1754 const quint32 lastRelevantStreamID = [
this, streamID]() {
1755 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1756 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1758 if (streamID > lastRelevantStreamID) {
1762 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on idle stream");
1765 Q_ASSERT(inboundFrame.dataSize() == 4);
1767 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1768 stream->handleRST_STREAM(inboundFrame);
1771void QHttp2Connection::handleSETTINGS()
1774 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1778 if (inboundFrame.streamID() != connectionStreamID)
1779 return connectionError(PROTOCOL_ERROR,
"SETTINGS on invalid stream");
1781 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1784 if (inboundFrame.payloadSize ())
1785 return connectionError(FRAME_SIZE_ERROR,
"SETTINGS ACK with data");
1786 if (!waitingForSettingsACK)
1787 return connectionError(PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
1788 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1789 waitingForSettingsACK =
false;
1792 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1794 if (inboundFrame.dataSize()) {
1798 Q_ASSERT (inboundFrame.payloadSize() % 6 == 0);
1800 auto src = inboundFrame.dataBegin();
1801 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1802 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1803 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1804 if (!acceptSetting(identifier, intVal)) {
1806 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1807 quint32(identifier), intVal);
1813 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1815 emit settingsFrameReceived();
1818void QHttp2Connection::handlePUSH_PROMISE()
1821 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1826 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1829 return connectionError(PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
1834 const auto streamID = inboundFrame.streamID();
1835 if (streamID == connectionStreamID)
1836 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid associated stream (0x0)");
1838 auto it = m_streams.constFind(streamID);
1840 if (it != m_streams.constEnd()) {
1841 QHttp2Stream *associatedStream = it->get();
1842 if (associatedStream->state() != QHttp2Stream::State::Open
1843 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1845 it = m_streams.constEnd();
1855 if (it == m_streams.constEnd())
1856 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1857 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1858 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1859 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1861 if ((*it)->state() != QHttp2Stream::State::Open &&
1862 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1863 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1868 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1869 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1870 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid promised stream ID");
1872 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1875 auto *stream = createStreamInternal_impl(reservedID);
1877 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with already active stream ID");
1878 m_lastIncomingStreamID = reservedID;
1879 stream->setState(QHttp2Stream::State::ReservedRemote);
1881 if (!streamCountIsOk) {
1882 stream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1883 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1889 if (!pushPromiseEnabled) {
1890 return stream->streamError(REFUSE_STREAM,
1891 QLatin1String(
"PUSH_PROMISE not enabled but ignored"));
1898 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1899 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1900 continuedFrames.clear();
1901 continuedFrames.push_back(std::move(inboundFrame));
1904 continuationExpected =
true;
1908 handleContinuedHEADERS();
1911void QHttp2Connection::handlePING()
1913 Q_ASSERT(inboundFrame.type() == FrameType::PING);
1918 if (inboundFrame.streamID() != connectionStreamID)
1919 return connectionError(PROTOCOL_ERROR,
"PING on invalid stream");
1924 Q_ASSERT(inboundFrame.payloadSize() == 8);
1926 if (inboundFrame.flags() & FrameFlag::ACK) {
1927 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
1928 if (!m_lastPingSignature.has_value()) {
1929 emit pingFrameReceived(PingState::PongNoPingSent);
1930 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
1931 }
else if (pingSignature != m_lastPingSignature) {
1932 emit pingFrameReceived(PingState::PongSignatureChanged);
1933 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
1935 emit pingFrameReceived(PingState::PongSignatureIdentical);
1937 m_lastPingSignature.reset();
1940 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing) {
1941 sendFinalServerGracefulShutdownGoaway();
1942 }
else if (m_gracefulShutdownState == GracefulShutdownState::AwaitingPriorPing) {
1944 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
1945 [[maybe_unused]]
const bool ok = sendPing();
1951 emit pingFrameReceived(PingState::Ping);
1956 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
1957 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
1958 frameWriter.write(*getSocket());
1961void QHttp2Connection::handleGOAWAY()
1965 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
1968 if (inboundFrame.streamID() != connectionStreamID)
1969 return connectionError(PROTOCOL_ERROR,
"GOAWAY on invalid stream");
1974 Q_ASSERT(inboundFrame.payloadSize() >= 8);
1976 const uchar *
const src = inboundFrame.dataBegin();
1978 const quint32 lastStreamID = qFromBigEndian<quint32>(src) & lastValidStreamID;
1979 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
1985 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
1988 if (lastStreamID != 0 && (lastStreamID & 0x1) != LocalMask)
1989 return connectionError(PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
1994 if (m_lastGoAwayLastStreamID && lastStreamID > *m_lastGoAwayLastStreamID)
1995 return connectionError(PROTOCOL_ERROR,
"Repeated GOAWAY with invalid last stream ID");
1996 m_lastGoAwayLastStreamID = lastStreamID;
1998 qCDebug(qHttp2ConnectionLog,
"[%p] Received GOAWAY frame, error code %u, last stream %u",
1999 this, errorCode, lastStreamID);
2002 emit receivedGOAWAY(errorCode, lastStreamID);
2004 if (errorCode == HTTP2_NO_ERROR) {
2008 const quint32 firstPossibleStream = m_connectionType == Type::Client ? 1 : 2;
2009 const quint32 firstCancelledStream = lastStreamID ? lastStreamID + 2 : firstPossibleStream;
2010 Q_ASSERT((firstCancelledStream & 0x1) == LocalMask);
2011 for (quint32 id = firstCancelledStream; id < m_nextStreamID; id += 2) {
2012 QHttp2Stream *stream = m_streams.value(id,
nullptr);
2013 if (stream && stream->isActive())
2014 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2016 maybeCloseOnGoingAway();
2022 m_connectionAborted =
true;
2023 for (QHttp2Stream *stream : std::as_const(m_streams)) {
2024 if (stream && stream->isActive())
2025 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2031void QHttp2Connection::handleWINDOW_UPDATE()
2033 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
2035 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
2039 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
2040 const auto streamID = inboundFrame.streamID();
2041 if (streamIsIgnored(streamID))
2047 Q_ASSERT(inboundFrame.payloadSize() == 4);
2049 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
2051 if (streamID == connectionStreamID) {
2053 if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
2054 return connectionError(PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
2055 sessionSendWindowSize = sum;
2058 const auto blockedStreams = std::exchange(m_blockedStreams, {});
2059 for (quint32 blockedStreamID : blockedStreams) {
2060 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
2061 if (!stream || !stream->isActive())
2063 if (stream->isUploadingDATA() && !stream->isUploadBlocked())
2064 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2065 Qt::QueuedConnection);
2068 QHttp2Stream *stream = m_streams.value(streamID);
2069 if (!stream || !stream->isActive()) {
2071 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
2074 }
else if (!valid) {
2075 return stream->streamError(PROTOCOL_ERROR,
2076 QLatin1String(
"WINDOW_UPDATE invalid delta"));
2078 stream->handleWINDOW_UPDATE(inboundFrame);
2082void QHttp2Connection::handleCONTINUATION()
2084 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
2085 auto streamID = inboundFrame.streamID();
2086 qCDebug(qHttp2ConnectionLog,
2087 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
2088 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
2089 if (continuedFrames.empty())
2090 return connectionError(PROTOCOL_ERROR,
2091 "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
2092 if (!continuationExpected)
2093 return connectionError(PROTOCOL_ERROR,
2094 "CONTINUATION after a frame with the END_HEADERS flag set");
2096 if (inboundFrame.streamID() != continuedFrames.front().streamID())
2097 return connectionError(PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
2099 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
2100 continuedFrames.push_back(std::move(inboundFrame));
2105 continuationExpected =
false;
2106 handleContinuedHEADERS();
2109void QHttp2Connection::handleContinuedHEADERS()
2114 Q_ASSERT(!continuedFrames.empty());
2115 const auto firstFrameType = continuedFrames[0].type();
2116 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
2118 const auto streamID = continuedFrames[0].streamID();
2120 const auto streamIt = m_streams.constFind(streamID);
2121 if (firstFrameType == FrameType::HEADERS) {
2122 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
2123 QHttp2Stream *stream = streamIt.value();
2124 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
2125 && stream->state() != QHttp2Stream::State::ReservedRemote
2126 && stream->state() != QHttp2Stream::State::Idle
2127 && stream->state() != QHttp2Stream::State::Open) {
2131 return stream->streamError(PROTOCOL_ERROR,
"HEADERS on invalid stream"_L1);
2139 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
2140 const bool hasHeaderFields = !hpackBlock.empty();
2141 if (hasHeaderFields) {
2142 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
2143 if (!decoder.decodeHeaderFields(inputStream))
2144 return connectionError(COMPRESSION_ERROR,
"HPACK decompression failed");
2146 if (firstFrameType == FrameType::PUSH_PROMISE) {
2155 if (streamIt != m_streams.cend()) {
2156 (*streamIt)->streamError(PROTOCOL_ERROR,
2157 QLatin1String(
"PUSH_PROMISE with incomplete headers"));
2163 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
2164 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
2165 hpackBlockHasContent);
2167 return connectionError(FRAME_SIZE_ERROR,
"HEADERS frame too large");
2170 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
2172 if (streamIsIgnored(streamID)) {
2176 if (continuedFrames[0].flags().testFlag(Http2::FrameFlag::END_STREAM)) {
2177 if (QHttp2Stream *stream = streamIt.value()) {
2178 stream->setState(QHttp2Stream::State::Closed);
2185 switch (firstFrameType) {
2186 case FrameType::HEADERS:
2187 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2189 case FrameType::PUSH_PROMISE: {
2190 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
2193 if (m_promisedStreams.contains(*promiseKey))
2195 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
2196 QHttp2Stream *stream = m_streams.value(promiseID);
2197 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
2198 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2199 emit newPromisedStream(stream);
2200 m_promisedStreams.emplace(*promiseKey, promiseID);
2208bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
2210 switch (identifier) {
2211 case Settings::HEADER_TABLE_SIZE_ID: {
2212 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
2213 if (newValue > maxAcceptableTableSize) {
2214 connectionError(PROTOCOL_ERROR,
"SETTINGS invalid table size");
2217 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
2218 qCDebug(qHttp2ConnectionLog,
2219 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
2224 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
2225 pendingTableSizeUpdates[0] = newValue;
2226 pendingTableSizeUpdates[1].reset();
2227 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
2229 pendingTableSizeUpdates[1] = newValue;
2230 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
2231 this, newValue, *pendingTableSizeUpdates[0]);
2235 case Settings::INITIAL_WINDOW_SIZE_ID: {
2236 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
2240 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
2241 connectionError(FLOW_CONTROL_ERROR,
"SETTINGS invalid initial window size");
2245 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2246 streamInitialSendWindowSize = qint32(newValue);
2248 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2249 this, size_t(m_streams.size()), delta);
2250 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2254 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2255 stream->streamError(PROTOCOL_ERROR,
"SETTINGS window overflow"_L1);
2258 stream->m_sendWindow = sum;
2259 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2260 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2261 Qt::QueuedConnection);
2266 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2267 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2269 m_peerMaxConcurrentStreams = newValue;
2272 case Settings::MAX_FRAME_SIZE_ID: {
2273 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2274 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2275 connectionError(PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
2278 maxFrameSize = newValue;
2281 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2282 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2287 m_maxHeaderListSize = newValue;
2290 case Http2::Settings::ENABLE_PUSH_ID:
2291 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2292 if (newValue != 0 && newValue != 1) {
2293 connectionError(PROTOCOL_ERROR,
"SETTINGS peer sent illegal value for ENABLE_PUSH");
2296 if (m_connectionType == Type::Client) {
2297 if (newValue == 1) {
2298 connectionError(PROTOCOL_ERROR,
"SETTINGS server sent ENABLE_PUSH=1");
2302 pushPromiseEnabled = newValue;
2312#include "moc_qhttp2connection_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)