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);
290void QHttp2Stream::streamError(Http2::Http2Error errorCode,
291 QLatin1StringView message)
293 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
294 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
296 sendRST_STREAM(errorCode);
297 emit errorOccurred(errorCode, message);
301
302
303
304
305
306
307bool QHttp2Stream::sendRST_STREAM(Http2::Http2Error errorCode)
309 if (m_state == State::Closed || m_state == State::Idle) {
310 qCDebug(qHttp2ConnectionLog,
"[%p] could not send RST_STREAM on %s stream %u",
311 getConnection(), QDebug::toBytes(m_state).constData(), m_streamID);
315 if (m_RST_STREAM_received.has_value())
318 getConnection()->registerStreamAsResetLocally(streamID());
320 m_RST_STREAM_sent = errorCode;
321 qCDebug(qHttp2ConnectionLog,
"[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
322 m_streamID, errorCode);
323 transitionState(StateTransition::RST);
325 QHttp2Connection *connection = getConnection();
326 FrameWriter &frameWriter = connection->frameWriter;
327 frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
328 frameWriter.append(quint32(errorCode));
329 return frameWriter.write(*connection->getSocket());
333
334
335
336
337
338
339
340
341
342
343
344bool QHttp2Stream::sendDATA(
const QByteArray &payload,
bool endStream)
346 Q_ASSERT(!m_uploadByteDevice);
347 if (m_state != State::Open && m_state != State::HalfClosedRemote)
350 auto *byteDevice = QNonContiguousByteDeviceFactory::create(payload);
351 m_owningByteDevice =
true;
352 byteDevice->setParent(
this);
353 return sendDATA(byteDevice, endStream);
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371bool QHttp2Stream::sendDATA(QIODevice *device,
bool endStream)
373 Q_ASSERT(!m_uploadDevice);
374 Q_ASSERT(!m_uploadByteDevice);
376 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
377 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
379 getConnection(), m_streamID, device);
383 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
384 getConnection(), m_streamID, device);
385 auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
386 m_owningByteDevice =
true;
387 byteDevice->setParent(
this);
388 m_uploadDevice = device;
389 return sendDATA(byteDevice, endStream);
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407bool QHttp2Stream::sendDATA(QNonContiguousByteDevice *device,
bool endStream)
409 Q_ASSERT(!m_uploadByteDevice);
411 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
412 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
414 getConnection(), m_streamID, device);
418 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
419 getConnection(), m_streamID, device);
420 m_uploadByteDevice = device;
421 m_endStreamAfterDATA = endStream;
422 connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead,
this,
423 &QHttp2Stream::maybeResumeUpload);
424 connect(m_uploadByteDevice, &QObject::destroyed,
this, &QHttp2Stream::uploadDeviceDestroyed);
432void QHttp2Stream::internalSendDATA()
434 Q_ASSERT(m_uploadByteDevice);
435 QHttp2Connection *connection = getConnection();
436 Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
437 QIODevice *socket = connection->getSocket();
439 qCDebug(qHttp2ConnectionLog,
440 "[%p] stream %u, about to write to socket, current session window size: %d, stream "
441 "window size: %d, bytes available: %lld",
442 connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
443 m_uploadByteDevice->size() - m_uploadByteDevice->pos());
445 qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
446 FrameWriter &frameWriter = connection->frameWriter;
447 qint64 totalBytesWritten = 0;
448 const auto deviceCanRead = [
this, connection] {
453 const qint64 requestSize = connection->maxFrameSize * 10ll;
455 return m_uploadByteDevice->readPointer(requestSize, tmp) !=
nullptr && tmp > 0;
458 bool sentEND_STREAM =
false;
459 while (remainingWindowSize && deviceCanRead()) {
460 quint32 bytesWritten = 0;
461 qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
462 frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
464 while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
465 const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
467 qint64 outBytesAvail = 0;
468 const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
469 if (!readPointer || outBytesAvail <= 0) {
470 qCDebug(qHttp2ConnectionLog,
471 "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
472 connection, m_streamID, m_uploadByteDevice, outBytesAvail);
475 const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
476 frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
477 m_uploadByteDevice->advanceReadPointer(bytesToWrite);
479 bytesWritten += bytesToWrite;
481 m_sendWindow -= bytesToWrite;
482 Q_ASSERT(m_sendWindow >= 0);
483 connection->sessionSendWindowSize -= bytesToWrite;
484 Q_ASSERT(connection->sessionSendWindowSize >= 0);
485 remainingBytesInFrame -= bytesToWrite;
486 Q_ASSERT(remainingBytesInFrame >= 0);
487 remainingWindowSize -= bytesToWrite;
488 Q_ASSERT(remainingWindowSize >= 0);
491 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, writing %u bytes to socket", connection,
492 m_streamID, bytesWritten);
493 if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
494 sentEND_STREAM =
true;
495 frameWriter.addFlag(FrameFlag::END_STREAM);
497 if (!frameWriter.write(*socket)) {
498 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, failed to write to socket", connection,
500 return finishWithError(INTERNAL_ERROR,
"failed to write to socket"_L1);
503 totalBytesWritten += bytesWritten;
506 qCDebug(qHttp2ConnectionLog,
507 "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
508 "more later. Remaining window size: %d",
509 connection, m_streamID, totalBytesWritten, remainingWindowSize);
511 emit bytesWritten(totalBytesWritten);
512 if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
513 qCDebug(qHttp2ConnectionLog,
514 "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
516 connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
517 !sentEND_STREAM && m_endStreamAfterDATA ?
"" :
"not ");
518 if (!sentEND_STREAM && m_endStreamAfterDATA) {
523 frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
524 frameWriter.write(*socket);
527 }
else if (isUploadBlocked()) {
528 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, upload blocked", connection, m_streamID);
529 emit uploadBlocked();
533void QHttp2Stream::finishSendDATA()
535 if (m_endStreamAfterDATA)
536 transitionState(StateTransition::CloseLocal);
538 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
539 m_uploadDevice =
nullptr;
540 if (m_owningByteDevice) {
541 m_owningByteDevice =
false;
542 delete m_uploadByteDevice;
544 m_uploadByteDevice =
nullptr;
545 emit uploadFinished();
548void QHttp2Stream::maybeResumeUpload()
550 qCDebug(qHttp2ConnectionLog,
551 "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
553 getConnection(), m_streamID, m_uploadByteDevice,
554 !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
556 if (isUploadingDATA() && !isUploadBlocked())
559 getConnection()->m_blockedStreams.insert(streamID());
563
564
565
566bool QHttp2Stream::isUploadBlocked()
const noexcept
568 constexpr auto MinFrameSize = Http2::frameHeaderSize + 1;
569 return isUploadingDATA()
570 && (m_sendWindow <= MinFrameSize
571 || getConnection()->sessionSendWindowSize <= MinFrameSize);
574void QHttp2Stream::uploadDeviceReadChannelFinished()
580
581
582
583
584
585
586bool QHttp2Stream::sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream, quint8 priority)
588 using namespace HPack;
589 if (
auto hs = header_size(headers);
590 !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
594 transitionState(StateTransition::Open);
596 Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
598 QHttp2Connection *connection = getConnection();
600 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending HEADERS frame with %u entries",
601 connection, streamID(), uint(headers.size()));
603 QIODevice *socket = connection->getSocket();
604 FrameWriter &frameWriter = connection->frameWriter;
606 frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
608 frameWriter.addFlag(FrameFlag::END_STREAM);
610 frameWriter.append(quint32());
611 frameWriter.append(priority);
614 BitOStream outputStream(frameWriter.outboundFrame().buffer);
617 for (
auto &maybePendingTableSizeUpdate : connection->pendingTableSizeUpdates) {
618 if (!maybePendingTableSizeUpdate)
620 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending dynamic table size update of size %u",
621 connection, streamID(), *maybePendingTableSizeUpdate);
622 connection->encoder.setMaxDynamicTableSize(*maybePendingTableSizeUpdate);
623 connection->encoder.encodeSizeUpdate(outputStream, *maybePendingTableSizeUpdate);
624 maybePendingTableSizeUpdate.reset();
627 if (connection->m_connectionType == QHttp2Connection::Type::Client) {
628 if (!connection->encoder.encodeRequest(outputStream, headers))
631 if (!connection->encoder.encodeResponse(outputStream, headers))
635 bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
637 transitionState(StateTransition::CloseLocal);
643
644
645
646
647void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
649 QHttp2Connection *connection = getConnection();
650 m_recvWindow += qint32(delta);
651 connection->sendWINDOW_UPDATE(streamID(), delta);
654void QHttp2Stream::uploadDeviceDestroyed()
656 if (isUploadingDATA()) {
659 streamError(CANCEL, QLatin1String(
"Upload device destroyed while uploading"));
660 emit uploadDeviceError(
"Upload device destroyed while uploading"_L1);
662 m_uploadDevice =
nullptr;
665void QHttp2Stream::setState(State newState)
667 if (m_state == newState)
669 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, state changed from %d to %d", getConnection(),
670 streamID(),
int(m_state),
int(newState));
672 emit stateChanged(newState);
673 if (m_state == State::Closed)
674 getConnection()->maybeCloseOnGoingAway();
680void QHttp2Stream::transitionState(StateTransition transition)
684 if (transition == StateTransition::Open)
685 setState(State::Open);
690 switch (transition) {
691 case StateTransition::CloseLocal:
692 setState(State::HalfClosedLocal);
694 case StateTransition::CloseRemote:
695 setState(State::HalfClosedRemote);
697 case StateTransition::RST:
698 setState(State::Closed);
700 case StateTransition::Open:
704 case State::HalfClosedLocal:
705 if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
706 setState(State::Closed);
708 case State::HalfClosedRemote:
709 if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
710 setState(State::Closed);
712 case State::ReservedRemote:
713 if (transition == StateTransition::RST) {
714 setState(State::Closed);
715 }
else if (transition == StateTransition::CloseLocal) {
716 setState(State::HalfClosedLocal);
724void QHttp2Stream::handleDATA(
const Frame &inboundFrame)
726 QHttp2Connection *connection = getConnection();
728 qCDebug(qHttp2ConnectionLog,
729 "[%p] stream %u, received DATA frame with payload of %u bytes, closing stream? %s",
730 connection, m_streamID, inboundFrame.payloadSize(),
731 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
737 Q_ASSERT(state() != State::HalfClosedRemote && state() != State::Closed);
739 if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
740 qCDebug(qHttp2ConnectionLog,
741 "[%p] stream %u, received DATA frame with payload size %u, "
742 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
743 connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
744 return streamError(FLOW_CONTROL_ERROR, QLatin1String(
"data bigger than window size"));
750 Q_ASSERT(inboundFrame.buffer.size() >= frameHeaderSize);
751 Q_ASSERT(inboundFrame.payloadSize() + frameHeaderSize == inboundFrame.buffer.size());
753 m_recvWindow -= qint32(inboundFrame.payloadSize());
754 const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
755 const bool ignoreData = connection->streamIsIgnored(m_streamID);
757 if ((inboundFrame.dataSize() > 0 || endStream) && !ignoreData) {
758 QByteArray fragment(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()),
759 inboundFrame.dataSize());
761 transitionState(StateTransition::CloseRemote);
762 const auto shouldBuffer = m_configuration.useDownloadBuffer && !fragment.isEmpty();
765 m_downloadBuffer.append(std::move(fragment));
766 emit dataReceived(m_downloadBuffer.last(), endStream);
768 emit dataReceived(fragment, endStream);
772 if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
774 sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
778void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers)
780 if (m_state == State::Idle)
781 transitionState(StateTransition::Open);
782 const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
784 transitionState(StateTransition::CloseRemote);
785 if (!headers.empty() && m_configuration.useHeaderBuffer) {
786 m_headers.insert(m_headers.end(), headers.begin(), headers.end());
787 emit headersUpdated();
789 emit headersReceived(headers, endStream);
792void QHttp2Stream::handleRST_STREAM(
const Frame &inboundFrame)
794 if (m_state == State::Closed)
797 transitionState(StateTransition::RST);
798 m_RST_STREAM_received = qFromBigEndian<quint32>(inboundFrame.dataBegin());
799 if (isUploadingDATA()) {
800 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
801 m_uploadDevice =
nullptr;
802 m_uploadByteDevice =
nullptr;
804 finishWithError(Http2Error(*m_RST_STREAM_received));
807void QHttp2Stream::handleWINDOW_UPDATE(
const Frame &inboundFrame)
809 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
810 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
814 qCDebug(qHttp2ConnectionLog,
815 "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
817 getConnection(), m_streamID, delta);
818 return streamError(PROTOCOL_ERROR,
"invalid WINDOW_UPDATE delta"_L1);
821 if (qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
824 qCDebug(qHttp2ConnectionLog,
825 "[%p] stream %u, WINDOW_UPDATE delta %u overflows the flow-control window, "
826 "sending FLOW_CONTROL_ERROR",
827 getConnection(), m_streamID, delta);
828 return streamError(FLOW_CONTROL_ERROR,
"WINDOW_UPDATE exceeds maximum window"_L1);
832 if (isUploadingDATA())
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
855
856
857
858
859
862
863
864
865
868
869
870
871
874
875
876
877
880
881
882
883
886
887
888
889
890
891
894
895
896
897
898
899
900
901
904
905
906
907
908
909
910QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
911 const QHttp2Configuration &config)
915 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
916 connection->setH2Configuration(config);
917 connection->m_connectionType = QHttp2Connection::Type::Client;
918 connection->m_upgradedConnection =
true;
921 QHttp2Stream *stream = connection->createLocalStreamInternal().unwrap();
922 Q_ASSERT(stream->streamID() == 1);
923 stream->setState(QHttp2Stream::State::HalfClosedLocal);
925 if (!connection->m_prefaceSent)
928 return connection.release();
932
933
934
935
936
937QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
938 const QHttp2Configuration &config)
940 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
941 connection->setH2Configuration(config);
942 connection->m_connectionType = QHttp2Connection::Type::Client;
944 return connection.release();
948
949
950
951
952QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
953 const QHttp2Configuration &config)
955 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
956 connection->setH2Configuration(config);
957 connection->m_connectionType = QHttp2Connection::Type::Server;
959 connection->m_nextStreamID = 2;
961 connection->m_waitingForClientPreface =
true;
963 return connection.release();
967
968
969
970
971
972
973
974
975
976
977
978
981
982
983
984
985QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
986QHttp2Connection::createStream(QHttp2Stream::Configuration configuration)
988 Q_ASSERT(m_connectionType == Type::Client);
989 if (m_nextStreamID > lastValidStreamID)
990 return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
991 return createLocalStreamInternal(configuration);
994QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
995QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf)
998 return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
999 const quint32 streamID = m_nextStreamID;
1000 if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams()))
1001 return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
1003 if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) {
1004 m_nextStreamID += 2;
1008 return { QHttp2Connection::CreateStreamError::UnknownError };
1011QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID,
1012 QHttp2Stream::Configuration conf)
1014 Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID);
1016 if (m_connectionType == Type::Client && !m_prefaceSent && !sendClientPreface()) {
1017 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send client preface",
this);
1021 auto result = m_streams.tryEmplace(streamID,
nullptr);
1022 if (!result.inserted)
1024 QPointer<QHttp2Stream> &stream = result.iterator.value();
1025 stream =
new QHttp2Stream(
this, streamID, conf);
1026 stream->m_recvWindow = streamInitialReceiveWindowSize;
1027 stream->m_sendWindow = streamInitialSendWindowSize;
1029 connect(stream, &QHttp2Stream::uploadBlocked,
this, [
this, stream] {
1030 m_blockedStreams.insert(stream->streamID());
1032 *result.iterator = stream;
1033 return *result.iterator;
1036qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask)
const noexcept
1038 const auto shouldCount = [mask](
const QPointer<QHttp2Stream> &stream) ->
bool {
1039 return stream && (stream->streamID() & 1) == mask && stream->isActive();
1041 return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
1045
1046
1047
1048qsizetype QHttp2Connection::numActiveRemoteStreams()
const noexcept
1050 const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
1051 return numActiveStreamsImpl(RemoteMask);
1055
1056
1057
1058qsizetype QHttp2Connection::numActiveLocalStreams()
const noexcept
1060 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
1061 return numActiveStreamsImpl(LocalMask);
1065
1066
1067
1068QHttp2Stream *QHttp2Connection::getStream(quint32 streamID)
const
1070 return m_streams.value(streamID,
nullptr).get();
1074
1075
1076
1077
1078void QHttp2Connection::close(Http2::Http2Error errorCode)
1080 if (m_connectionAborted)
1083 if (errorCode == Http2::HTTP2_NO_ERROR) {
1084 if (m_connectionType == Type::Server)
1085 sendInitialServerGracefulShutdownGoaway();
1087 sendClientGracefulShutdownGoaway();
1091 connectionError(errorCode,
"Connection closed with error",
false);
1096
1097
1098
1099
1100
1103
1104
1105
1106
1107
1110
1111
1112
1113
1114
1115
1118
1119
1120
1121
1124
1125
1126
1127
1128
1130QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
1133 Q_ASSERT(socket->isOpen());
1134 Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
1141QHttp2Connection::~QHttp2Connection()
1145 for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
1146 delete stream.get();
1149bool QHttp2Connection::serverCheckClientPreface()
1151 if (!m_waitingForClientPreface)
1153 auto *socket = getSocket();
1154 if (socket->bytesAvailable() < Http2::clientPrefaceLength)
1156 if (!readClientPreface()) {
1158 emit errorOccurred(Http2Error::PROTOCOL_ERROR,
"invalid client preface"_L1);
1159 qCDebug(qHttp2ConnectionLog,
"[%p] Invalid client preface",
this);
1162 qCDebug(qHttp2ConnectionLog,
"[%p] Peer sent valid client preface",
this);
1163 m_waitingForClientPreface =
false;
1164 if (!sendServerPreface()) {
1165 connectionError(INTERNAL_ERROR,
"Failed to send server preface");
1171bool QHttp2Connection::sendPing()
1173 std::array<
char, 8> data;
1175 QRandomGenerator gen;
1176 gen.generate(data.begin(), data.end());
1177 return sendPing(data);
1180bool QHttp2Connection::sendPing(QByteArrayView data)
1182 frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
1184 Q_ASSERT(data.length() == 8);
1185 if (!m_lastPingSignature) {
1186 m_lastPingSignature = data.toByteArray();
1188 qCWarning(qHttp2ConnectionLog,
"[%p] No PING is sent while waiting for the previous PING.",
this);
1192 frameWriter.append((uchar*)data.data(), (uchar*)data.end());
1193 frameWriter.write(*getSocket());
1198
1199
1200
1201
1202void QHttp2Connection::handleReadyRead()
1205 if (m_connectionType == Type::Server && !serverCheckClientPreface())
1208 QIODevice *socket = getSocket();
1210 qCDebug(qHttp2ConnectionLog,
"[%p] Receiving data, %lld bytes available",
this,
1211 socket->bytesAvailable());
1213 using namespace Http2;
1217 while (!m_connectionAborted) {
1218 const auto result = frameReader.read(*socket);
1219 if (result != FrameStatus::goodFrame)
1220 qCDebug(qHttp2ConnectionLog,
"[%p] Tried to read frame, got %d",
this,
int(result));
1222 case FrameStatus::incompleteFrame:
1224 case FrameStatus::protocolError:
1225 return connectionError(PROTOCOL_ERROR,
"invalid frame");
1226 case FrameStatus::sizeError: {
1227 const auto streamID = frameReader.inboundFrame().streamID();
1228 const auto frameType = frameReader.inboundFrame().type();
1229 auto stream = getStream(streamID);
1235 if (frameType == FrameType::HEADERS ||
1236 frameType == FrameType::SETTINGS ||
1237 frameType == FrameType::PUSH_PROMISE ||
1238 frameType == FrameType::CONTINUATION ||
1240 frameType == FrameType::RST_STREAM ||
1241 streamID == connectionStreamID)
1242 return connectionError(FRAME_SIZE_ERROR,
"invalid frame size");
1245 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1246 QLatin1String(
"invalid frame size"));
1254 Q_ASSERT(result == FrameStatus::goodFrame);
1256 inboundFrame = std::move(frameReader.inboundFrame());
1258 const auto frameType = inboundFrame.type();
1259 qCDebug(qHttp2ConnectionLog,
"[%p] Successfully read a frame, with type: %d",
this,
1266 if (continuationExpected && frameType != FrameType::CONTINUATION)
1267 return connectionError(PROTOCOL_ERROR,
"CONTINUATION expected");
1269 switch (frameType) {
1270 case FrameType::DATA:
1273 case FrameType::HEADERS:
1276 case FrameType::PRIORITY:
1279 case FrameType::RST_STREAM:
1282 case FrameType::SETTINGS:
1285 case FrameType::PUSH_PROMISE:
1286 handlePUSH_PROMISE();
1288 case FrameType::PING:
1291 case FrameType::GOAWAY:
1294 case FrameType::WINDOW_UPDATE:
1295 handleWINDOW_UPDATE();
1297 case FrameType::CONTINUATION:
1298 handleCONTINUATION();
1300 case FrameType::LAST_FRAME_TYPE:
1307bool QHttp2Connection::readClientPreface()
1309 auto *socket = getSocket();
1310 Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
1311 char buffer[Http2::clientPrefaceLength];
1312 const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
1313 if (read != Http2::clientPrefaceLength)
1315 return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
1319
1320
1321
1322void QHttp2Connection::handleConnectionClosure()
1324 const auto errorString = QCoreApplication::translate(
"QHttp",
"Connection closed");
1325 for (
auto it = m_streams.cbegin(), end = m_streams.cend(); it != end; ++it) {
1326 const QPointer<QHttp2Stream> &stream = it.value();
1327 if (stream && stream->isActive())
1328 stream->finishWithError(PROTOCOL_ERROR, errorString);
1332void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
1334 m_config = std::move(config);
1337 maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
1338 pushPromiseEnabled = m_config.serverPushEnabled();
1339 streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
1340 m_maxConcurrentStreams = m_config.maxConcurrentStreams();
1341 encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
1342 decoder.setMaxHeaderListSize(
1343 QHttp2ConfigurationPrivate::get(std::as_const(m_config))->maxHeaderListSize);
1346void QHttp2Connection::connectionError(Http2Error errorCode,
const char *message,
bool logAsError)
1349 if (m_connectionAborted)
1351 m_connectionAborted =
true;
1354 qCCritical(qHttp2ConnectionLog,
"[%p] Connection error: %s (%d)",
this, message,
1357 qCDebug(qHttp2ConnectionLog,
"[%p] Closing connection: %s (%d)",
this, message,
1365 m_lastStreamToProcess = std::min(m_lastIncomingStreamID, m_lastStreamToProcess);
1366 sendGOAWAYFrame(errorCode, m_lastStreamToProcess);
1367 auto messageView = QLatin1StringView(message);
1369 for (QHttp2Stream *stream : std::as_const(m_streams)) {
1370 if (stream && stream->isActive())
1371 stream->finishWithError(errorCode, messageView);
1378void QHttp2Connection::closeSession()
1380 emit connectionClosed();
1383bool QHttp2Connection::streamWasResetLocally(quint32 streamID)
noexcept
1385 return m_resetStreamIDs.contains(streamID);
1388void QHttp2Connection::registerStreamAsResetLocally(quint32 streamID)
1396 m_resetStreamIDs.append(streamID);
1397 while (m_resetStreamIDs.size() > 100)
1398 m_resetStreamIDs.takeFirst();
1401bool QHttp2Connection::isInvalidStream(quint32 streamID)
noexcept
1403 auto stream = m_streams.value(streamID,
nullptr);
1404 return (!stream || stream->wasResetbyPeer()) && !streamWasResetLocally(streamID);
1408
1409
1410
1411
1412
1413
1414
1415bool QHttp2Connection::streamIsIgnored(quint32 streamID)
const noexcept
1417 const bool streamIsRemote = (streamID & 1) == (m_connectionType == Type::Client ? 0 : 1);
1418 return Q_UNLIKELY(streamIsRemote && m_lastStreamToProcess < streamID);
1421bool QHttp2Connection::sendClientPreface()
1423 QIODevice *socket = getSocket();
1425 const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
1426 if (written != clientPrefaceLength)
1429 if (!sendSETTINGS()) {
1430 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1433 m_prefaceSent =
true;
1434 if (socket->bytesAvailable())
1435 QMetaObject::invokeMethod(
this, &QHttp2Connection::handleReadyRead, Qt::QueuedConnection);
1439bool QHttp2Connection::sendServerPreface()
1443 if (!sendSETTINGS()) {
1444 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1447 m_prefaceSent =
true;
1451bool QHttp2Connection::sendSETTINGS()
1453 QIODevice *socket = getSocket();
1455 frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
1456 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS frame, %d bytes",
this,
1457 frameWriter.outboundFrame().payloadSize());
1458 Q_ASSERT(frameWriter.outboundFrame().payloadSize());
1460 if (!frameWriter.write(*socket))
1463 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1466 const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
1467 if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
1470 waitingForSettingsACK =
true;
1474bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
1476 qCDebug(qHttp2ConnectionLog,
"[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u",
this,
1478 frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
1479 frameWriter.append(delta);
1480 return frameWriter.write(*getSocket());
1484
1485
1486
1487
1488
1489
1492
1493
1494
1495
1496
1497
1498
1499
1500bool QHttp2Connection::setSessionReceiveWindowSize(qint32 size)
1503 qCWarning(qHttp2ConnectionLog,
"[%p] Invalid session receive window size: %d",
this, size);
1506 if (size <= maxSessionReceiveWindowSize) {
1508 maxSessionReceiveWindowSize = size;
1511 const qint32 delta = size - maxSessionReceiveWindowSize;
1512 maxSessionReceiveWindowSize = size;
1513 sessionReceiveWindowSize += delta;
1514 return sendWINDOW_UPDATE(connectionStreamID, quint32(delta));
1517void QHttp2Connection::sendClientGracefulShutdownGoaway()
1520 Q_ASSERT(m_connectionType == Type::Client);
1522 if (m_connectionAborted || m_goingAway) {
1523 qCWarning(qHttp2ConnectionLog,
"[%p] Client graceful shutdown already in progress",
this);
1528 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1529 m_lastStreamToProcess = m_lastIncomingStreamID;
1530 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1532 maybeCloseOnGoingAway();
1535void QHttp2Connection::sendInitialServerGracefulShutdownGoaway()
1537 Q_ASSERT(m_connectionType == Type::Server);
1541 if (m_connectionAborted || m_goingAway) {
1542 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown already in progress",
this);
1547 m_goawayGraceTimer.setRemainingTime(GoawayGracePeriod);
1548 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, Http2::lastValidStreamID);
1554 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
1556 m_gracefulShutdownState = GracefulShutdownState::AwaitingPriorPing;
1559void QHttp2Connection::sendFinalServerGracefulShutdownGoaway()
1561 if (m_connectionAborted || !m_goingAway) {
1562 qCWarning(qHttp2ConnectionLog,
"[%p] Server graceful shutdown not in progress",
this);
1565 m_gracefulShutdownState = GracefulShutdownState::FinalGOAWAYSent;
1566 m_lastStreamToProcess = m_lastIncomingStreamID;
1567 sendGOAWAYFrame(Http2::HTTP2_NO_ERROR, m_lastStreamToProcess);
1568 maybeCloseOnGoingAway();
1571bool QHttp2Connection::sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastStreamID)
1573 QIODevice *socket = getSocket();
1574 if (!socket || !socket->isOpen())
1577 qCDebug(qHttp2ConnectionLog,
"[%p] Sending GOAWAY frame, error code %u, last stream %u",
this,
1578 errorCode, lastStreamID);
1580 frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
1581 Http2PredefinedParameters::connectionStreamID);
1582 frameWriter.append(lastStreamID);
1583 frameWriter.append(quint32(errorCode));
1584 return frameWriter.write(*socket);
1587void QHttp2Connection::maybeCloseOnGoingAway()
1592 if (m_connectionAborted || !m_goingAway) {
1593 qCDebug(qHttp2ConnectionLog,
"[%p] Connection close deferred, graceful shutdown not active",
1599 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing)
1602 const auto streamIsActive = [](
const QPointer<QHttp2Stream> &stream) {
1603 return stream && stream->isActive();
1606 if (std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
1607 qCDebug(qHttp2ConnectionLog,
"[%p] All streams closed, closing connection",
this);
1612bool QHttp2Connection::sendSETTINGS_ACK()
1614 frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
1615 return frameWriter.write(*getSocket());
1618void QHttp2Connection::handleDATA()
1620 Q_ASSERT(inboundFrame.type() == FrameType::DATA);
1622 const auto streamID = inboundFrame.streamID();
1626 if (streamID == connectionStreamID)
1627 return connectionError(PROTOCOL_ERROR,
"DATA on the connection stream");
1629 if (isInvalidStream(streamID))
1630 return connectionError(ENHANCE_YOUR_CALM,
"DATA on invalid stream");
1632 QHttp2Stream *stream =
nullptr;
1633 if (!streamWasResetLocally(streamID)) {
1634 stream = getStream(streamID);
1637 if (stream->state() == QHttp2Stream::State::HalfClosedRemote
1638 || stream->state() == QHttp2Stream::State::Closed) {
1639 return stream->streamError(Http2Error::STREAM_CLOSED,
1640 QLatin1String(
"Data on closed stream"));
1644 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1645 qCDebug(qHttp2ConnectionLog,
1646 "[%p] Received DATA frame with payload size %u, "
1647 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1648 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1650 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1651 QLatin1String(
"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"));
1652 return connectionError(FRAME_SIZE_ERROR,
"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE");
1655 if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1656 qCDebug(qHttp2ConnectionLog,
1657 "[%p] Received DATA frame with payload size %u, "
1658 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1659 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1660 return connectionError(FLOW_CONTROL_ERROR,
"Flow control error");
1663 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1664 m_totalBytesReceivedDATA += inboundFrame.payloadSize();
1667 stream->handleDATA(inboundFrame);
1670 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
1671 const bool ignoreData = stream && streamIsIgnored(stream->streamID());
1673 emit receivedEND_STREAM(streamID);
1679 stream->setState(QHttp2Stream::State::Closed);
1684 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1686 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1687 quint32(connectionStreamID),
1688 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1689 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1693void QHttp2Connection::handleHEADERS()
1695 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1697 const auto streamID = inboundFrame.streamID();
1698 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1699 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1703 if (streamID == connectionStreamID)
1704 return connectionError(PROTOCOL_ERROR,
"HEADERS on 0x0 stream");
1706 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1707 qCDebug(qHttp2ConnectionLog,
1708 "[%p] Received HEADERS frame with payload size %u, "
1709 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1710 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1711 return connectionError(Http2Error::FRAME_SIZE_ERROR,
1712 "HEADERS payload size exceeds SETTINGS_MAX_FRAME_SIZE");
1715 const bool isClient = m_connectionType == Type::Client;
1716 const bool isClientInitiatedStream = !!(streamID & 1);
1717 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1719 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1720 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1721 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1722 Q_ASSERT(newStream);
1723 m_lastIncomingStreamID = streamID;
1725 if (!streamCountIsOk) {
1726 newStream->setState(QHttp2Stream::State::Open);
1727 newStream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1729 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1733 qCDebug(qHttp2ConnectionLog,
"[%p] New incoming stream %d",
this, streamID);
1734 if (!streamIsIgnored(newStream->streamID())) {
1735 emit newIncomingStream(newStream);
1736 }
else if (m_goawayGraceTimer.hasExpired()) {
1739 connectionError(Http2Error::PROTOCOL_ERROR,
"Peer refused to GOAWAY.");
1742 }
else if (streamWasResetLocally(streamID)) {
1743 qCDebug(qHttp2ConnectionLog,
1744 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1747 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1750 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1752 return connectionError(PROTOCOL_ERROR,
"HEADERS on invalid stream");
1753 }
else if (isInvalidStream(streamID)) {
1756 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1757 return connectionError(ENHANCE_YOUR_CALM,
"HEADERS on invalid stream");
1760 const auto flags = inboundFrame.flags();
1761 if (flags.testFlag(FrameFlag::PRIORITY)) {
1762 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1767 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1768 continuedFrames.clear();
1769 m_headerBlockSize = 0;
1770 if (!validateHeaderListSize(inboundFrame))
1772 continuedFrames.push_back(std::move(inboundFrame));
1774 continuationExpected =
true;
1778 handleContinuedHEADERS();
1781void QHttp2Connection::handlePRIORITY()
1783 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1784 || inboundFrame.type() == FrameType::HEADERS);
1786 const auto streamID = inboundFrame.streamID();
1787 if (streamIsIgnored(streamID))
1792 if (streamID == connectionStreamID)
1793 return connectionError(PROTOCOL_ERROR,
"PRIORITY on 0x0 stream");
1797 if (isInvalidStream(streamID))
1798 return connectionError(ENHANCE_YOUR_CALM,
"PRIORITY on invalid stream");
1803 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1805 quint32 streamDependency = 0;
1807 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1811 const bool exclusive = streamDependency & 0x80000000;
1812 streamDependency &= ~0x80000000;
1816 Q_UNUSED(exclusive);
1820void QHttp2Connection::handleRST_STREAM()
1822 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1824 const auto streamID = inboundFrame.streamID();
1825 if (streamIsIgnored(streamID))
1832 if (streamID == connectionStreamID)
1833 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on 0x0");
1838 Q_ASSERT(inboundFrame.payloadSize() == 4);
1840 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1841 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1842 emit stream->rstFrameReceived(error);
1845 const quint32 lastRelevantStreamID = [
this, streamID]() {
1846 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1847 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1849 if (streamID > lastRelevantStreamID) {
1853 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on idle stream");
1856 Q_ASSERT(inboundFrame.dataSize() == 4);
1858 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1859 stream->handleRST_STREAM(inboundFrame);
1862void QHttp2Connection::handleSETTINGS()
1865 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1869 if (inboundFrame.streamID() != connectionStreamID)
1870 return connectionError(PROTOCOL_ERROR,
"SETTINGS on invalid stream");
1872 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1875 if (inboundFrame.payloadSize())
1876 return connectionError(FRAME_SIZE_ERROR,
"SETTINGS ACK with data");
1877 if (!waitingForSettingsACK)
1878 return connectionError(PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
1879 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1880 waitingForSettingsACK =
false;
1883 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1885 if (inboundFrame.dataSize()) {
1889 Q_ASSERT(inboundFrame.payloadSize() % 6 == 0);
1891 auto src = inboundFrame.dataBegin();
1892 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1893 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1894 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1895 if (!acceptSetting(identifier, intVal)) {
1897 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1898 quint32(identifier), intVal);
1904 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1906 emit settingsFrameReceived();
1909void QHttp2Connection::handlePUSH_PROMISE()
1912 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1917 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1920 return connectionError(PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
1925 const auto streamID = inboundFrame.streamID();
1926 if (streamID == connectionStreamID)
1927 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid associated stream (0x0)");
1929 auto it = m_streams.constFind(streamID);
1931 if (it != m_streams.constEnd()) {
1932 QHttp2Stream *associatedStream = it->get();
1933 if (associatedStream->state() != QHttp2Stream::State::Open
1934 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1936 it = m_streams.constEnd();
1946 if (it == m_streams.constEnd())
1947 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1948 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1949 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1950 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1952 if ((*it)->state() != QHttp2Stream::State::Open &&
1953 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1954 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1959 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1960 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1961 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid promised stream ID");
1963 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1966 auto *stream = createStreamInternal_impl(reservedID);
1968 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with already active stream ID");
1969 m_lastIncomingStreamID = reservedID;
1970 stream->setState(QHttp2Stream::State::ReservedRemote);
1972 if (!streamCountIsOk) {
1973 stream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1974 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1980 if (!pushPromiseEnabled) {
1981 return stream->streamError(REFUSE_STREAM,
1982 QLatin1String(
"PUSH_PROMISE not enabled but ignored"));
1989 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1990 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1991 continuedFrames.clear();
1992 m_headerBlockSize = 0;
1993 if (!validateHeaderListSize(inboundFrame))
1995 continuedFrames.push_back(std::move(inboundFrame));
1998 continuationExpected =
true;
2002 handleContinuedHEADERS();
2005void QHttp2Connection::handlePING()
2007 Q_ASSERT(inboundFrame.type() == FrameType::PING);
2012 if (inboundFrame.streamID() != connectionStreamID)
2013 return connectionError(PROTOCOL_ERROR,
"PING on invalid stream");
2018 Q_ASSERT(inboundFrame.payloadSize() == 8);
2020 if (inboundFrame.flags() & FrameFlag::ACK) {
2021 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
2022 if (!m_lastPingSignature.has_value()) {
2023 emit pingFrameReceived(PingState::PongNoPingSent);
2024 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
2025 }
else if (pingSignature != m_lastPingSignature) {
2026 emit pingFrameReceived(PingState::PongSignatureChanged);
2027 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
2029 emit pingFrameReceived(PingState::PongSignatureIdentical);
2031 m_lastPingSignature.reset();
2034 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing) {
2035 sendFinalServerGracefulShutdownGoaway();
2036 }
else if (m_gracefulShutdownState == GracefulShutdownState::AwaitingPriorPing) {
2038 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
2039 [[maybe_unused]]
const bool ok = sendPing();
2045 emit pingFrameReceived(PingState::Ping);
2050 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
2051 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
2052 frameWriter.write(*getSocket());
2055void QHttp2Connection::handleGOAWAY()
2059 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
2062 if (inboundFrame.streamID() != connectionStreamID)
2063 return connectionError(PROTOCOL_ERROR,
"GOAWAY on invalid stream");
2068 Q_ASSERT(inboundFrame.payloadSize() >= 8);
2070 const uchar *
const src = inboundFrame.dataBegin();
2072 const quint32 lastStreamID = qFromBigEndian<quint32>(src) & lastValidStreamID;
2073 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
2079 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
2082 if (lastStreamID != 0 && (lastStreamID & 0x1) != LocalMask)
2083 return connectionError(PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
2088 if (m_lastGoAwayLastStreamID && lastStreamID > *m_lastGoAwayLastStreamID)
2089 return connectionError(PROTOCOL_ERROR,
"Repeated GOAWAY with invalid last stream ID");
2090 m_lastGoAwayLastStreamID = lastStreamID;
2092 qCDebug(qHttp2ConnectionLog,
"[%p] Received GOAWAY frame, error code %u, last stream %u",
2093 this, errorCode, lastStreamID);
2096 emit receivedGOAWAY(errorCode, lastStreamID);
2098 if (errorCode == HTTP2_NO_ERROR) {
2102 const quint32 firstPossibleStream = m_connectionType == Type::Client ? 1 : 2;
2103 const quint32 firstCancelledStream = lastStreamID ? lastStreamID + 2 : firstPossibleStream;
2104 Q_ASSERT((firstCancelledStream & 0x1) == LocalMask);
2105 for (quint32 id = firstCancelledStream; id < m_nextStreamID; id += 2) {
2106 QHttp2Stream *stream = m_streams.value(id,
nullptr);
2107 if (stream && stream->isActive())
2108 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2110 maybeCloseOnGoingAway();
2116 m_connectionAborted =
true;
2117 for (QHttp2Stream *stream : std::as_const(m_streams)) {
2118 if (stream && stream->isActive())
2119 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
2125void QHttp2Connection::handleWINDOW_UPDATE()
2127 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
2129 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
2133 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
2134 const auto streamID = inboundFrame.streamID();
2135 if (streamIsIgnored(streamID))
2141 Q_ASSERT(inboundFrame.payloadSize() == 4);
2143 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
2145 if (streamID == connectionStreamID) {
2147 return connectionError(PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
2151 if (qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
2152 return connectionError(FLOW_CONTROL_ERROR,
"WINDOW_UPDATE exceeds maximum window");
2153 sessionSendWindowSize = sum;
2156 const auto blockedStreams = std::exchange(m_blockedStreams, {});
2157 for (quint32 blockedStreamID : blockedStreams) {
2158 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
2159 if (!stream || !stream->isActive() || !stream->isUploadingDATA())
2161 if (stream->isUploadBlocked()) {
2162 m_blockedStreams.insert(blockedStreamID);
2166 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2167 Qt::QueuedConnection);
2171 QHttp2Stream *stream = m_streams.value(streamID);
2172 if (!stream || !stream->isActive()) {
2174 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
2177 }
else if (!valid) {
2178 return stream->streamError(PROTOCOL_ERROR,
2179 QLatin1String(
"WINDOW_UPDATE invalid delta"));
2181 stream->handleWINDOW_UPDATE(inboundFrame);
2185void QHttp2Connection::handleCONTINUATION()
2187 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
2188 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
2189 qCDebug(qHttp2ConnectionLog,
2190 "[%p] Received CONTINUATION frame with payload size %u, "
2191 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
2192 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
2193 return connectionError(Http2Error::FRAME_SIZE_ERROR,
2194 "CONTINUATION payload size exceeds SETTINGS_MAX_FRAME_SIZE");
2196 auto streamID = inboundFrame.streamID();
2197 qCDebug(qHttp2ConnectionLog,
2198 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
2199 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
2200 if (continuedFrames.empty())
2201 return connectionError(PROTOCOL_ERROR,
2202 "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
2203 if (!continuationExpected)
2204 return connectionError(PROTOCOL_ERROR,
2205 "CONTINUATION after a frame with the END_HEADERS flag set");
2207 if (inboundFrame.streamID() != continuedFrames.front().streamID())
2208 return connectionError(PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
2210 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
2213 if (!validateHeaderListSize(inboundFrame))
2215 continuedFrames.push_back(std::move(inboundFrame));
2220 continuationExpected =
false;
2221 handleContinuedHEADERS();
2224bool QHttp2Connection::validateHeaderListSize(
const Frame &frame)
2226 const quint32 limit =
2227 QHttp2ConfigurationPrivate::get(std::as_const(m_config))->maxHeaderListSize;
2228 if (limit == std::numeric_limits<quint32>::max())
2234 m_headerBlockSize += frame.hpackBlockSize();
2235 if (m_headerBlockSize > limit) {
2236 connectionError(ENHANCE_YOUR_CALM,
"Header list size limit exceeded");
2242void QHttp2Connection::handleContinuedHEADERS()
2247 Q_ASSERT(!continuedFrames.empty());
2248 const auto firstFrameType = continuedFrames[0].type();
2249 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
2251 const auto streamID = continuedFrames[0].streamID();
2253 const auto streamIt = m_streams.constFind(streamID);
2254 if (firstFrameType == FrameType::HEADERS) {
2255 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
2256 QHttp2Stream *stream = streamIt.value();
2257 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
2258 && stream->state() != QHttp2Stream::State::ReservedRemote
2259 && stream->state() != QHttp2Stream::State::Idle
2260 && stream->state() != QHttp2Stream::State::Open) {
2264 return stream->streamError(PROTOCOL_ERROR,
"HEADERS on invalid stream"_L1);
2272 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
2273 const bool hasHeaderFields = !hpackBlock.empty();
2274 if (hasHeaderFields) {
2275 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
2276 if (!decoder.decodeHeaderFields(inputStream))
2277 return connectionError(COMPRESSION_ERROR,
"HPACK decompression failed");
2279 if (firstFrameType == FrameType::PUSH_PROMISE) {
2288 if (streamIt != m_streams.cend()) {
2289 (*streamIt)->streamError(PROTOCOL_ERROR,
2290 QLatin1String(
"PUSH_PROMISE with incomplete headers"));
2296 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
2297 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
2298 hpackBlockHasContent);
2300 return connectionError(FRAME_SIZE_ERROR,
"HEADERS frame too large");
2303 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
2305 if (streamIsIgnored(streamID)) {
2309 if (continuedFrames[0].flags().testFlag(Http2::FrameFlag::END_STREAM)) {
2310 if (QHttp2Stream *stream = streamIt.value()) {
2311 stream->setState(QHttp2Stream::State::Closed);
2318 switch (firstFrameType) {
2319 case FrameType::HEADERS:
2320 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2322 case FrameType::PUSH_PROMISE: {
2323 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
2326 if (m_promisedStreams.contains(*promiseKey))
2328 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
2329 QHttp2Stream *stream = m_streams.value(promiseID);
2330 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
2331 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2332 emit newPromisedStream(stream);
2333 m_promisedStreams.emplace(*promiseKey, promiseID);
2341bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
2343 switch (identifier) {
2344 case Settings::HEADER_TABLE_SIZE_ID: {
2345 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
2346 if (newValue > maxAcceptableTableSize) {
2347 connectionError(PROTOCOL_ERROR,
"SETTINGS invalid table size");
2350 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
2351 qCDebug(qHttp2ConnectionLog,
2352 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
2357 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
2358 pendingTableSizeUpdates[0] = newValue;
2359 pendingTableSizeUpdates[1].reset();
2360 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
2362 pendingTableSizeUpdates[1] = newValue;
2363 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
2364 this, newValue, *pendingTableSizeUpdates[0]);
2368 case Settings::INITIAL_WINDOW_SIZE_ID: {
2369 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
2373 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
2374 connectionError(FLOW_CONTROL_ERROR,
"SETTINGS invalid initial window size");
2378 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2379 streamInitialSendWindowSize = qint32(newValue);
2381 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2382 this, size_t(m_streams.size()), delta);
2383 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2389 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2390 connectionError(FLOW_CONTROL_ERROR,
2391 "SETTINGS_INITIAL_WINDOW_SIZE overflowed a flow-control window");
2394 stream->m_sendWindow = sum;
2395 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2396 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2397 Qt::QueuedConnection);
2402 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2403 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2405 m_peerMaxConcurrentStreams = newValue;
2408 case Settings::MAX_FRAME_SIZE_ID: {
2409 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2410 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2411 connectionError(PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
2414 maxFrameSize = newValue;
2417 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2418 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2423 m_maxHeaderListSize = newValue;
2426 case Http2::Settings::ENABLE_PUSH_ID:
2427 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2428 if (newValue != 0 && newValue != 1) {
2429 connectionError(PROTOCOL_ERROR,
"SETTINGS peer sent illegal value for ENABLE_PUSH");
2432 if (m_connectionType == Type::Client) {
2433 if (newValue == 1) {
2434 connectionError(PROTOCOL_ERROR,
"SETTINGS server sent ENABLE_PUSH=1");
2438 pushPromiseEnabled = newValue;
2448#include "moc_qhttp2connection_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)