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();
766 m_downloadBuffer.append(std::move(fragment));
767 emit dataReceived(m_downloadBuffer.last(), endStream);
769 emit dataReceived(fragment, endStream);
773 if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
775 sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
779void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers)
781 if (m_state == State::Idle)
782 transitionState(StateTransition::Open);
783 const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
785 transitionState(StateTransition::CloseRemote);
786 if (!headers.empty() && m_configuration.useHeaderBuffer) {
787 m_headers.insert(m_headers.end(), headers.begin(), headers.end());
788 emit headersUpdated();
790 emit headersReceived(headers, endStream);
793void QHttp2Stream::handleRST_STREAM(
const Frame &inboundFrame)
795 if (m_state == State::Closed)
798 transitionState(StateTransition::RST);
799 m_RST_STREAM_received = qFromBigEndian<quint32>(inboundFrame.dataBegin());
800 if (isUploadingDATA()) {
801 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
802 m_uploadDevice =
nullptr;
803 m_uploadByteDevice =
nullptr;
805 finishWithError(Http2Error(*m_RST_STREAM_received));
808void QHttp2Stream::handleWINDOW_UPDATE(
const Frame &inboundFrame)
810 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
811 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
815 qCDebug(qHttp2ConnectionLog,
816 "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
818 getConnection(), m_streamID, delta);
819 return streamError(PROTOCOL_ERROR, u"invalid WINDOW_UPDATE delta"_s);
822 if (qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
825 qCDebug(qHttp2ConnectionLog,
826 "[%p] stream %u, WINDOW_UPDATE delta %u overflows the flow-control window, "
827 "sending FLOW_CONTROL_ERROR",
828 getConnection(), m_streamID, delta);
829 return streamError(FLOW_CONTROL_ERROR, u"WINDOW_UPDATE exceeds maximum window"_s);
833 if (isUploadingDATA())
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
856
857
858
859
860
863
864
865
866
869
870
871
872
875
876
877
878
881
882
883
884
887
888
889
890
891
892
895
896
897
898
899
900
901
902
905
906
907
908
909
910
911QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
912 const QHttp2Configuration &config)
916 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
917 connection->setH2Configuration(config);
918 connection->m_connectionType = QHttp2Connection::Type::Client;
919 connection->m_upgradedConnection =
true;
922 QHttp2Stream *stream = connection->createLocalStreamInternal().unwrap();
923 Q_ASSERT(stream->streamID() == 1);
924 stream->setState(QHttp2Stream::State::HalfClosedLocal);
926 if (!connection->m_prefaceSent)
929 return connection.release();
933
934
935
936
937
938QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
939 const QHttp2Configuration &config)
941 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
942 connection->setH2Configuration(config);
943 connection->m_connectionType = QHttp2Connection::Type::Client;
945 return connection.release();
949
950
951
952
953QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
954 const QHttp2Configuration &config)
956 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
957 connection->setH2Configuration(config);
958 connection->m_connectionType = QHttp2Connection::Type::Server;
960 connection->m_nextStreamID = 2;
962 connection->m_waitingForClientPreface =
true;
964 return connection.release();
968
969
970
971
972
973
974
975
976
977
978
979
982
983
984
985
986QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
987QHttp2Connection::createStream(QHttp2Stream::Configuration configuration)
989 Q_ASSERT(m_connectionType == Type::Client);
990 if (m_nextStreamID > lastValidStreamID)
991 return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
992 return createLocalStreamInternal(configuration);
995QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
996QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf)
999 return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
1000 const quint32 streamID = m_nextStreamID;
1001 if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams()))
1002 return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
1004 if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) {
1005 m_nextStreamID += 2;
1009 return { QHttp2Connection::CreateStreamError::UnknownError };
1012QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID,
1013 QHttp2Stream::Configuration conf)
1015 Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID);
1017 if (m_connectionType == Type::Client && !m_prefaceSent && !sendClientPreface()) {
1018 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send client preface",
this);
1022 auto result = m_streams.tryEmplace(streamID,
nullptr);
1023 if (!result.inserted)
1025 QPointer<QHttp2Stream> &stream = result.iterator.value();
1026 stream =
new QHttp2Stream(
this, streamID, conf);
1027 stream->m_recvWindow = streamInitialReceiveWindowSize;
1028 stream->m_sendWindow = streamInitialSendWindowSize;
1030 connect(stream, &QHttp2Stream::uploadBlocked,
this, [
this, stream] {
1031 m_blockedStreams.insert(stream->streamID());
1033 *result.iterator = stream;
1034 return *result.iterator;
1037qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask)
const noexcept
1039 const auto shouldCount = [mask](
const QPointer<QHttp2Stream> &stream) ->
bool {
1040 return stream && (stream->streamID() & 1) == mask && stream->isActive();
1042 return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
1046
1047
1048
1049qsizetype QHttp2Connection::numActiveRemoteStreams()
const noexcept
1051 const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
1052 return numActiveStreamsImpl(RemoteMask);
1056
1057
1058
1059qsizetype QHttp2Connection::numActiveLocalStreams()
const noexcept
1061 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
1062 return numActiveStreamsImpl(LocalMask);
1066
1067
1068
1069QHttp2Stream *QHttp2Connection::getStream(quint32 streamID)
const
1071 return m_streams.value(streamID,
nullptr).get();
1075
1076
1077
1078
1079void QHttp2Connection::close(Http2::Http2Error errorCode)
1081 if (m_connectionAborted)
1084 if (errorCode == Http2::HTTP2_NO_ERROR) {
1085 if (m_connectionType == Type::Server)
1086 sendInitialServerGracefulShutdownGoaway();
1088 sendClientGracefulShutdownGoaway();
1092 connectionError(errorCode, u"Connection closed with error"_s,
false);
1097
1098
1099
1100
1101
1104
1105
1106
1107
1108
1111
1112
1113
1114
1115
1116
1119
1120
1121
1122
1125
1126
1127
1128
1129
1131QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
1134 Q_ASSERT(socket->isOpen());
1135 Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
1142QHttp2Connection::~QHttp2Connection()
1146 for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
1147 delete stream.get();
1150bool QHttp2Connection::serverCheckClientPreface()
1152 if (!m_waitingForClientPreface)
1154 auto *socket = getSocket();
1155 if (socket->bytesAvailable() < Http2::clientPrefaceLength)
1157 if (!readClientPreface()) {
1159 emit errorOccurred(Http2Error::PROTOCOL_ERROR, u"invalid client preface"_s);
1160 qCDebug(qHttp2ConnectionLog,
"[%p] Invalid client preface",
this);
1163 qCDebug(qHttp2ConnectionLog,
"[%p] Peer sent valid client preface",
this);
1164 m_waitingForClientPreface =
false;
1165 if (!sendServerPreface()) {
1166 connectionError(INTERNAL_ERROR, u"Failed to send server preface"_s);
1172bool QHttp2Connection::sendPing()
1174 std::array<
char, 8> data;
1176 QRandomGenerator gen;
1177 gen.generate(data.begin(), data.end());
1178 return sendPing(data);
1181bool QHttp2Connection::sendPing(QByteArrayView data)
1183 frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
1185 Q_ASSERT(data.length() == 8);
1186 if (!m_lastPingSignature) {
1187 m_lastPingSignature = data.toByteArray();
1189 qCWarning(qHttp2ConnectionLog,
"[%p] No PING is sent while waiting for the previous PING.",
this);
1193 frameWriter.append((uchar*)data.data(), (uchar*)data.end());
1194 frameWriter.write(*getSocket());
1199
1200
1201
1202
1203void QHttp2Connection::handleReadyRead()
1206 if (m_connectionType == Type::Server && !serverCheckClientPreface())
1209 QIODevice *socket = getSocket();
1211 qCDebug(qHttp2ConnectionLog,
"[%p] Receiving data, %lld bytes available",
this,
1212 socket->bytesAvailable());
1214 using namespace Http2;
1218 while (!m_connectionAborted) {
1219 const auto result = frameReader.read(*socket);
1220 if (result != FrameStatus::goodFrame)
1221 qCDebug(qHttp2ConnectionLog,
"[%p] Tried to read frame, got %d",
this,
int(result));
1223 case FrameStatus::incompleteFrame:
1225 case FrameStatus::protocolError:
1226 return connectionError(PROTOCOL_ERROR, u"invalid frame"_s);
1227 case FrameStatus::sizeError: {
1228 const auto streamID = frameReader.inboundFrame().streamID();
1229 const auto frameType = frameReader.inboundFrame().type();
1230 auto stream = getStream(streamID);
1236 if (frameType == FrameType::HEADERS ||
1237 frameType == FrameType::SETTINGS ||
1238 frameType == FrameType::PUSH_PROMISE ||
1239 frameType == FrameType::CONTINUATION ||
1241 frameType == FrameType::RST_STREAM ||
1242 streamID == connectionStreamID)
1243 return connectionError(FRAME_SIZE_ERROR, u"invalid frame size"_s);
1246 return stream->streamError(Http2Error::FRAME_SIZE_ERROR, u"invalid frame size"_s);
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, u"CONTINUATION expected"_s);
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 QString &message,
1349 if (m_connectionAborted)
1351 m_connectionAborted =
true;
1354 qCCritical(qHttp2ConnectionLog,
"[%p] Connection error: %ls (%d)",
this,
1355 qUtf16Printable(message),
int(errorCode));
1357 qCDebug(qHttp2ConnectionLog,
"[%p] Closing connection: %ls (%d)",
this,
1358 qUtf16Printable(message),
int(errorCode));
1365 m_lastStreamToProcess = std::min(m_lastIncomingStreamID, m_lastStreamToProcess);
1366 sendGOAWAYFrame(errorCode, m_lastStreamToProcess);
1368 for (QHttp2Stream *stream : std::as_const(m_streams)) {
1369 if (stream && stream->isActive())
1370 stream->finishWithError(errorCode, message);
1372 emit errorOccurred(errorCode, message);
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, u"DATA on the connection stream"_s);
1629 if (isInvalidStream(streamID))
1630 return connectionError(ENHANCE_YOUR_CALM, u"DATA on invalid stream"_s);
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, u"Data on closed stream"_s);
1643 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1644 qCDebug(qHttp2ConnectionLog,
1645 "[%p] Received DATA frame with payload size %u, "
1646 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1647 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1649 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1650 u"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1651 return connectionError(FRAME_SIZE_ERROR, u"DATA payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1654 if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1655 qCDebug(qHttp2ConnectionLog,
1656 "[%p] Received DATA frame with payload size %u, "
1657 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1658 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1659 return connectionError(FLOW_CONTROL_ERROR, u"Flow control error"_s);
1662 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1663 m_totalBytesReceivedDATA += inboundFrame.payloadSize();
1666 stream->handleDATA(inboundFrame);
1669 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
1670 const bool ignoreData = stream && streamIsIgnored(stream->streamID());
1672 emit receivedEND_STREAM(streamID);
1678 stream->setState(QHttp2Stream::State::Closed);
1683 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1685 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1686 quint32(connectionStreamID),
1687 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1688 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1692void QHttp2Connection::handleHEADERS()
1694 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1696 const auto streamID = inboundFrame.streamID();
1697 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1698 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1702 if (streamID == connectionStreamID)
1703 return connectionError(PROTOCOL_ERROR, u"HEADERS on 0x0 stream"_s);
1705 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
1706 qCDebug(qHttp2ConnectionLog,
1707 "[%p] Received HEADERS frame with payload size %u, "
1708 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
1709 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
1710 return connectionError(Http2Error::FRAME_SIZE_ERROR,
1711 u"HEADERS payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
1714 const bool isClient = m_connectionType == Type::Client;
1715 const bool isClientInitiatedStream = !!(streamID & 1);
1716 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1718 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1719 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1720 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1721 Q_ASSERT(newStream);
1722 m_lastIncomingStreamID = streamID;
1724 if (!streamCountIsOk) {
1725 newStream->setState(QHttp2Stream::State::Open);
1726 newStream->streamError(PROTOCOL_ERROR, u"Max concurrent streams reached"_s);
1728 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1732 qCDebug(qHttp2ConnectionLog,
"[%p] New incoming stream %d",
this, streamID);
1733 if (!streamIsIgnored(newStream->streamID())) {
1734 emit newIncomingStream(newStream);
1735 }
else if (m_goawayGraceTimer.hasExpired()) {
1738 connectionError(Http2Error::PROTOCOL_ERROR, u"Peer refused to GOAWAY."_s);
1741 }
else if (streamWasResetLocally(streamID)) {
1742 qCDebug(qHttp2ConnectionLog,
1743 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1746 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1749 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1751 return connectionError(PROTOCOL_ERROR, u"HEADERS on invalid stream"_s);
1752 }
else if (isInvalidStream(streamID)) {
1755 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1756 return connectionError(ENHANCE_YOUR_CALM, u"HEADERS on invalid stream"_s);
1759 const auto flags = inboundFrame.flags();
1760 if (flags.testFlag(FrameFlag::PRIORITY)) {
1761 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1766 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1767 continuedFrames.clear();
1768 m_headerBlockSize = 0;
1769 if (!validateHeaderListSize(inboundFrame))
1771 continuedFrames.push_back(std::move(inboundFrame));
1773 continuationExpected =
true;
1777 handleContinuedHEADERS();
1780void QHttp2Connection::handlePRIORITY()
1782 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1783 || inboundFrame.type() == FrameType::HEADERS);
1785 const auto streamID = inboundFrame.streamID();
1786 if (streamIsIgnored(streamID))
1791 if (streamID == connectionStreamID)
1792 return connectionError(PROTOCOL_ERROR, u"PRIORITY on 0x0 stream"_s);
1796 if (isInvalidStream(streamID))
1797 return connectionError(ENHANCE_YOUR_CALM, u"PRIORITY on invalid stream"_s);
1802 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1804 quint32 streamDependency = 0;
1806 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1810 const bool exclusive = streamDependency & 0x80000000;
1811 streamDependency &= ~0x80000000;
1815 Q_UNUSED(exclusive);
1819void QHttp2Connection::handleRST_STREAM()
1821 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1823 const auto streamID = inboundFrame.streamID();
1824 if (streamIsIgnored(streamID))
1831 if (streamID == connectionStreamID)
1832 return connectionError(PROTOCOL_ERROR, u"RST_STREAM on 0x0"_s);
1837 Q_ASSERT(inboundFrame.payloadSize() == 4);
1839 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1840 if (QPointer<QHttp2Stream> stream = m_streams.value(streamID))
1841 emit stream->rstFrameReceived(error);
1844 const quint32 lastRelevantStreamID = [
this, streamID]() {
1845 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1846 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1848 if (streamID > lastRelevantStreamID) {
1852 return connectionError(PROTOCOL_ERROR, u"RST_STREAM on idle stream"_s);
1855 Q_ASSERT(inboundFrame.dataSize() == 4);
1857 if (QPointer<QHttp2Stream> stream = m_streams.value(streamID))
1858 stream->handleRST_STREAM(inboundFrame);
1861void QHttp2Connection::handleSETTINGS()
1864 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1868 if (inboundFrame.streamID() != connectionStreamID)
1869 return connectionError(PROTOCOL_ERROR, u"SETTINGS on invalid stream"_s);
1871 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1874 if (inboundFrame.payloadSize())
1875 return connectionError(FRAME_SIZE_ERROR, u"SETTINGS ACK with data"_s);
1876 if (!waitingForSettingsACK)
1877 return connectionError(PROTOCOL_ERROR, u"unexpected SETTINGS ACK"_s);
1878 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1879 waitingForSettingsACK =
false;
1882 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1884 if (inboundFrame.dataSize()) {
1888 Q_ASSERT(inboundFrame.payloadSize() % 6 == 0);
1890 auto src = inboundFrame.dataBegin();
1891 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1892 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1893 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1894 if (!acceptSetting(identifier, intVal)) {
1896 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1897 quint32(identifier), intVal);
1903 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1905 emit settingsFrameReceived();
1908void QHttp2Connection::handlePUSH_PROMISE()
1911 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1916 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1919 return connectionError(PROTOCOL_ERROR, u"unexpected PUSH_PROMISE frame"_s);
1924 const auto streamID = inboundFrame.streamID();
1925 if (streamID == connectionStreamID)
1926 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with invalid associated stream (0x0)"_s);
1928 auto it = m_streams.constFind(streamID);
1930 if (it != m_streams.constEnd()) {
1931 QHttp2Stream *associatedStream = it->get();
1932 if (associatedStream->state() != QHttp2Stream::State::Open
1933 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1935 it = m_streams.constEnd();
1945 if (it == m_streams.constEnd() || it->isNull())
1946 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1947 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1948 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1949 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1951 if ((*it)->state() != QHttp2Stream::State::Open &&
1952 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1953 return connectionError(ENHANCE_YOUR_CALM, u"PUSH_PROMISE with invalid associated stream"_s);
1958 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1959 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1960 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with invalid promised stream ID"_s);
1962 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1965 auto *stream = createStreamInternal_impl(reservedID);
1967 return connectionError(PROTOCOL_ERROR, u"PUSH_PROMISE with already active stream ID"_s);
1968 m_lastIncomingStreamID = reservedID;
1969 stream->setState(QHttp2Stream::State::ReservedRemote);
1971 if (!streamCountIsOk) {
1972 stream->streamError(PROTOCOL_ERROR, u"Max concurrent streams reached"_s);
1973 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1979 if (!pushPromiseEnabled)
1980 return stream->streamError(REFUSE_STREAM, u"PUSH_PROMISE not enabled but ignored"_s);
1986 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1987 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1988 continuedFrames.clear();
1989 m_headerBlockSize = 0;
1990 if (!validateHeaderListSize(inboundFrame))
1992 continuedFrames.push_back(std::move(inboundFrame));
1995 continuationExpected =
true;
1999 handleContinuedHEADERS();
2002void QHttp2Connection::handlePING()
2004 Q_ASSERT(inboundFrame.type() == FrameType::PING);
2009 if (inboundFrame.streamID() != connectionStreamID)
2010 return connectionError(PROTOCOL_ERROR, u"PING on invalid stream"_s);
2015 Q_ASSERT(inboundFrame.payloadSize() == 8);
2017 if (inboundFrame.flags() & FrameFlag::ACK) {
2018 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
2019 if (!m_lastPingSignature.has_value()) {
2020 emit pingFrameReceived(PingState::PongNoPingSent);
2021 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
2022 }
else if (pingSignature != m_lastPingSignature) {
2023 emit pingFrameReceived(PingState::PongSignatureChanged);
2024 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
2026 emit pingFrameReceived(PingState::PongSignatureIdentical);
2028 m_lastPingSignature.reset();
2031 if (m_gracefulShutdownState == GracefulShutdownState::AwaitingShutdownPing) {
2032 sendFinalServerGracefulShutdownGoaway();
2033 }
else if (m_gracefulShutdownState == GracefulShutdownState::AwaitingPriorPing) {
2035 m_gracefulShutdownState = GracefulShutdownState::AwaitingShutdownPing;
2036 [[maybe_unused]]
const bool ok = sendPing();
2042 emit pingFrameReceived(PingState::Ping);
2047 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
2048 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
2049 frameWriter.write(*getSocket());
2052void QHttp2Connection::handleGOAWAY()
2056 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
2059 if (inboundFrame.streamID() != connectionStreamID)
2060 return connectionError(PROTOCOL_ERROR, u"GOAWAY on invalid stream"_s);
2065 Q_ASSERT(inboundFrame.payloadSize() >= 8);
2067 const uchar *
const src = inboundFrame.dataBegin();
2069 const quint32 lastStreamID = qFromBigEndian<quint32>(src) & lastValidStreamID;
2070 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
2076 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
2079 if (lastStreamID != 0 && (lastStreamID & 0x1) != LocalMask)
2080 return connectionError(PROTOCOL_ERROR, u"GOAWAY with invalid last stream ID"_s);
2085 if (m_lastGoAwayLastStreamID && lastStreamID > *m_lastGoAwayLastStreamID)
2086 return connectionError(PROTOCOL_ERROR, u"Repeated GOAWAY with invalid last stream ID"_s);
2087 m_lastGoAwayLastStreamID = lastStreamID;
2089 qCDebug(qHttp2ConnectionLog,
"[%p] Received GOAWAY frame, error code %u, last stream %u",
2090 this, errorCode, lastStreamID);
2093 emit receivedGOAWAY(errorCode, lastStreamID);
2095 if (errorCode == HTTP2_NO_ERROR) {
2099 const quint32 firstPossibleStream = m_connectionType == Type::Client ? 1 : 2;
2100 const quint32 firstCancelledStream = lastStreamID ? lastStreamID + 2 : firstPossibleStream;
2101 Q_ASSERT((firstCancelledStream & 0x1) == LocalMask);
2102 for (quint32 id = firstCancelledStream; id < m_nextStreamID; id += 2) {
2103 QHttp2Stream *stream = m_streams.value(id,
nullptr);
2104 if (stream && stream->isActive())
2105 stream->finishWithError(errorCode, u"Received GOAWAY"_s);
2107 maybeCloseOnGoingAway();
2113 m_connectionAborted =
true;
2114 for (QHttp2Stream *stream : std::as_const(m_streams)) {
2115 if (stream && stream->isActive())
2116 stream->finishWithError(errorCode, u"Received GOAWAY"_s);
2122void QHttp2Connection::handleWINDOW_UPDATE()
2124 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
2126 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
2130 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
2131 const auto streamID = inboundFrame.streamID();
2132 if (streamIsIgnored(streamID))
2138 Q_ASSERT(inboundFrame.payloadSize() == 4);
2140 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
2142 if (streamID == connectionStreamID) {
2144 return connectionError(PROTOCOL_ERROR, u"WINDOW_UPDATE invalid delta"_s);
2148 if (qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
2149 return connectionError(FLOW_CONTROL_ERROR, u"WINDOW_UPDATE exceeds maximum window"_s);
2150 sessionSendWindowSize = sum;
2153 const auto blockedStreams = std::exchange(m_blockedStreams, {});
2154 for (quint32 blockedStreamID : blockedStreams) {
2155 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
2156 if (!stream || !stream->isActive() || !stream->isUploadingDATA())
2158 if (stream->isUploadBlocked()) {
2159 m_blockedStreams.insert(blockedStreamID);
2163 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2164 Qt::QueuedConnection);
2168 QHttp2Stream *stream = m_streams.value(streamID);
2169 if (!stream || !stream->isActive()) {
2171 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
2176 return stream->streamError(PROTOCOL_ERROR, u"WINDOW_UPDATE invalid delta"_s);
2178 stream->handleWINDOW_UPDATE(inboundFrame);
2182void QHttp2Connection::handleCONTINUATION()
2184 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
2185 if (inboundFrame.payloadSize() > m_config.maxFrameSize()) {
2186 qCDebug(qHttp2ConnectionLog,
2187 "[%p] Received CONTINUATION frame with payload size %u, "
2188 "but SETTINGS_MAX_FRAME_SIZE is %u, sending FRAME_SIZE_ERROR",
2189 this, inboundFrame.payloadSize(), m_config.maxFrameSize());
2190 return connectionError(Http2Error::FRAME_SIZE_ERROR,
2191 u"CONTINUATION payload size exceeds SETTINGS_MAX_FRAME_SIZE"_s);
2193 auto streamID = inboundFrame.streamID();
2194 qCDebug(qHttp2ConnectionLog,
2195 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
2196 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
2197 if (continuedFrames.empty())
2198 return connectionError(PROTOCOL_ERROR,
2199 u"CONTINUATION without a preceding HEADERS or PUSH_PROMISE"_s);
2200 if (!continuationExpected)
2201 return connectionError(PROTOCOL_ERROR,
2202 u"CONTINUATION after a frame with the END_HEADERS flag set"_s);
2204 if (inboundFrame.streamID() != continuedFrames.front().streamID())
2205 return connectionError(PROTOCOL_ERROR, u"CONTINUATION on invalid stream"_s);
2207 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
2210 if (!validateHeaderListSize(inboundFrame))
2212 continuedFrames.push_back(std::move(inboundFrame));
2217 continuationExpected =
false;
2218 handleContinuedHEADERS();
2221bool QHttp2Connection::validateHeaderListSize(
const Frame &frame)
2223 const quint32 limit =
2224 QHttp2ConfigurationPrivate::get(std::as_const(m_config))->maxHeaderListSize;
2225 if (limit == std::numeric_limits<quint32>::max())
2231 m_headerBlockSize += frame.hpackBlockSize();
2232 if (m_headerBlockSize > limit) {
2233 connectionError(ENHANCE_YOUR_CALM, u"Header list size limit exceeded"_s);
2239void QHttp2Connection::handleContinuedHEADERS()
2244 Q_ASSERT(!continuedFrames.empty());
2245 const auto firstFrameType = continuedFrames[0].type();
2246 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
2248 const auto streamID = continuedFrames[0].streamID();
2250 const auto streamIt = m_streams.constFind(streamID);
2251 if (firstFrameType == FrameType::HEADERS) {
2252 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
2253 QHttp2Stream *stream = streamIt.value();
2254 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
2255 && stream->state() != QHttp2Stream::State::ReservedRemote
2256 && stream->state() != QHttp2Stream::State::Idle
2257 && stream->state() != QHttp2Stream::State::Open) {
2261 return stream->streamError(PROTOCOL_ERROR, u"HEADERS on invalid stream"_s);
2269 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
2270 const bool hasHeaderFields = !hpackBlock.empty();
2271 if (hasHeaderFields) {
2272 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
2273 if (!decoder.decodeHeaderFields(inputStream))
2274 return connectionError(COMPRESSION_ERROR, u"HPACK decompression failed"_s);
2276 if (firstFrameType == FrameType::PUSH_PROMISE) {
2285 if (streamIt != m_streams.cend())
2286 (*streamIt)->streamError(PROTOCOL_ERROR, u"PUSH_PROMISE with incomplete headers"_s);
2291 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
2292 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
2293 hpackBlockHasContent);
2295 return connectionError(FRAME_SIZE_ERROR, u"HEADERS frame too large"_s);
2298 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
2300 if (streamIsIgnored(streamID)) {
2304 if (continuedFrames[0].flags().testFlag(Http2::FrameFlag::END_STREAM)) {
2305 if (QHttp2Stream *stream = streamIt.value()) {
2306 stream->setState(QHttp2Stream::State::Closed);
2313 switch (firstFrameType) {
2314 case FrameType::HEADERS:
2315 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2317 case FrameType::PUSH_PROMISE: {
2318 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
2321 if (m_promisedStreams.contains(*promiseKey))
2323 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
2324 QHttp2Stream *stream = m_streams.value(promiseID);
2325 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
2326 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
2327 emit newPromisedStream(stream);
2328 m_promisedStreams.emplace(*promiseKey, promiseID);
2336bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
2338 switch (identifier) {
2339 case Settings::HEADER_TABLE_SIZE_ID: {
2340 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
2341 if (newValue > maxAcceptableTableSize) {
2342 connectionError(PROTOCOL_ERROR, u"SETTINGS invalid table size"_s);
2345 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
2346 qCDebug(qHttp2ConnectionLog,
2347 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
2352 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
2353 pendingTableSizeUpdates[0] = newValue;
2354 pendingTableSizeUpdates[1].reset();
2355 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
2357 pendingTableSizeUpdates[1] = newValue;
2358 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
2359 this, newValue, *pendingTableSizeUpdates[0]);
2363 case Settings::INITIAL_WINDOW_SIZE_ID: {
2364 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
2368 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
2369 connectionError(FLOW_CONTROL_ERROR, u"SETTINGS invalid initial window size"_s);
2373 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2374 streamInitialSendWindowSize = qint32(newValue);
2376 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2377 this, size_t(m_streams.size()), delta);
2378 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2384 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2385 connectionError(FLOW_CONTROL_ERROR,
2386 u"SETTINGS_INITIAL_WINDOW_SIZE overflowed a flow-control window"_s);
2389 stream->m_sendWindow = sum;
2390 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2391 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2392 Qt::QueuedConnection);
2397 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2398 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2400 m_peerMaxConcurrentStreams = newValue;
2403 case Settings::MAX_FRAME_SIZE_ID: {
2404 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2405 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2406 connectionError(PROTOCOL_ERROR, u"SETTINGS max frame size is out of range"_s);
2409 maxFrameSize = newValue;
2412 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2413 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2418 m_maxHeaderListSize = newValue;
2421 case Http2::Settings::ENABLE_PUSH_ID:
2422 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2423 if (newValue != 0 && newValue != 1) {
2424 connectionError(PROTOCOL_ERROR, u"SETTINGS peer sent illegal value for ENABLE_PUSH"_s);
2427 if (m_connectionType == Type::Client) {
2428 if (newValue == 1) {
2429 connectionError(PROTOCOL_ERROR, u"SETTINGS server sent ENABLE_PUSH=1"_s);
2433 pushPromiseEnabled = newValue;
2443#include "moc_qhttp2connection_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)