8#include <private/bitstreams_p.h>
10#include <QtCore/private/qnumeric_p.h>
11#include <QtCore/private/qiodevice_p.h>
12#include <QtCore/private/qnoncontiguousbytedevice_p.h>
13#include <QtCore/qcoreapplication.h>
14#include <QtCore/QRandomGenerator>
15#include <QtCore/qloggingcategory.h>
25using namespace Qt::StringLiterals;
29
30
31
32
33
34
35
36
37
40
41
42
43
44
45
46
47
48
49
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
87QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID,
88 Configuration configuration)
noexcept
89 : QObject(connection), m_streamID(streamID), m_configuration(configuration)
93 qCDebug(qHttp2ConnectionLog,
"[%p] new stream %u", connection, streamID);
96QHttp2Stream::~QHttp2Stream()
noexcept {
97 if (
auto *connection = getConnection()) {
98 if (m_state != State::Idle && m_state != State::Closed) {
99 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, destroyed while still open", connection,
102 if (connection->getSocket()) {
103 if (isUploadingDATA())
104 sendRST_STREAM(CANCEL);
106 sendRST_STREAM(HTTP2_NO_ERROR);
110 connection->m_streams.remove(streamID());
115
116
117
118
121
122
123
124
125
126
127
128
129
130
131
132
133
136
137
138
139
140
141
142
145
146
147
148
149
150
151
152
155
156
157
158
159
160
161
162
163
164
165
169
170
171
172
173
174
175
178
179
180
181
182
183
184
185
188
189
190
191
192
193
194
195
198
199
200
201
202
205
206
207
208
209
210
213
214
215
216
217
218
219
220
223
224
225
226
229
230
231
232
233
234
236
237
238
239
241
242
243
244
246
247
248
249
251
252
253
254
255
257
258
259
260
262
263
264
265
268
269
270
271
273void QHttp2Stream::finishWithError(Http2::Http2Error errorCode,
const QString &message)
275 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
276 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
277 transitionState(StateTransition::RST);
278 emit errorOccurred(errorCode, message);
281void QHttp2Stream::finishWithError(Http2::Http2Error errorCode)
283 QNetworkReply::NetworkError ignored = QNetworkReply::NoError;
285 qt_error(errorCode, ignored, message);
286 finishWithError(errorCode, message);
289void QHttp2Stream::streamError(Http2::Http2Error errorCode,
const QString &message)
291 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
292 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
294 sendRST_STREAM(errorCode);
295 emit errorOccurred(errorCode, message);
299
300
301
302
303
304
305bool QHttp2Stream::sendRST_STREAM(Http2::Http2Error errorCode)
307 if (m_state == State::Closed || m_state == State::Idle) {
308 qCDebug(qHttp2ConnectionLog,
"[%p] could not send RST_STREAM on %s stream %u",
309 getConnection(), QDebug::toBytes(m_state).constData(), m_streamID);
313 if (m_RST_STREAM_received.has_value())
316 getConnection()->registerStreamAsResetLocally(streamID());
318 m_RST_STREAM_sent = errorCode;
319 qCDebug(qHttp2ConnectionLog,
"[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
320 m_streamID, errorCode);
321 transitionState(StateTransition::RST);
323 QHttp2Connection *connection = getConnection();
324 FrameWriter &frameWriter = connection->frameWriter;
325 frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
326 frameWriter.append(quint32(errorCode));
327 return frameWriter.write(*connection->getSocket());
331
332
333
334
335
336
337
338
339
340
341
342bool QHttp2Stream::sendDATA(
const QByteArray &payload,
bool endStream)
344 Q_ASSERT(!m_uploadByteDevice);
345 if (m_state != State::Open && m_state != State::HalfClosedRemote)
348 auto *byteDevice = QNonContiguousByteDeviceFactory::create(payload);
349 m_owningByteDevice =
true;
350 byteDevice->setParent(
this);
351 return sendDATA(byteDevice, endStream);
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369bool QHttp2Stream::sendDATA(QIODevice *device,
bool endStream)
371 Q_ASSERT(!m_uploadDevice);
372 Q_ASSERT(!m_uploadByteDevice);
374 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
375 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
377 getConnection(), m_streamID, device);
381 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
382 getConnection(), m_streamID, device);
383 auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
384 m_owningByteDevice =
true;
385 byteDevice->setParent(
this);
386 m_uploadDevice = device;
387 return sendDATA(byteDevice, endStream);
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405bool QHttp2Stream::sendDATA(QNonContiguousByteDevice *device,
bool endStream)
407 Q_ASSERT(!m_uploadByteDevice);
409 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
410 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
412 getConnection(), m_streamID, device);
416 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
417 getConnection(), m_streamID, device);
418 m_uploadByteDevice = device;
419 m_endStreamAfterDATA = endStream;
420 connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead,
this,
421 &QHttp2Stream::maybeResumeUpload);
422 connect(m_uploadByteDevice, &QObject::destroyed,
this, &QHttp2Stream::uploadDeviceDestroyed);
430void QHttp2Stream::internalSendDATA()
432 Q_ASSERT(m_uploadByteDevice);
433 QHttp2Connection *connection = getConnection();
434 Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
435 QIODevice *socket = connection->getSocket();
437 qCDebug(qHttp2ConnectionLog,
438 "[%p] stream %u, about to write to socket, current session window size: %d, stream "
439 "window size: %d, bytes available: %lld",
440 connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
441 m_uploadByteDevice->size() - m_uploadByteDevice->pos());
443 qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
444 FrameWriter &frameWriter = connection->frameWriter;
445 qint64 totalBytesWritten = 0;
446 const auto deviceCanRead = [
this, connection] {
451 const qint64 requestSize = connection->maxFrameSize * 10ll;
453 return m_uploadByteDevice->readPointer(requestSize, tmp) !=
nullptr && tmp > 0;
456 bool sentEND_STREAM =
false;
457 while (remainingWindowSize && deviceCanRead()) {
458 quint32 bytesWritten = 0;
459 qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
460 frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
462 while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
463 const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
465 qint64 outBytesAvail = 0;
466 const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
467 if (!readPointer || outBytesAvail <= 0) {
468 qCDebug(qHttp2ConnectionLog,
469 "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
470 connection, m_streamID, m_uploadByteDevice, outBytesAvail);
473 const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
474 frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
475 m_uploadByteDevice->advanceReadPointer(bytesToWrite);
477 bytesWritten += bytesToWrite;
479 m_sendWindow -= bytesToWrite;
480 Q_ASSERT(m_sendWindow >= 0);
481 connection->sessionSendWindowSize -= bytesToWrite;
482 Q_ASSERT(connection->sessionSendWindowSize >= 0);
483 remainingBytesInFrame -= bytesToWrite;
484 Q_ASSERT(remainingBytesInFrame >= 0);
485 remainingWindowSize -= bytesToWrite;
486 Q_ASSERT(remainingWindowSize >= 0);
489 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, writing %u bytes to socket", connection,
490 m_streamID, bytesWritten);
491 if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
492 sentEND_STREAM =
true;
493 frameWriter.addFlag(FrameFlag::END_STREAM);
495 if (!frameWriter.write(*socket)) {
496 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, failed to write to socket", connection,
498 return finishWithError(INTERNAL_ERROR, u"failed to write to socket"_s);
501 totalBytesWritten += bytesWritten;
504 qCDebug(qHttp2ConnectionLog,
505 "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
506 "more later. Remaining window size: %d",
507 connection, m_streamID, totalBytesWritten, remainingWindowSize);
509 emit bytesWritten(totalBytesWritten);
510 if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
511 qCDebug(qHttp2ConnectionLog,
512 "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
514 connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
515 !sentEND_STREAM && m_endStreamAfterDATA ?
"" :
"not ");
516 if (!sentEND_STREAM && m_endStreamAfterDATA) {
521 frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
522 frameWriter.write(*socket);
525 }
else if (isUploadBlocked()) {
526 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, upload blocked", connection, m_streamID);
527 emit uploadBlocked();
531void QHttp2Stream::finishSendDATA()
533 if (m_endStreamAfterDATA)
534 transitionState(StateTransition::CloseLocal);
536 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
537 m_uploadDevice =
nullptr;
538 if (m_owningByteDevice) {
539 m_owningByteDevice =
false;
540 delete m_uploadByteDevice;
542 m_uploadByteDevice =
nullptr;
543 emit uploadFinished();
546void QHttp2Stream::maybeResumeUpload()
548 qCDebug(qHttp2ConnectionLog,
549 "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
551 getConnection(), m_streamID, m_uploadByteDevice,
552 !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
554 if (isUploadingDATA() && !isUploadBlocked())
557 getConnection()->m_blockedStreams.insert(streamID());
561
562
563
564bool QHttp2Stream::isUploadBlocked()
const noexcept
566 constexpr auto MinFrameSize = Http2::frameHeaderSize + 1;
567 return isUploadingDATA()
568 && (m_sendWindow <= MinFrameSize
569 || getConnection()->sessionSendWindowSize <= MinFrameSize);
572void QHttp2Stream::uploadDeviceReadChannelFinished()
578
579
580
581
582
583
584bool QHttp2Stream::sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream, quint8 priority)
586 using namespace HPack;
587 if (
auto hs = header_size(headers);
588 !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
592 transitionState(StateTransition::Open);
594 Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
596 QHttp2Connection *connection = getConnection();
598 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending HEADERS frame with %u entries",
599 connection, streamID(), uint(headers.size()));
601 QIODevice *socket = connection->getSocket();
602 FrameWriter &frameWriter = connection->frameWriter;
604 frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
606 frameWriter.addFlag(FrameFlag::END_STREAM);
608 frameWriter.append(quint32());
609 frameWriter.append(priority);
612 BitOStream outputStream(frameWriter.outboundFrame().buffer);
615 for (
auto &maybePendingTableSizeUpdate : connection->pendingTableSizeUpdates) {
616 if (!maybePendingTableSizeUpdate)
618 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending dynamic table size update of size %u",
619 connection, streamID(), *maybePendingTableSizeUpdate);
620 connection->encoder.setMaxDynamicTableSize(*maybePendingTableSizeUpdate);
621 connection->encoder.encodeSizeUpdate(outputStream, *maybePendingTableSizeUpdate);
622 maybePendingTableSizeUpdate.reset();
625 if (connection->m_connectionType == QHttp2Connection::Type::Client) {
626 if (!connection->encoder.encodeRequest(outputStream, headers))
629 if (!connection->encoder.encodeResponse(outputStream, headers))
633 bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
635 transitionState(StateTransition::CloseLocal);
641
642
643
644
645void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
647 QHttp2Connection *connection = getConnection();
648 m_recvWindow += qint32(delta);
649 connection->sendWINDOW_UPDATE(streamID(), delta);
652void QHttp2Stream::uploadDeviceDestroyed()
654 if (isUploadingDATA()) {
657 const QString message = u"Upload device destroyed while uploading"_s;
658 streamError(CANCEL, message);
659 emit uploadDeviceError(message);
661 m_uploadDevice =
nullptr;
662 m_owningByteDevice =
false;
663 m_uploadByteDevice =
nullptr;
666void QHttp2Stream::setState(State newState)
668 if (m_state == newState)
670 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, state changed from %d to %d", getConnection(),
671 streamID(),
int(m_state),
int(newState));
673 emit stateChanged(newState);
674 if (m_state == State::Closed)
675 getConnection()->maybeCloseOnGoingAway();
681void QHttp2Stream::transitionState(StateTransition transition)
685 if (transition == StateTransition::Open)
686 setState(State::Open);
691 switch (transition) {
692 case StateTransition::CloseLocal:
693 setState(State::HalfClosedLocal);
695 case StateTransition::CloseRemote:
696 setState(State::HalfClosedRemote);
698 case StateTransition::RST:
699 setState(State::Closed);
701 case StateTransition::Open:
705 case State::HalfClosedLocal:
706 if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
707 setState(State::Closed);
709 case State::HalfClosedRemote:
710 if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
711 setState(State::Closed);
713 case State::ReservedRemote:
714 if (transition == StateTransition::RST) {
715 setState(State::Closed);
716 }
else if (transition == StateTransition::CloseLocal) {
717 setState(State::HalfClosedLocal);
725void QHttp2Stream::handleDATA(
const Frame &inboundFrame)
727 QHttp2Connection *connection = getConnection();
729 qCDebug(qHttp2ConnectionLog,
730 "[%p] stream %u, received DATA frame with payload of %u bytes, closing stream? %s",
731 connection, m_streamID, inboundFrame.payloadSize(),
732 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
738 Q_ASSERT(state() != State::HalfClosedRemote && state() != State::Closed);
740 if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
741 qCDebug(qHttp2ConnectionLog,
742 "[%p] stream %u, received DATA frame with payload size %u, "
743 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
744 connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
745 return streamError(FLOW_CONTROL_ERROR, u"data bigger than window size"_s);
751 Q_ASSERT(inboundFrame.buffer.size() >= frameHeaderSize);
752 Q_ASSERT(inboundFrame.payloadSize() + frameHeaderSize == inboundFrame.buffer.size());
754 m_recvWindow -= qint32(inboundFrame.payloadSize());
755 const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
756 const bool ignoreData = connection->streamIsIgnored(m_streamID);
758 if ((inboundFrame.dataSize() > 0 || endStream) && !ignoreData) {
759 QByteArray fragment(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()),
760 inboundFrame.dataSize());
762 transitionState(StateTransition::CloseRemote);
763 const auto shouldBuffer = m_configuration.useDownloadBuffer && !fragment.isEmpty();
764 QPointer<QHttp2Stream> self(
this);
767 m_downloadBuffer.append(std::move(fragment));
768 emit dataReceived(m_downloadBuffer.last(), endStream);
770 emit dataReceived(fragment, endStream);
776 if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
778 sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
782void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers)
784 if (m_state == State::Idle)
785 transitionState(StateTransition::Open);
786 const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
788 transitionState(StateTransition::CloseRemote);
789 if (!headers.empty() && m_configuration.useHeaderBuffer) {
790 m_headers.insert(m_headers.end(), headers.begin(), headers.end());
791 emit headersUpdated();
793 emit headersReceived(headers, endStream);
796void QHttp2Stream::handleRST_STREAM(
const Frame &inboundFrame)
798 if (m_state == State::Closed)
801 transitionState(StateTransition::RST);
802 m_RST_STREAM_received = qFromBigEndian<quint32>(inboundFrame.dataBegin());
803 if (isUploadingDATA()) {
804 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
805 m_uploadDevice =
nullptr;
806 m_uploadByteDevice =
nullptr;
808 finishWithError(Http2Error(*m_RST_STREAM_received));
811void QHttp2Stream::handleWINDOW_UPDATE(
const Frame &inboundFrame)
813 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
814 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
818 qCDebug(qHttp2ConnectionLog,
819 "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
821 getConnection(), m_streamID, delta);
822 return streamError(PROTOCOL_ERROR, u"invalid WINDOW_UPDATE delta"_s);
825 if (qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
828 qCDebug(qHttp2ConnectionLog,
829 "[%p] stream %u, WINDOW_UPDATE delta %u overflows the flow-control window, "
830 "sending FLOW_CONTROL_ERROR",
831 getConnection(), m_streamID, delta);
832 return streamError(FLOW_CONTROL_ERROR, u"WINDOW_UPDATE exceeds maximum window"_s);
836 if (isUploadingDATA())
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
859
860
861
862
863
866
867
868
869
872
873
874
875
878
879
880
881
884
885
886
887
890
891
892
893
894
895
898
899
900
901
902
903
904
905
908
909
910
911
912
913
914QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
915 const QHttp2Configuration &config)
919 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
920 connection->setH2Configuration(config);
921 connection->m_connectionType = QHttp2Connection::Type::Client;
922 connection->m_upgradedConnection =
true;
925 QHttp2Stream *stream = connection->createLocalStreamInternal().unwrap();
926 Q_ASSERT(stream->streamID() == 1);
927 stream->setState(QHttp2Stream::State::HalfClosedLocal);
929 if (!connection->m_prefaceSent)
932 return connection.release();
936
937
938
939
940
941QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
942 const QHttp2Configuration &config)
944 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
945 connection->setH2Configuration(config);
946 connection->m_connectionType = QHttp2Connection::Type::Client;
948 return connection.release();
952
953
954
955
956QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
957 const QHttp2Configuration &config)
959 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
960 connection->setH2Configuration(config);
961 connection->m_connectionType = QHttp2Connection::Type::Server;
963 connection->m_nextStreamID = 2;
965 connection->m_waitingForClientPreface =
true;
967 return connection.release();
971
972
973
974
975
976
977
978
979
980
981
982
985
986
987
988
989QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
990QHttp2Connection::createStream(QHttp2Stream::Configuration configuration)
992 Q_ASSERT(m_connectionType == Type::Client);
993 if (m_nextStreamID > lastValidStreamID)
994 return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
995 return createLocalStreamInternal(configuration);
998QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
999QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf)
1002 return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
1003 const quint32 streamID = m_nextStreamID;
1004 if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams()))
1005 return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
1007 if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) {
1008 m_nextStreamID += 2;
1012 return { QHttp2Connection::CreateStreamError::UnknownError };
1015QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID,
1016 QHttp2Stream::Configuration conf)
1018 Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID);
1020 if (m_connectionType == Type::Client && !m_prefaceSent && !sendClientPreface()) {
1021 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send client preface",
this);
1025 auto result = m_streams.tryEmplace(streamID,
nullptr);
1026 if (!result.inserted)
1028 QPointer<QHttp2Stream> &stream = result.iterator.value();
1029 stream =
new QHttp2Stream(
this, streamID, conf);
1030 stream->m_recvWindow = streamInitialReceiveWindowSize;
1031 stream->m_sendWindow = streamInitialSendWindowSize;
1033 connect(stream, &QHttp2Stream::uploadBlocked,
this, [
this, stream] {
1034 m_blockedStreams.insert(stream->streamID());
1036 *result.iterator = stream;
1037 return *result.iterator;
1040qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask)
const noexcept
1042 const auto shouldCount = [mask](
const QPointer<QHttp2Stream> &stream) ->
bool {
1043 return stream && (stream->streamID() & 1) == mask && stream->isActive();
1045 return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
1049
1050
1051
1052qsizetype QHttp2Connection::numActiveRemoteStreams()
const noexcept
1054 const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
1055 return numActiveStreamsImpl(RemoteMask);
1059
1060
1061
1062qsizetype QHttp2Connection::numActiveLocalStreams()
const noexcept
1064 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
1065 return numActiveStreamsImpl(LocalMask);
1069
1070
1071
1072QHttp2Stream *QHttp2Connection::getStream(quint32 streamID)
const
1074 return m_streams.value(streamID,
nullptr).get();
1078
1079
1080
1081
1082void QHttp2Connection::close(Http2::Http2Error errorCode)
1084 if (m_connectionAborted)
1087 if (errorCode == Http2::HTTP2_NO_ERROR) {
1088 if (m_connectionType == Type::Server)
1089 sendInitialServerGracefulShutdownGoaway();
1091 sendClientGracefulShutdownGoaway();
1095 connectionError(errorCode, u"Connection closed with error"_s,
false);
1100
1101
1102
1103
1104
1107
1108
1109
1110
1111
1114
1115
1116
1117
1118
1119
1122
1123
1124
1125
1128
1129
1130
1131
1132
1134QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
1137 Q_ASSERT(socket->isOpen());
1138 Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
1145QHttp2Connection::~QHttp2Connection()
1149 for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
1150 delete stream.get();
1153bool QHttp2Connection::serverCheckClientPreface()
1155 if (!m_waitingForClientPreface)
1157 auto *socket = getSocket();
1158 if (socket->bytesAvailable() < Http2::clientPrefaceLength)
1160 if (!readClientPreface()) {
1162 emit errorOccurred(Http2Error::PROTOCOL_ERROR, u"invalid client preface"_s);
1163 qCDebug(qHttp2ConnectionLog,
"[%p] Invalid client preface",
this);
1166 qCDebug(qHttp2ConnectionLog,
"[%p] Peer sent valid client preface",
this);
1167 m_waitingForClientPreface =
false;
1168 if (!sendServerPreface()) {
1169 connectionError(INTERNAL_ERROR, u"Failed to send server preface"_s);
1175bool QHttp2Connection::sendPing()
1177 std::array<
char, 8> data;
1179 QRandomGenerator gen;
1180 gen.generate(data.begin(), data.end());
1181 return sendPing(data);
1184bool QHttp2Connection::sendPing(QByteArrayView data)
1186 frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
1188 Q_ASSERT(data.length() == 8);
1189 if (!m_lastPingSignature) {
1190 m_lastPingSignature = data.toByteArray();
1192 qCWarning(qHttp2ConnectionLog,
"[%p] No PING is sent while waiting for the previous PING.",
this);
1196 frameWriter.append((uchar*)data.data(), (uchar*)data.end());
1197 frameWriter.write(*getSocket());
1202
1203
1204
1205
1206void QHttp2Connection::handleReadyRead()
1209 if (m_connectionType == Type::Server && !serverCheckClientPreface())
1212 QIODevice *socket = getSocket();
1213 if (!socket->isOpen())
1216 qCDebug(qHttp2ConnectionLog,
"[%p] Receiving data, %lld bytes available",
this,
1217 socket->bytesAvailable());
1219 using namespace Http2;
1223 while (!m_connectionAborted && socket->isOpen()) {
1224 const auto result = frameReader.read(*socket);
1225 if (result != FrameStatus::goodFrame)
1226 qCDebug(qHttp2ConnectionLog,
"[%p] Tried to read frame, got %d",
this,
int(result));
1228 case FrameStatus::incompleteFrame:
1230 case FrameStatus::protocolError:
1231 return connectionError(PROTOCOL_ERROR, u"invalid frame"_s);
1232 case FrameStatus::sizeError: {
1233 const auto streamID = frameReader.inboundFrame().streamID();
1234 const auto frameType = frameReader.inboundFrame().type();
1235 auto stream = getStream(streamID);
1241 if (frameType == FrameType::HEADERS ||
1242 frameType == FrameType::SETTINGS ||
1243 frameType == FrameType::PUSH_PROMISE ||
1244 frameType == FrameType::CONTINUATION ||
1246 frameType == FrameType::RST_STREAM ||
1247 streamID == connectionStreamID)
1248 return connectionError(FRAME_SIZE_ERROR, u"invalid frame size"_s);
1251 return stream->streamError(Http2Error::FRAME_SIZE_ERROR, u"invalid frame size"_s);
1259 Q_ASSERT(result == FrameStatus::goodFrame);
1261 inboundFrame = std::move(frameReader.inboundFrame());
1263 const auto frameType = inboundFrame.type();
1264 qCDebug(qHttp2ConnectionLog,
"[%p] Successfully read a frame, with type: %d",
this,
1271 if (continuationExpected && frameType != FrameType::CONTINUATION)
1272 return connectionError(PROTOCOL_ERROR, u"CONTINUATION expected"_s);
1274 switch (frameType) {
1275 case FrameType::DATA:
1278 case FrameType::HEADERS:
1281 case FrameType::PRIORITY:
1284 case FrameType::RST_STREAM:
1287 case FrameType::SETTINGS:
1290 case FrameType::PUSH_PROMISE:
1291 handlePUSH_PROMISE();
1293 case FrameType::PING:
1296 case FrameType::GOAWAY:
1299 case FrameType::WINDOW_UPDATE:
1300 handleWINDOW_UPDATE();
1302 case FrameType::CONTINUATION:
1303 handleCONTINUATION();
1305 case FrameType::LAST_FRAME_TYPE:
1312bool QHttp2Connection::readClientPreface()
1314 auto *socket = getSocket();
1315 Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
1316 char buffer[Http2::clientPrefaceLength];
1317 const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
1318 if (read != Http2::clientPrefaceLength)
1320 return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
1324
1325
1326
1327void QHttp2Connection::handleConnectionClosure()
1329 const auto errorString = QCoreApplication::translate(
"QHttp",
"Connection closed");
1330 const QList<quint32> streamIDs = m_streams.keys();
1331 for (quint32 streamID : streamIDs) {
1332 QHttp2Stream *stream = m_streams.value(streamID,
nullptr);
1333 if (stream && stream->isActive())
1334 stream->finishWithError(PROTOCOL_ERROR, errorString);
1338void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
1340 m_config = std::move(config);
1343 maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
1344 pushPromiseEnabled = m_config.serverPushEnabled();
1345 streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
1346 m_maxConcurrentStreams = m_config.maxConcurrentStreams();
1347 encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
1348 decoder.setMaxHeaderListSize(
1349 QHttp2ConfigurationPrivate::get(std::as_const(m_config))->maxHeaderListSize);
1352void QHttp2Connection::connectionError(Http2Error errorCode,
const QString &message,
1355 if (m_connectionAborted)
1357 m_connectionAborted =
true;
1360 qCCritical(qHttp2ConnectionLog,
"[%p] Connection error: %ls (%d)",
this,
1361 qUtf16Printable(message),
int(errorCode));
1363 qCDebug(qHttp2ConnectionLog,
"[%p] Closing connection: %ls (%d)",
this,
1364 qUtf16Printable(message),
int(errorCode));
1371 m_lastStreamToProcess = std::min(m_lastIncomingStreamID, m_lastStreamToProcess);
1372 sendGOAWAYFrame(errorCode, m_lastStreamToProcess);
1374 const QList<quint32> streamIDs = m_streams.keys();
1375 for (quint32 streamID : streamIDs) {
1376 QHttp2Stream *stream = m_streams.value(streamID,
nullptr);
1377 if (stream && stream->isActive())
1378 stream->finishWithError(errorCode, message);
1380 emit errorOccurred(errorCode, message);
1386void QHttp2Connection::closeSession()
1388 emit connectionClosed();
1391bool QHttp2Connection::streamWasResetLocally(quint32 streamID)
noexcept
1393 return m_resetStreamIDs.contains(streamID);
1396void QHttp2Connection::registerStreamAsResetLocally(quint32 streamID)
1404 m_resetStreamIDs.append(streamID);
1405 while (m_resetStreamIDs.size() > 100)
1406 m_resetStreamIDs.takeFirst();
1409bool QHttp2Connection::isInvalidStream(quint32 streamID)
noexcept
1411 auto stream = m_streams.value(streamID,
nullptr);
1412 return (!stream || stream->wasResetbyPeer()) && !streamWasResetLocally(streamID);
1416
1417
1418
1419
1420
1421
1422
1423bool QHttp2Connection::streamIsIgnored(quint32 streamID)
const noexcept
1425 const bool streamIsRemote = (streamID & 1) == (m_connectionType == Type::Client ? 0 : 1);
1426 return Q_UNLIKELY(streamIsRemote && m_lastStreamToProcess < streamID);
1429bool QHttp2Connection::sendClientPreface()
1431 QIODevice *socket = getSocket();
1433 const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
1434 if (written != clientPrefaceLength)
1437 if (!sendSETTINGS()) {
1438 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1441 m_prefaceSent =
true;
1442 if (socket->bytesAvailable())
1443 QMetaObject::invokeMethod(
this, &QHttp2Connection::handleReadyRead, Qt::QueuedConnection);
1447bool QHttp2Connection::sendServerPreface()
1451 if (!sendSETTINGS()) {
1452 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1455 m_prefaceSent =
true;
1459bool QHttp2Connection::sendSETTINGS()
1461 QIODevice *socket = getSocket();
1463 frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
1464 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS frame, %d bytes",
this,
1465 frameWriter.outboundFrame().payloadSize());
1466 Q_ASSERT(frameWriter.outboundFrame().payloadSize());
1468 if (!frameWriter.write(*socket))
1471 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1474 const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
1475 if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
1478 waitingForSettingsACK =
true;
1482bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
1484 qCDebug(qHttp2ConnectionLog,
"[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u",
this,
1486 frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
1487 frameWriter.append(delta);
1488 return frameWriter.write(*getSocket());
1492
1493
1494
1495
1496
1497
1500
1501
1502
1503
1504
1505
1506
1507
1508bool QHttp2Connection::setSessionReceiveWindowSize(qint32 size)
1511 qCWarning(qHttp2ConnectionLog,
"[%p] Invalid session receive window size: %d",
this, size);
1514 if (size <= maxSessionReceiveWindowSize) {
1516 maxSessionReceiveWindowSize = size;
1519 const qint32 delta = size - maxSessionReceiveWindowSize;
1520 maxSessionReceiveWindowSize = size;
1521 sessionReceiveWindowSize += delta;
1522 return sendWINDOW_UPDATE(connectionStreamID, quint32(delta));
1525void QHttp2Connection::sendClientGracefulShutdownGoaway()
1528 Q_ASSERT(m_connectionType == Type::Client);
1530 if (m_connectionAborted || m_goingAway) {
1531 qCWarning(qHttp2ConnectionLog,
"[%p] Client graceful shutdown already in progress",
this);
1536 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1537 m_lastStreamToProcess = m_lastIncomingStreamID;
1538 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1540 maybeCloseOnGoingAway();
1543void QHttp2Connection::sendInitialServerGracefulShutdownGoaway()
1545 Q_ASSERT(m_connectionType == Type::Server);
1549 if (m_connectionAborted || m_goingAway) {
1550 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown already in progress",
this);
1555 m_goawayGraceTimer.setRemainingTime(GoawayGracePeriod);
1556 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, Http2::lastValidStreamID);
1562 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
1564 m_gracefulShutdownState = GracefulShutdownState::AwaitingPriorPing;
1567void QHttp2Connection::sendFinalServerGracefulShutdownGoaway()
1569 if (m_connectionAborted || !m_goingAway) {
1570 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown not in progress",
this);
1573 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1574 m_lastStreamToProcess = m_lastIncomingStreamID;
1575 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1576 maybeCloseOnGoingAway();
1579bool QHttp2Connection::sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastStreamID)
1581 QIODevice *socket = getSocket();
1582 if (!socket || !socket->isOpen())
1585 qCDebug(qHttp2ConnectionLog,
"[%p] Sending GOAWAY frame, error code %u, last stream %u",
this,
1586 errorCode, lastStreamID);
1588 frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
1589 Http2PredefinedParameters::connectionStreamID);
1590 frameWriter.append(lastStreamID);
1591 frameWriter.append(quint32(errorCode));
1592 return frameWriter.write(*socket);
1595void QHttp2Connection::maybeCloseOnGoingAway()
1600 if (m_connectionAborted || !m_goingAway) {
1601 qCDebug(qHttp2ConnectionLog,
"[%p] Connection close deferred, graceful shutdown not active",
1607 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing)
1610 const auto streamIsActive = [](
const QPointer<QHttp2Stream> &stream) {
1611 return stream && stream->isActive();
1614 if (std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
1615 qCDebug(qHttp2ConnectionLog,
"[%p] All streams closed, closing connection",
this);
1620bool QHttp2Connection::sendSETTINGS_ACK()
1622 frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
1623 return frameWriter.write(*getSocket());
1626void QHttp2Connection::handleDATA()
1628 Q_ASSERT(inboundFrame.type() == FrameType::DATA);
1630 const auto streamID = inboundFrame.streamID();
1634 if (streamID == connectionStreamID)
1635 return connectionError(PROTOCOL_ERROR, u"DATA on the connection stream"_s);
1637 if (isInvalidStream(streamID))
1638 return connectionError(ENHANCE_YOUR_CALM, u"DATA on invalid stream"_s);
1640 QHttp2Stream *stream =
nullptr;
1641 if (!streamWasResetLocally(streamID)) {
1642 stream = getStream(streamID);
1645 if (stream->state() == QHttp2Stream::State::HalfClosedRemote
1646 || stream->state() == QHttp2Stream::State::Closed) {
1647 return stream->streamError(Http2Error::STREAM_CLOSED, u"Data on closed stream"_s);
1651 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1652 qCDebug(qHttp2ConnectionLog,
1653 "[%p] Received DATA frame with payload size %u, "
1654 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1655 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1657 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1658 u"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1659 return connectionError(FRAME_SIZE_ERROR, u"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1662 if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1663 qCDebug(qHttp2ConnectionLog,
1664 "[%p] Received DATA frame with payload size %u, "
1665 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1666 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1667 return connectionError(FLOW_CONTROL_ERROR, u"Flow control error"_s);
1670 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1671 m_totalBytesReceivedDATA += inboundFrame.payloadSize();
1674 stream->handleDATA(inboundFrame);
1677 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
1678 const bool ignoreData = stream && streamIsIgnored(stream->streamID());
1680 emit receivedEND_STREAM(streamID);
1686 stream->setState(QHttp2Stream::State::Closed);
1691 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1693 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1694 quint32(connectionStreamID),
1695 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1696 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1700void QHttp2Connection::handleHEADERS()
1702 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1704 const auto streamID = inboundFrame.streamID();
1705 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1706 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1710 if (streamID == connectionStreamID)
1711 return connectionError(PROTOCOL_ERROR, u"HEADERS on 0x0 stream"_s);
1713 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1714 qCDebug(qHttp2ConnectionLog,
1715 "[%p] Received HEADERS frame with payload size %u, "
1716 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1717 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1718 return connectionError(Http2Error::FRAME_SIZE_ERROR,
1719 u"HEADERS payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1722 const bool isClient = m_connectionType == Type::Client;
1723 const bool isClientInitiatedStream = !!(streamID & 1);
1724 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1726 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1727 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1728 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1729 Q_ASSERT(newStream);
1730 m_lastIncomingStreamID = streamID;
1732 if (!streamCountIsOk) {
1733 newStream->setState(QHttp2Stream::State::Open);
1734 newStream->streamError(PROTOCOL_ERROR, u"Max concurrent streams reached"_s);
1736 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1740 qCDebug(qHttp2ConnectionLog,
"[%p] New incoming stream %d",
this, streamID);
1741 if (!streamIsIgnored(newStream->streamID())) {
1742 emit newIncomingStream(newStream);
1743 }
else if (m_goawayGraceTimer.hasExpired()) {
1746 connectionError(Http2Error::PROTOCOL_ERROR, u"Peer refused to GOAWAY."_s);
1749 }
else if (streamWasResetLocally(streamID)) {
1750 qCDebug(qHttp2ConnectionLog,
1751 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1754 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1757 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1759 return connectionError(PROTOCOL_ERROR, u"HEADERS on invalid stream"_s);
1760 }
else if (isInvalidStream(streamID)) {
1763 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1764 return connectionError(ENHANCE_YOUR_CALM, u"HEADERS on invalid stream"_s);
1767 const auto flags = inboundFrame.flags();
1768 if (flags.testFlag(FrameFlag::PRIORITY)) {
1769 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1774 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1775 continuedFrames.clear();
1776 m_headerBlockSize = 0;
1777 if (!validateHeaderListSize(inboundFrame))
1779 continuedFrames.push_back(std::move(inboundFrame));
1781 continuationExpected =
true;
1785 handleContinuedHEADERS();
1788void QHttp2Connection::handlePRIORITY()
1790 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1791 || inboundFrame.type() == FrameType::HEADERS);
1793 const auto streamID = inboundFrame.streamID();
1794 if (streamIsIgnored(streamID))
1799 if (streamID == connectionStreamID)
1800 return connectionError(PROTOCOL_ERROR, u"PRIORITY on 0x0 stream"_s);
1804 if (isInvalidStream(streamID))
1805 return connectionError(ENHANCE_YOUR_CALM, u"PRIORITY on invalid stream"_s);
1810 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1812 quint32 streamDependency = 0;
1814 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1818 const bool exclusive = streamDependency & 0x80000000;
1819 streamDependency &= ~0x80000000;
1823 Q_UNUSED(exclusive);
1827void QHttp2Connection::handleRST_STREAM()
1829 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1831 const auto streamID = inboundFrame.streamID();
1832 if (streamIsIgnored(streamID))
1839 if (streamID == connectionStreamID)
1840 return connectionError(PROTOCOL_ERROR, u"RST_STREAM on 0x0"_s);
1845 Q_ASSERT(inboundFrame.payloadSize() == 4);
1847 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1848 if (QPointer<QHttp2Stream> stream = m_streams.value(streamID))
1849 emit stream->rstFrameReceived(error);
1852 const quint32 lastRelevantStreamID = [
this, streamID]() {
1853 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1854 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1856 if (streamID > lastRelevantStreamID) {
1860 return connectionError(PROTOCOL_ERROR, u"RST_STREAM on idle stream"_s);
1863 Q_ASSERT(inboundFrame.dataSize() == 4);
1865 if (QPointer<QHttp2Stream> stream = m_streams.value(streamID))
1866 stream->handleRST_STREAM(inboundFrame);
1869void QHttp2Connection::handleSETTINGS()
1872 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1876 if (inboundFrame.streamID() != connectionStreamID)
1877 return connectionError(PROTOCOL_ERROR, u"SETTINGS on invalid stream"_s);
1879 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1882 if (inboundFrame.payloadSize())
1883 return connectionError(FRAME_SIZE_ERROR, u"SETTINGS ACK with data"_s);
1884 if (!waitingForSettingsACK)
1885 return connectionError(PROTOCOL_ERROR, u"unexpected SETTINGS ACK"_s);
1886 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1887 waitingForSettingsACK =
false;
1890 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1892 if (inboundFrame.dataSize()) {
1896 Q_ASSERT(inboundFrame.payloadSize() % 6 == 0);
1898 auto src = inboundFrame.dataBegin();
1899 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1900 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1901 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1902 if (!acceptSetting(identifier, intVal)) {
1904 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1905 quint32(identifier), intVal);
1911 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1913 emit settingsFrameReceived();
1916void QHttp2Connection::handlePUSH_PROMISE()
1919 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1924 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1927 return connectionError(PROTOCOL_ERROR, u"unexpected PUSH_PROMISE frame"_s);
1932 const auto streamID = inboundFrame.streamID();
1933 if (streamID == connectionStreamID)
1934 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with invalid associated stream (0x0)"_s);
1936 auto it = m_streams.constFind(streamID);
1938 if (it != m_streams.constEnd()) {
1939 QHttp2Stream *associatedStream = it->get();
1940 if (associatedStream->state() != QHttp2Stream::State::Open
1941 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1943 it = m_streams.constEnd();
1953 if (it == m_streams.constEnd() || it->isNull())
1954 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1955 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1956 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1957 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1959 if ((*it)->state() != QHttp2Stream::State::Open &&
1960 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1961 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1966 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1967 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1968 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with invalid promised stream ID"_s);
1970 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1973 auto *stream = createStreamInternal_impl(reservedID);
1975 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with already active stream ID"_s);
1976 m_lastIncomingStreamID = reservedID;
1977 stream->setState(QHttp2Stream::State::ReservedRemote);
1979 if (!streamCountIsOk) {
1980 stream->streamError(PROTOCOL_ERROR, u"Max concurrent streams reached"_s);
1981 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1987 if (!pushPromiseEnabled)
1988 return stream->streamError(REFUSE_STREAM, u"PUSH_PROMISE not enabled but ignored"_s);
1994 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1995 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1996 continuedFrames.clear();
1997 m_headerBlockSize = 0;
1998 if (!validateHeaderListSize(inboundFrame))
2000 continuedFrames.push_back(std::move(inboundFrame));
2003 continuationExpected =
true;
2007 handleContinuedHEADERS();
2010void QHttp2Connection::handlePING()
2012 Q_ASSERT(inboundFrame.type() == FrameType::PING);
2017 if (inboundFrame.streamID() != connectionStreamID)
2018 return connectionError(PROTOCOL_ERROR, u"PING on invalid stream"_s);
2023 Q_ASSERT(inboundFrame.payloadSize() == 8);
2025 if (inboundFrame.flags() & FrameFlag::ACK) {
2026 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
2027 if (!m_lastPingSignature.has_value()) {
2028 emit pingFrameReceived(PingState::PongNoPingSent);
2029 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
2030 }
else if (pingSignature != m_lastPingSignature) {
2031 emit pingFrameReceived(PingState::PongSignatureChanged);
2032 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
2034 emit pingFrameReceived(PingState::PongSignatureIdentical);
2036 m_lastPingSignature.reset();
2039 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing) {
2040 sendFinalServerGracefulShutdownGoaway();
2041 }
else if (m_gracefulShutdownState == GracefulShutdownState::AwaitingPriorPing) {
2043 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
2044 [[maybe_unused]]
const bool ok = sendPing();
2050 emit pingFrameReceived(PingState::Ping);
2055 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
2056 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
2057 frameWriter.write(*getSocket());
2060void QHttp2Connection::handleGOAWAY()
2064 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
2067 if (inboundFrame.streamID() != connectionStreamID)
2068 return connectionError(PROTOCOL_ERROR, u"GOAWAY on invalid stream"_s);
2073 Q_ASSERT(inboundFrame.payloadSize() >= 8);
2075 const uchar *
const src = inboundFrame.dataBegin();
2077 const quint32 lastStreamID = qFromBigEndian<quint32>(src) & lastValidStreamID;
2078 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
2084 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
2087 if (lastStreamID != 0 && (lastStreamID & 0x1) != LocalMask)
2088 return connectionError(PROTOCOL_ERROR, u"GOAWAY with invalid last stream ID"_s);
2093 if (m_lastGoAwayLastStreamID && lastStreamID > *m_lastGoAwayLastStreamID)
2094 return connectionError(PROTOCOL_ERROR, u"Repeated GOAWAY with invalid last stream ID"_s);
2095 m_lastGoAwayLastStreamID = lastStreamID;
2097 qCDebug(qHttp2ConnectionLog,
"[%p] Received GOAWAY frame, error code %u, last stream %u",
2098 this, errorCode, lastStreamID);
2101 emit receivedGOAWAY(errorCode, lastStreamID);
2103 if (errorCode == HTTP2_NO_ERROR) {
2107 const quint32 firstPossibleStream = m_connectionType == Type::Client ? 1 : 2;
2108 const quint32 firstCancelledStream = lastStreamID ? lastStreamID + 2 : firstPossibleStream;
2109 Q_ASSERT((firstCancelledStream & 0x1) == LocalMask);
2110 for (quint32 id = firstCancelledStream; id < m_nextStreamID; id += 2) {
2111 QHttp2Stream *stream = m_streams.value(id,
nullptr);
2112 if (stream && stream->isActive())
2113 stream->finishWithError(errorCode, u"Received GOAWAY"_s);
2115 maybeCloseOnGoingAway();
2121 m_connectionAborted =
true;
2122 const QList<quint32> streamIDs = m_streams.keys();
2123 for (quint32 streamID : streamIDs) {
2124 QHttp2Stream *stream = m_streams.value(streamID,
nullptr);
2125 if (stream && stream->isActive())
2126 stream->finishWithError(errorCode, u"Received GOAWAY"_s);
2132void QHttp2Connection::handleWINDOW_UPDATE()
2134 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
2136 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
2140 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
2141 const auto streamID = inboundFrame.streamID();
2142 if (streamIsIgnored(streamID))
2148 Q_ASSERT(inboundFrame.payloadSize() == 4);
2150 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
2152 if (streamID == connectionStreamID) {
2154 return connectionError(PROTOCOL_ERROR, u"WINDOW_UPDATE invalid delta"_s);
2158 if (qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
2159 return connectionError(FLOW_CONTROL_ERROR, u"WINDOW_UPDATE exceeds maximum window"_s);
2160 sessionSendWindowSize = sum;
2163 const auto blockedStreams = std::exchange(m_blockedStreams, {});
2164 for (quint32 blockedStreamID : blockedStreams) {
2165 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
2166 if (!stream || !stream->isActive() || !stream->isUploadingDATA())
2168 if (stream->isUploadBlocked()) {
2169 m_blockedStreams.insert(blockedStreamID);
2173 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2174 Qt::QueuedConnection);
2178 QHttp2Stream *stream = m_streams.value(streamID);
2179 if (!stream || !stream->isActive()) {
2181 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
2186 return stream->streamError(PROTOCOL_ERROR, u"WINDOW_UPDATE invalid delta"_s);
2188 stream->handleWINDOW_UPDATE(inboundFrame);
2192void QHttp2Connection::handleCONTINUATION()
2194 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
2195 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
2196 qCDebug(qHttp2ConnectionLog,
2197 "[%p] Received CONTINUATION frame with payload size %u, "
2198 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
2199 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
2200 return connectionError(Http2Error::FRAME_SIZE_ERROR,
2201 u"CONTINUATION payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
2203 auto streamID = inboundFrame.streamID();
2204 qCDebug(qHttp2ConnectionLog,
2205 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
2206 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
2207 if (continuedFrames.empty())
2208 return connectionError(PROTOCOL_ERROR,
2209 u"CONTINUATION without a preceding HEADERS or PUSH_PROMISE"_s);
2210 if (!continuationExpected)
2211 return connectionError(PROTOCOL_ERROR,
2212 u"CONTINUATION after a frame with the END_HEADERS flag set"_s);
2214 if (inboundFrame.streamID() != continuedFrames.front().streamID())
2215 return connectionError(PROTOCOL_ERROR, u"CONTINUATION on invalid stream"_s);
2217 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
2220 if (!validateHeaderListSize(inboundFrame))
2222 continuedFrames.push_back(std::move(inboundFrame));
2227 continuationExpected =
false;
2228 handleContinuedHEADERS();
2231bool QHttp2Connection::validateHeaderListSize(
const Frame &frame)
2233 const quint32 limit =
2234 QHttp2ConfigurationPrivate::get(std::as_const(m_config))->maxHeaderListSize;
2235 if (limit == std::numeric_limits<quint32>::max())
2241 m_headerBlockSize += frame.hpackBlockSize();
2242 if (m_headerBlockSize > limit) {
2243 connectionError(ENHANCE_YOUR_CALM, u"Header list size limit exceeded"_s);
2249void QHttp2Connection::handleContinuedHEADERS()
2254 Q_ASSERT(!continuedFrames.empty());
2255 const auto firstFrameType = continuedFrames[0].type();
2256 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
2258 const auto streamID = continuedFrames[0].streamID();
2260 const auto streamIt = m_streams.constFind(streamID);
2261 if (firstFrameType == FrameType::HEADERS) {
2262 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
2263 QHttp2Stream *stream = streamIt.value();
2264 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
2265 && stream->state() != QHttp2Stream::State::ReservedRemote
2266 && stream->state() != QHttp2Stream::State::Idle
2267 && stream->state() != QHttp2Stream::State::Open) {
2271 return stream->streamError(PROTOCOL_ERROR, u"HEADERS on invalid stream"_s);
2279 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
2280 const bool hasHeaderFields = !hpackBlock.empty();
2281 if (hasHeaderFields) {
2282 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
2283 if (!decoder.decodeHeaderFields(inputStream))
2284 return connectionError(COMPRESSION_ERROR, u"HPACK decompression failed"_s);
2286 if (firstFrameType == FrameType::PUSH_PROMISE) {
2295 if (streamIt != m_streams.cend())
2296 (*streamIt)->streamError(PROTOCOL_ERROR, u"PUSH_PROMISE with incomplete headers"_s);
2301 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
2302 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
2303 hpackBlockHasContent);
2305 return connectionError(FRAME_SIZE_ERROR, u"HEADERS frame too large"_s);
2308 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
2310 if (streamIsIgnored(streamID)) {
2314 if (continuedFrames[0].flags().testFlag(Http2::FrameFlag::END_STREAM)) {
2315 if (QHttp2Stream *stream = streamIt.value()) {
2316 stream->setState(QHttp2Stream::State::Closed);
2323 switch (firstFrameType) {
2324 case FrameType::HEADERS:
2325 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2327 case FrameType::PUSH_PROMISE: {
2328 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
2331 if (m_promisedStreams.contains(*promiseKey))
2333 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
2334 QHttp2Stream *stream = m_streams.value(promiseID);
2335 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
2336 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2337 emit newPromisedStream(stream);
2338 m_promisedStreams.emplace(*promiseKey, promiseID);
2346bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
2348 switch (identifier) {
2349 case Settings::HEADER_TABLE_SIZE_ID: {
2350 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
2351 if (newValue > maxAcceptableTableSize) {
2352 connectionError(PROTOCOL_ERROR, u"SETTINGS invalid table size"_s);
2355 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
2356 qCDebug(qHttp2ConnectionLog,
2357 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
2362 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
2363 pendingTableSizeUpdates[0] = newValue;
2364 pendingTableSizeUpdates[1].reset();
2365 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
2367 pendingTableSizeUpdates[1] = newValue;
2368 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
2369 this, newValue, *pendingTableSizeUpdates[0]);
2373 case Settings::INITIAL_WINDOW_SIZE_ID: {
2374 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
2378 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
2379 connectionError(FLOW_CONTROL_ERROR, u"SETTINGS invalid initial window size"_s);
2383 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2384 streamInitialSendWindowSize = qint32(newValue);
2386 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2387 this, size_t(m_streams.size()), delta);
2388 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2394 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2395 connectionError(FLOW_CONTROL_ERROR,
2396 u"SETTINGS_INITIAL_WINDOW_SIZE overflowed a flow-control window"_s);
2399 stream->m_sendWindow = sum;
2400 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2401 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2402 Qt::QueuedConnection);
2407 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2408 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2410 m_peerMaxConcurrentStreams = newValue;
2413 case Settings::MAX_FRAME_SIZE_ID: {
2414 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2415 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2416 connectionError(PROTOCOL_ERROR, u"SETTINGS max frame size is out of range"_s);
2419 maxFrameSize = newValue;
2422 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2423 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2428 m_maxHeaderListSize = newValue;
2431 case Http2::Settings::ENABLE_PUSH_ID:
2432 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2433 if (newValue != 0 && newValue != 1) {
2434 connectionError(PROTOCOL_ERROR, u"SETTINGS peer sent illegal value for ENABLE_PUSH"_s);
2437 if (m_connectionType == Type::Client) {
2438 if (newValue == 1) {
2439 connectionError(PROTOCOL_ERROR, u"SETTINGS server sent ENABLE_PUSH=1"_s);
2443 pushPromiseEnabled = newValue;
2453#include "moc_qhttp2connection_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)